Showing posts with label FluentHtml. Show all posts
Showing posts with label FluentHtml. Show all posts

Sunday, March 04, 2012

Create an ActionLink without Magic Strings

Updated: 3/7/2011

So it’s been a while. I have a new job working for Headspring in Austin, which is one of the reasons I haven’t blogged in a while. It’s tough moving and re-adjusting to a new area and job, but Austin is great and Headspring is terrific. I started blogging on the Headspring site too. I’m trying to alternate weeks for posting, so we’ll see how that goes. (also fyi, Headspring is hiring)

On to the good stuff, I started playing with an HtmlHelper extension to rid a project of magic strings for action links. It was definitely more tricky than I originally thought, but I have a first attempt ready to share. Here’s the code:

@(Html.ActionLinkFor<HomeController>(a=>a.Contact(Model), "Contact"
, new Dictionary<string, object>{{"style","color: #f90;background:#000"}})) <br />

@(
Html.ActionLinkFor<MainControllerController>(a=>a.Index(), "Main Home"))<br/>

@(Html.ActionLinkFor<HomeController>(a=>a.Index(), "Home")) <br />

@(Html.ActionLinkFor<HomeController>(a=>a.AnotherTest(new SampleModel{Id=5555
, Name =
"Just a test", Address = "123 Main"}), "Another Test"))<br/>

@(Html.ActionLinkFor<HomeController>(a=>a.TestYetAnother(new SampleModel{Id=12345678
, Name =
"Just another test yet"}), "Yet Another Test"))

The code above is what I wanted for the end result. The code below is what makes the code above work.

    public static class HtmlHelperExtensions
{
public static IHtmlString ActionLinkFor<TController>(this HtmlHelper htmlHelper
,
Expression<Func<TController, object>> expression,
string displayText) where TController : Controller
{
return ActionLinkFor(htmlHelper, expression, displayText, null);
}

public static IHtmlString ActionLinkFor<TController>(this HtmlHelper htmlHelper
,
Expression<Func<TController, object>> expression, string displayText
,
IDictionary<string, object> htmlAttributes)
where TController : Controller
{
var method = ((MethodCallExpression)expression.Body).Method;
var actionName = method.Name;
var parameters = method.GetParameters();
var arguments = ((MethodCallExpression)expression.Body).Arguments;

var routeValue = new RouteValueDictionary();
for (int i = 0; i < parameters.Length; i++)
{
if (arguments[i] as MemberExpression != null)
{
var memberExpression = (MemberExpression)arguments[i];
if (memberExpression.Expression as ConstantExpression == null)
AddRouteValuesForMemberExpression((
MemberExpression)arguments[i]
, parameters[i].Name, routeValue);

if (memberExpression.Expression as ConstantExpression != null)
AddRouteValuesForConstantExpression(((
MemberExpression)arguments[i])
, parameters[i].Name, routeValue);
}
if (arguments[i] as MemberInitExpression != null)
AddRouteValuesForMemberInitExpression((
MemberInitExpression)arguments[i]
, routeValue);
if (arguments[i] as MethodCallExpression != null)
AddRouteValuesForMethodCallExpression((
MethodCallExpression)arguments[i]
, parameters[i].Name, routeValue);
}

var controller = typeof(TController).Name;
var controllerName = controller.Remove(controller.LastIndexOf("Controller"));
return htmlHelper.ActionLink(displayText, actionName, controllerName, routeValue, htmlAttributes);
}

private static void AddRouteValuesForMethodCallExpression(MethodCallExpression methodCallExpression
,
string parameterName, RouteValueDictionary routeValue)
{
if (methodCallExpression.Object != null)
{
var value = ((PropertyInfo)((MemberExpression)methodCallExpression.Object).Member)
.GetValue(((
ConstantExpression)((MemberExpression)methodCallExpression.Object)
.Expression).Value,
null);
routeValue.Add(parameterName, value);
}
}

private static void AddRouteValuesForMemberExpression(MemberExpression memberExpression
,
string parameterName, IDictionary<string, object> routeValue)
{
object model;
if (((MemberExpression)memberExpression.Expression).Member is PropertyInfo)
model = ((
PropertyInfo)((MemberExpression)memberExpression.Expression).Member)
.GetValue(((
ConstantExpression)((MemberExpression)memberExpression.Expression)
.Expression).Value,
null);
else
model = ((FieldInfo)((MemberExpression)memberExpression.Expression).Member)
.GetValue(((
ConstantExpression)((MemberExpression)memberExpression.Expression)
.Expression).Value);

foreach (var p in model.GetType().GetProperties())
{
if (p.Name == memberExpression.Member.Name)
{
var value = p.GetValue(model, null);
if (value != null && !routeValue.ContainsKey(p.Name))
routeValue.Add(parameterName, value.ToString());
}
}
}

private static void AddRouteValuesForConstantExpression(MemberExpression memberExpression
,
string parameterName, IDictionary<string, object> routeValue)
{
var properties = ((PropertyInfo)memberExpression.Member)
.GetValue(((
ConstantExpression)memberExpression.Expression).Value, null)
.GetType()
.GetProperties();

foreach (var property in properties)
{
var value = property.GetValue(((PropertyInfo)memberExpression.Member)
.GetValue(((
ConstantExpression)memberExpression.Expression).Value, null), null);

if (value != null)
routeValue.Add(property.Name, value.ToString());
}

if (properties.Length == 0)
{
var value = ((PropertyInfo)memberExpression.Member)
.GetValue(((
ConstantExpression)memberExpression.Expression).Value, null);

if (value != null)
routeValue.Add(parameterName, value.ToString());
}
}

private static void AddRouteValuesForMemberInitExpression(MemberInitExpression memberInitExpression
,
IDictionary<string, object> routeValue)
{
foreach (var p in memberInitExpression.Bindings.Cast<MemberAssignment>())
{
if (p.Expression is ConstantExpression)
{
var value = ((ConstantExpression)p.Expression).Value;
var name = p.Member.Name;
if (value != null)
routeValue.Add(name, value.ToString());
}
if (p.Expression is UnaryExpression)
{
throw new NotImplementedException("Not able to handle UnaryExpressions yet.");
}
}
}
}

Basically the way this thing works is off the Expression<Func<TController, object>> parameter being passed into the ActionLinkFor method. From the expression, you can determine the route values and action name…by doing a lot of manipulation. So, if you would, try this helper out and let me know of any bugs or improvements that can be made.


Shout it

kick it on DotNetKicks.com

Friday, April 15, 2011

ASP.NET MVC 3 Sample Project Launched




Okay, after popular demand of I think 0 people, I’ve published my old demo MVC 1 project as an MVC 3 project. If you haven’t read any of the posts on the last demo project, check out this post. This project completely separates UI & C# code, so you only have 2 projects (Core & UI…not counting the Unit Tests project).

Basically, all I did was create a new MVC 3 project using Razor with Microsoft’s default template and deleted everything except the following:

  • Views folder
  • Root Default.aspx
  • global.asax (I did delete the global.asax.cs)
  • both web.configs

I referenced my Core, setup my views that match my Core project, and inherited from my global.cs in the global.asax. That was it!

You can download it via zip here: http://code.google.com/p/derans/downloads/list (the file is called DemoPhotographySite_v3.zip)

I feel like this project will act as a great stepping stone to understanding the Code Camp server, which is much more complex.

The sample project was built with the following tools:

If you downloaded the old one, you’ll notice that I removed the following 3rd parties:

The primary reason I removed these three parties is because MVC 3 and Razor are good enough so you don’t need the 3rd party tools.

The best practices I mentioned above come straight from experience and the following people/resources:

You can see the exact same Core code in use at sweetandhappy.com. If you see any improvements that can be made or you’d just like to comment, please do so!

Also, please note that I basically am even re-posting the exact same blog post I did over a year ago with my original MVC demo project. I hope you don’t mind, but it’s late and I have to work tomorrow :)

Thanks for reading!

Shout it

kick it on DotNetKicks.com

Tuesday, December 07, 2010

WebFormContrib – Sample Part 3




This sample on the WebFormContrib mini-framework is on how to do a form post. If you’d like a particular sample on how to do something or if I’m missing something, please leave a comment.

Here’s how…

  1. Create a New Web Site of Use an Existing
  2. Download and reference the WebFormContrib.DLL
  3. Delete the CodeFile=”Default.aspx.cs” Inherits=”_Default” in the <%@ Page area
  4. Delete the actual Default.aspx.cs file attached to the Default.aspx page
  5. Create a new Class in the App_Code folder ( I’m calling mine DefaultPage )
  6. Inherit the DefaultPage from BasePage<SampleEmployeeView>
  7. Create a new Class file called SampleEmployeeView and make it look like this:
public class SampleEmployeeView
{
[
Required("Id is Required.")]
public int Id { get; set; }
[
Required("First Name is Required.")]
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }

//Could use AutoMapper
public static SampleEmployeeView MapEmployeeToView(Employee employee)
{
var view = new SampleEmployeeView();
view.FirstName = employee.Name.FirstName;
view.LastName = employee.Name.LastName;
view.EmailAddress = employee.EmailAddress;
view.Id = employee.Id;
return view;
}

//Could use AutoMapper
public Employee MapToEmployee()
{
var emp = new Employee();
emp.Name.FirstName = FirstName;
emp.Name.LastName = LastName;
emp.EmailAddress = EmailAddress;
emp.Id = Id;
return emp;
}
}

Employee Class & Person Class look like this:

public class Employee
{
public Employee()
{
Name =
new PersonName();
}
public int Id { get; set; }
public PersonName Name { get; set; }
public string EmailAddress { get; set; }
}

public class PersonName
{
public PersonName(){}
public PersonName(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
}

  1. In the DefaultPage Class create in Step 6, add the following code:

public class DefaultPage : BasePage<SampleEmployeeView>
{
private IRepository rep;
protected void Page_Load(object sender, EventArgs e)
{
rep =
DI.CreateSampleRepository();
if(Page.IsPostBack)
return;

getEmployee();
}

private void getEmployee()
{
//Pass in ID here to load employee into view.
ViewModel = SampleEmployeeView.MapEmployeeToView(rep.GetEmployeeBy(ViewModel.Id));
}

protected void SaveEmployee(object sender, EventArgs e)
{
if (!ModelIsValid(ViewModel))
return;

rep.Save(ViewModel.MapToEmployee());
C<
HtmlGenericControl>("successmsg").Visible = true;
}
}

Notice the ModelIsValid(ViewModel), which checks the validators set on the ViewModel. I considered using Castle Validators, but I preferred to not have any 3rd party references. I sometimes get annoyed when an open-source project has a lot of dependencies on other projects. Anyhow…


  1. Now we need to setup a dummy repository and service locator like this:

public class DI
{
public static IRepository CreateSampleRepository()
{
return new SampleRepository();
}
}

public class SampleRepository : IRepository
{
public Employee GetEmployeeBy(int employeeId)
{
//Get Employee from DB here.
return new Employee {EmailAddress = "test@test.com", Id = 123, Name = new PersonName("John", "Doe")};
}

public void Save(Employee employee)
{
//Save Employee here.
}
}

public interface IRepository
{
Employee GetEmployeeBy(int employeeId);
void Save(Employee employee);
}

  1. And finally the HTML portion of the site…the “cool” part:

<%@ Page Language="C#" AutoEventWireup="true"  Inherits="DefaultPage" %>
<%
@ Import Namespace="WebFormContrib.Helpers" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<
html xmlns="http://www.w3.org/1999/xhtml">
<
head runat="server">
<
title></title>
</
head>
<
body>
<
form id="form1" runat="server">
<
div>
<
div id="successmsg" runat="server" enableviewstate="false" visible="false">Saved Successfully.</div>
<%=ErrorMessages.ToList("Important Message") %>
<p><%=Html<TextBox>(f=>f.FirstName).Label("First Name:<br/>") %></p>
<
p><%=Html<TextBox>(f=>f.LastName).Label("Last Name:<br/>") %></p>
<
p><%=Html<TextBox>(f=>f.EmailAddress).Label("Email Address:<br/>") %></p>
<
p><%=Html<HiddenField>(f=>f.Id) %></p>
<
p><asp:Button ID="bSave" runat="server" Text="Save" OnClick="SaveEmployee" /></p>
</
div>
</
form>
</
body>
</
html>

Finished view should look like this:

image

Download the sample project here.

Please comment if you have any questions or suggestions. Thanks for reading!

Shout it

kick it on DotNetKicks.com

Saturday, December 04, 2010

WebFormContrib – Sample Part 2




This sample on the WebFormContrib mini-framework is a mixture of a form post and viewing an item. If you’d like a particular sample on how to do something or if I’m missing something, please leave a comment.

Down to the code…

  1. Create a New Web Site of Use an Existing
  2. Download and reference the WebFormContrib.DLL
  3. Delete the CodeFile=”Default.aspx.cs” Inherits=”_Default” in the <%@ Page area
  4. Delete the actual Default.aspx.cs file attached to the Default.aspx page
  5. Create a new Class in the App_Code folder ( I’m calling mine DefaultPage )
  6. Inherit the DefaultPage from BasePage<SampleDataView>
  7. Create a new Class file called SampleDataView and make it look like this:
public class SampleDataView
{
public SampleDataView()
{
Employees =
new List<Employee>();
SelectedEmployee =
new Employee();
}
public IList<Employee> Employees { get; set; }
public Employee SelectedEmployee { get; set; }
public string SelectedEmployeeEmailAddress { get; set; }
}

My Employee class looks like this:

public class Employee
{
public Employee(){}
public Employee(string firstName, string lastName, string email)
{
FirstName = firstName;
LastName = lastName;
Email = email;
}

public string DisplayName {get { return FirstName + " " + LastName;}}
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}

  1. In the DefaultPage Class create in Step 6, add the following code:

public class DefaultPage : BasePage<SampleDataView>
{
private IRepository rep;
protected void Page_Load(object sender, EventArgs e)
{
rep =
DI.CreateSampleRepository();
initializePage();
}

private void initializePage()
{
ViewModel.Employees = rep.GetAllEmployeesResult;
}

protected void getEmployee(object sender, EventArgs e)
{
ViewModel.SelectedEmployee = rep.GetEmployeeBy(ViewModel.SelectedEmployeeEmailAddress);
}
}
  1. Create the IRepository Interface and a new DI Class that look like these:
public interface IRepository
{
IList<Employee> GetAllEmployeesResult { get; }
Employee GetEmployeeBy(string emailAddress);
}
public class DI
{
public static IRepository CreateSampleRepository()
{
return new SampleRepository();
}
}
  1. Let’s implement the IRepository real quick with a Class called SampleRepository. Looks like this:
public class SampleRepository : IRepository
{
public IList<Employee> GetAllEmployeesResult
{
get
{
var list = new List<Employee>();
list.Add(
new Employee("John", "Smith", "john@test.com"));
list.Add(
new Employee("Cindy", "Sue", "sue@test.com"));
list.Add(
new Employee("Speed", "Racer", "speed@test.com"));
list.Add(
new Employee("Joan", "Arc", "joan@test.com"));
return list;
}
}
public Employee GetEmployeeBy(string emailAddress)
{
return GetAllEmployeesResult.SingleOrDefault(x => x.Email == emailAddress);
}
}
  1. Now we get to see the fun part, the HTML code :)
<%@ Page Language="C#" AutoEventWireup="true" Inherits="DefaultPage" %>
<%
@ Import Namespace="WebFormContrib.Helpers" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<
html xmlns="http://www.w3.org/1999/xhtml">
<
head runat="server">
<
title></title>
</
head>
<
body>
<
form id="form1" runat="server">
<
div>
<%=Html<DropDownList>(f=>f.SelectedEmployeeEmailAddress)
.Options(ViewModel.Employees, f=>f.DisplayName, f=>f.Email)
.Selected(ViewModel.SelectedEmployeeEmailAddress)
%>
<asp:Button ID="bLoadEmployee" runat="server" Text="Display Employee" OnClick="getEmployee" />

<
p>Selected Employee:<br />
Email <
strong><%=ViewModel.SelectedEmployee.DisplayName %></strong> at <%=ViewModel.SelectedEmployee.Email %></p>
</
div>
</
form>
</
body>
</
html>

You will notice the new DropDownList with Options and Selected extension methods. The Options extension has 2 other overloads that accepts the IEnumerable<string> or IEnumerable<ListItem> so you don’t have to specify the datafield and valuefield. The Selected extension gets the value of what needs to be selected after the postback or during an initial load.

When you run the app, you should see this:

image

After clicking Display Employee:

image

Download the sample project here.

Please comment if you have any questions or suggestions. Thanks for reading!


Shout it

kick it on DotNetKicks.com

Thursday, December 02, 2010

WebFormContrib – Sample Part 1




After using the WebFormContrib mini-framework the past couple weeks, I’ve grown to really like it. I thought I’d share some of the ways I’m using it in hopes that you might benefit from it too.

This first sample is on how to show data on a page and not have to worry about any of the form elements. We will also not use any asp.net controls like gridviews, literals, or labels. So, here we go using Visual Studio 2008…

  1. Create a New Web Site or Use an Existing
  2. Download and reference the WebFormContrib DLL
  3. Delete the CodeFile="Default.aspx.cs" Inherits="_Default" in the <%@ Page area
  4. Delete the actual ….aspx.cs
  5. Create a new Class file in the App_Code folder ( I called mine DefaultPage)
  6. Inherit the DefaultPage from BasePage<SampleDataView>
  7. Create a new Class file called SampleDataView and make it look like this:
public class SampleDataView
{
public SampleDataView()
{
Employees =
new List<Employee>();
}
public string DepartmentName { get; set; }
public IList<Employee> Employees { get; set; }
public int TotalEmployees { get { return Employees.Count; } }
}

The above class is just made up of some practical things that your view might contain. My Employee class just looks like this:

public class Employee
{
public Employee(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}

public string FirstName { get; set; }
public string LastName { get; set; }
}

  1. Back in the DefaultPage Class that we created in Step 6, add the following code:

public class DefaultPage : BasePage<SampleDataView>
{
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
return;

initializePage();
}

private void initializePage()
{
//this is where you'd call your repository and possibly automapper to map from your domain model to your view model
ViewModel.DepartmentName = "Sample Department";
ViewModel.Employees.Add(
new Employee("John", "Smith"));
ViewModel.Employees.Add(
new Employee("Cindy", "Sue"));
ViewModel.Employees.Add(
new Employee("Speed", "Racer"));
ViewModel.Employees.Add(
new Employee("Joan", "Arc"));
}
}

  1. Now we can setup our view in the Default.aspx page like this:

<%@ Page Language="C#" AutoEventWireup="true" Inherits="DefaultPage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<
html xmlns="http://www.w3.org/1999/xhtml">
<
head runat="server">
<
title>WebFormContrib Sample Part 1</title>
</
head>
<
body>
<
form id="form1" runat="server">
<h3><%=ViewModel.DepartmentName %></h3>
<
p>Total Employees: <%=ViewModel.TotalEmployees %></p>
<
ul>
<% foreach (var employee in ViewModel.Employees) {%>
<li><%=employee.FirstName %> <%=employee.LastName %></li>
<% }%>
</ul>
</form>
</
body>
</
html>

As you can see, it looks pretty much exactly like MVC and it allows you to keep complete control of your HTML. When you run your app, you should see this:

image

Download the sample project here.

Please comment if you have any questions or suggestions. Thanks for reading!


Shout it

kick it on DotNetKicks.com

Thursday, November 25, 2010

WebFormContrib – Web Forms more like ASP.NET MVC




Lately I’ve been stuck working on projects using Web Forms and after reading one of Jimmy Bogard’s recent blog posts, I was inspired to create a small library similar to MVC Contrib, but for Web Forms. Obviously my little library won't be nearly as good as MVC Contrib because I'm not as talented as the guys at HeadSpring. However, I'm hoping this will be a good start to something great. Anyhow, the main thing that stuck out to me was the strongly-typed views, which I never used to do with Web Forms. With all that said, I thought I’d share what I created.

The project I’m currently working on is built on a 3rd party system and it’s difficult to add 3rd party references into our add-ons. So, I added a very basic validator into this thing, which will most likely be refactored, but works for now.

Onto the code I suppose…

So, first things first, let’s look at my WebForm view, which in this case is a UserControl, but the same thing can be done with a regular aspx Page.

<%@ Control Language="C#" AutoEventWireup="true" Inherits="WebFormContrib.Sample.Core.UI.Model.SampleControl" %>
<%
@ Import Namespace="WebFormContrib.Controls" %>
<%
@ Import Namespace="WebFormContrib.Helpers" %>
<h1>Employee Form</h1>
<%=ErrorMessages.ToList("Important Message").CssClass("err") %>
<div id="success" runat="server" enableviewstate="false" visible="false">Employee saved.</div>
<
p><%=Html<TextBox>(f=>f.FirstName).Label("First Name:<br/>").Width("300px").CssClass("tb") %></p>
<
p><%=Html<TextBox>(f=>f.LastName).Label("Last Name:<br/>").Width("300px") %></p>
<
p><%=Html<TextBox>(f=>f.EmailAddress).Label("Email Address:<br/>").Width("300px") %></p>
<
p>Do you want to receive emails?<br />
<%=Html<YesNoRadioButtonList>(f=>f.ReceiveEmails) %></p>

<
p><%=Html<TextArea>(f=>f.Bio).Width("500px").Label("Bio:<br/>") %></p>

<
asp:Button ID="bSend" runat="server" Text="Save Employee" CausesValidation="true" OnClick="Save" />

There are a few things to notice. First is the fact that there’s no code-behind page. Everything is inherited from my core project, just like I setup my MVC apps. You can see how I do that in this post.

As you can see, this is a total rip-off of the MVC type of HTML page. I left in a couple ASP controls too. There are a couple of controls that didn’t exist before like YesNoRadioButtonList and TextArea. These two objects were added as helpers.

All these extensions that make it look like fluentHtml are just extension methods on string. For example:

public static string Label(this string html, string label)
{
return string.Format("<label for=\"{0}\">{1}</label> {2}", html.GetId(), label, html);
}

So what does the SampleControl look like? It looks like this:

    public class SampleControl : BaseUserControl<EmployeeView>
{
private IEmployeeRepository _repository;
protected void Page_Load(object sender, EventArgs e)
{
_repository =
DI.CreateRepository();

if (Page.IsPostBack)
return;

loadEmployee();
}

private void loadEmployee()
{
var e = _repository.GetBy(Page.User.Identity.Name);
ViewModel =
EmployeeView.MapEmployeeToView(e); //Could user AutoMapper
}

protected void Save(object sender, EventArgs e)
{
if (!Page.IsValid || !ModelIsValid(ViewModel))
return;

var form = ViewModel.MapToEmployee(); //Could user AutoMapper
_repository.Save(form);

C<
HtmlGenericControl>("success").Visible = true;
}
}

This is all there is to my little employee edit user control. You should notice I set the ViewModel to a mapped version of my domain model to my view. In the save, I’m having to call both Page.IsValid and ModelIsValid…although since I don’t have any ASP validators, I could technically just call the ModelIsValid(ViewModel). Other than that, I map back to my domain model from my view model and call save. Then I use a generic control finder built into the BaseUserControl<T> to get my ASP success generic html control. You can also see that I added comments on where you might benefit from AutoMapper with this method.

So my view in this case looks like this:

    public class EmployeeView
{
[
Required("First Name is required.")]
public string FirstName { get; set; }
[
Required("Last Name is required.")]
public string LastName { get; set; }
public string Bio { get; set; }
public string EmailAddress { get; set; }
public bool ReceiveEmails { get; set; }

public Employee MapToEmployee()
{
var employee = new Employee();
employee.Name.FirstName = FirstName;
employee.Name.LastName = LastName;
employee.EmailAddress = EmailAddress;
employee.Biography = Bio;
return employee;
}

public static EmployeeView MapEmployeeToView(Employee employee)
{
var view = new EmployeeView();
view.FirstName = employee.Name.FirstName;
view.LastName = employee.Name.LastName;
view.EmailAddress = employee.EmailAddress;
view.Bio = employee.Biography;
return view;
}
}

The Required attributes are also contained in the WebFormContrib dll. It uses similar syntax to the MVC 2 validators. The BaseUserControl<T> looks like this:

    public class BaseUserControl<TModel> : BaseUserControl where TModel : new()
{
private readonly BaseWebForm<TModel> _baseWebForm;
public BaseUserControl()
{
_baseWebForm =
new BaseWebForm<TModel>();
}

protected IList<string> ErrorMessages
{
get { return _baseWebForm.ErrorMessages; }
}

protected bool DisableAll
{
get { return _baseWebForm.DisableAll; }
set { _baseWebForm.DisableAll = value; }
}

protected TModel ViewModel
{
get
{
try { _baseWebForm.Request = Request; }
catch { }
return _baseWebForm.ViewModel;
}
set { _baseWebForm.ViewModel = value; }
}

protected bool ModelIsValid(TModel view)
{
return _baseWebForm.ModelIsValid(view);
}

protected string Html<TControl>(Expression<Func<TModel, object>> func) where TControl : Control, new()
{
return _baseWebForm.Html<TControl>(func);
}
}

This inherits from a BaseUserControl, which inherits from UserControl and has another method associated that I’ll show later. Basically, this provides all the functionality of BaseWebForm<TModel>, which I will show last. The BaseWebForm<TModel> is the meat of the whole project. The try{} catch{} around the _baseWebForm.Request is only there so my unit tests won’t fail when trying to get the HttpRequest. So the BaseUserControl looks like this:

    public class BaseUserControl : UserControl
{
protected TControl C<TControl>(string id) where TControl : Control
{
return (TControl)FindControl(id);
}
}

Pretty basic stuff here…looks for the control and then casts to the specified type. BasePage<T> and BasePage look exactly the same as these two, except the BasePage inherits from Page instead of UserControl.

Now…BaseWebForm<T> I’m going to breakdown. It inherits from Control and T must allow new().

        internal TModel ViewModel
{
get
{
if (_view != null)
return _view;

_view =
new TModel();

foreach (var property in typeof (TModel).GetProperties())
try
{
if (Request.Form[property.Name] != null)
property.SetValue(_view, Request.Form[property.Name]);
}
catch (NullReferenceException) {}

return _view;
}
set
{
_view =
value;
}
}

First things first…the ViewModel…I loop through the properties of the view model object and pull out the key from Request.Form and then set the value of the property on the _view object. The try {} catch{} here is also for unit testing purposes.

The ModelIsValid looks like this:

       internal bool ModelIsValid(TModel view)
{
ErrorMessages =
new List<string>();
foreach (var property in typeof(TModel).GetProperties())
{
var attributes = property.GetCustomAttributes(typeof(RequiredAttribute), false);
if (attributes.Length > 0)
{
var value = property.GetValue(view, null);
if (value.ToSafeString() == string.Empty)
ErrorMessages.Add(((
RequiredAttribute)attributes[0]).Message);
}
}

if (ErrorMessages.Count == 0)
return true;

return false;
}

This will be the first thing I refactor. The thing I hate most is that the actual validation of the RequiredAttribute happens here instead of in the attribute. However, it works for now. If anyone has any suggestions, please add a comment.

Finally, the Html<TControl> looks like this:

    internal string Html<TControl>(Expression<Func<TModel, object>> func) where TControl : Control, new()
{
var id = func.ToId();
var control = new TControl { ID = id };
control.SetValue(func.Compile().Invoke(ViewModel));

using (var textwriter = new StringWriter())
using (var htmlwriter = new HtmlTextWriter(textwriter))
{
if (DisableAll)
control.Disable();
control.RenderControl(htmlwriter);
return textwriter.ToString();
}
}

This one required TControl to be of type Control and allow new(). I get the Id or PropertyName from func using an extension method and then I create the TControl object. Afterward, I set the value of it from ViewModel then I render it and return the string.

Here are the tests I have:

    [TestFixture]
public class BaseControlTests
{
[
Test]
public void Generic_C_should_return_any_object_by_id_contained_in_user_control_controls()
{
var testcontrol = new TestUserControl();
var textbox = testcontrol.GetTestUsingC<TextBox>("testid");

Assert.That(textbox, Is.TypeOf(typeof (TextBox)));
Assert.That(textbox.ID, Is.EqualTo("testid"));
}
}

public class TestUserControl : BaseUserControl
{
public TestUserControl()
{
Controls.Add(
new TextBox{ID="testid"});
}

public T GetTestUsingC<T>(string id) where T : Control
{
return C<T>(id);
}
}
    [TestFixture]
public class BaseControlTTests
{
[
Test]
public void Html_should_render_html_textbox_with_an_id_of_FirstName()
{
var testcontrol = new TestWebForm();
var textbox = testcontrol.ExposedHtml<TextBox>(f => f.FirstName);

Assert.That(textbox, Is.EqualTo("<input name=\"FirstName\" type=\"text\" id=\"FirstName\" />"));
}

[
Test]
public void Html_should_render_html_literal()
{
var testcontrol = new TestWebForm();
var literal = testcontrol.ExposedHtml<Literal>(x => x.LastName);
Assert.That(literal, Is.EqualTo(""));
}

[
Test]
public void Html_should_render_html_textarea_with_an_id_of_bio()
{
var testcontrol = new TestWebForm();
var textarea = testcontrol.ExposedHtml<TextArea>(x => x.Bio);
Assert.That(textarea, Is.EqualTo("<textarea name=\"Bio\" rows=\"10\" cols=\"20\" id=\"Bio\"></textarea>"));
}

[
Test]
public void ModelIsValid_should_return_false()
{
var testcontrol = new TestWebForm();

Assert.That(testcontrol.ExposedModelIsValid(), Is.False);
}

[
Test]
public void ModelIsValid_should_return_true()
{
var testcontrol = new TestWebForm();

testcontrol.ExposedViewModel.FirstName =
"test";
Assert.That(testcontrol.ExposedModelIsValid(), Is.True);
}

[
Test]
public void ModelIsValid_should_return_false_and_error_message_should_contain_one_record()
{
var testcontrol = new TestWebForm();

Assert.That(testcontrol.ExposedModelIsValid(), Is.False);
Assert.That(testcontrol.ExposedErrorMessages.Count, Is.EqualTo(1));
Assert.That(testcontrol.ExposedErrorMessages[0], Is.EqualTo("First Name is Required."));
}

[
Test]
public void Html_should_return_disabled_objects_if_disableall_is_true()
{
var testcontrol = new TestWebForm();
testcontrol.ExposedDisable =
true;

var textbox = testcontrol.ExposedHtml<TextBox>(f => f.FirstName);
Assert.That(textbox, Is.EqualTo("<input name=\"FirstName\" type=\"text\" readonly=\"readonly\" id=\"FirstName\" disabled=\"disabled\" />"));
}
}

public class TestWebForm : BasePage<TestViewModel>
{
public string ExposedHtml<TControl>(Expression<Func<TestViewModel, object>> func) where TControl : Control, new()
{
return Html<TControl>(func);
}

public bool ExposedModelIsValid()
{
return ModelIsValid(ViewModel);
}

public IList<string> ExposedErrorMessages
{
get { return ErrorMessages; }
}

public TestViewModel ExposedViewModel
{
get { return ViewModel; }
}

public bool ExposedDisable
{
get { return DisableAll;}
set { DisableAll = value; }
}
}

public class TestViewModel
{
[
Required("First Name is Required.")]
public string FirstName { get; set;}
public string LastName { get; set;}
public string EmailAddress { get; set;}
public string Bio { get; set; }
}

Well, that’s it for now. Please let me know what you think. You can download the code here. You can browse it here. You can download the dll here.

Please let me know if you have any suggestions or comments. Have a great day! Thanks for reading!

Happy Thanksgiving!


Shout it

kick it on DotNetKicks.com

Related Posts Plugin for WordPress, Blogger...