Using ModelState with MvcContrib's Fluent HTML Helpers

What is ModelState?

The ASP.NET MVC ModelState dictionary allows you to record validation errors which can then be displayed in your web page.

For example, imagine you add a textbox to a page using the MVC framework's built-in HTML helpers, then this will render a standard text field:

Your name: <%= Html.TextBox("Name") %>

In your controller, if you add an error to the ModelState dictionary with the same name as the textbox, then the HTML helper will change the styling of the input field by adding a css class of "input-validation-error".

public class HomeController : Controller {
   public ActionResult Index() {
       ModelState.AddModelError("Name", "Please enter a name");
       return View(new MyViewModel { Name = "" });
   }
}
Using the default ASP.NET MVC template, it will add a red border with a light pink background:

Textbox error

You can also display the actual validation errors by making use of the ValidationSummary helper:

<%= Html.ValidationSummary() %>
Enter your name: <%= Html.TextBox("Name") %>

Which will display something like this:

Validation Summary

ModelState with FluentHtml

By default, the Fluent HTML helpers in MvcContrib do not have support for ModelState. However, it is very easy to add support for this by writing a custom MemberBehaviour (I previously wrote about extending the HTML helpers with MemberBehaviors here)

Here is the implementation of the ModelStateMemberBehavior class:

public class ModelStateMemberBehavior : IMemberBehavior {
	private readonly ModelStateDictionary modelState;
 
	public ModelStateMemberBehavior(ModelStateDictionary modelState) {
		this.modelState = modelState;
	}
 
	public void Execute(IMemberElement element) {
		ModelState state;
		if (element.Builder.Attributes.ContainsKey("name")) {
			if (modelState.TryGetValue(element.Builder.Attributes["name"], out state)) {
				element.Builder.AddCssClass("input-validation-error");
			}
		}
	}
}

This class will check whether the name of the element being rendered is present in the specified ModelState dictionary. If so, it will add a css class of "input-validation-error" to the element being rendered.

Now we need to hook this into the HTML helpers. This can be done by implementing the IViewModelContainer interface on your base View Page:

public class MyBaseViewPage<T> : ViewPage<T>, IViewModelContainer<T> where T : class {
   private List<IMemberBehavior> memberBehaviors = new List<IMemberBehavior>();
 
   protected override void SetViewData(ViewDataDictionary viewData) {
      base.SetViewData(viewData);
      behaviors.Add(new ModelStateMemberBehavior(ViewData.ModelState));
   }
 
   public T ViewModel {
      get { return ViewData.Model; }
   }
 
   public IEnumerable<IMemberBehavior> MemberBehaviors {
	get { return memberBehaviors; }
   }
}

Now, so long as all your view pages inherit from MyBaseViewPage, any calls to the fluent HTML helpers will have support for ModelState:

Your name: <%= this.TextBox(model => model.Name) %>
Written on December 18, 2008