Introduction
In the example below I would like to break up the date into three fields: Day, Month, and Year, on my view, but I would like my model to have just one property that can store the combination of these fields.
Background
Recently I was looking at an unusual problem where we had to add custom fields to a view on an ad hoc basis. Not really a problem if it was an MVC app talking directly to a data source. But mine was over several REST based services that were talking to a CRM dynamics backend, spread across multiple domains. Which meant we had to change the data models through out the system. That's when it occurred to me that we can have one dedicated field that can store all the ad hoc field data using JASON or xml and we append it to one of the existing fields. Probably not the ideal solution, but that's when I came across custom binding for MVC. I would like to share with you how simple and easy it is to use custom binding to solve similar issues.
Using the Code
View
In my view I am going to provision three fields for the date within my form.
<form id="Home" action="" method="POST">
Day <input id="Day" name="Day" value="" type="text" />
Month <input id="Month" name="Month" value="" type="text" />
Year <input id="Year" name="Year" value="" type="text" />
<input id="Submit" type="Submit" value="Submit" />
</form>
Model
In my model I will only have one property for the three fields called a Date
:
public class HomePageModels
{
public string Title { get; set; }
public string Date { get; set; }
}
Custom Binding
The custom binding class needs to inherit form IModelBinder
. Here we capture the current request and extract the Form fields individually. Then we can manipulate these fields any way we like. In this example as you can see I am adding them to a single property called Date
.
public class HomeCustomBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
HttpRequestBase request = controllerContext.HttpContext.Request;
string title = request.Form.Get("Title");
string day = request.Form.Get("Day");
string month = request.Form.Get("Month");
string year = request.Form.Get("Year");
return new HomePageModels
{
Title = title,
Date = day +"/"+ month +"/"+ year
};
}
}
Alternatively if we do not want to implement custom binding for each and every Model and Property in our application we can inherit from the DefaultModelBinder and override the BindModel method as below
public class HomeCustomDataBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType == typeof(HomePageModels))
{
HttpRequestBase request = controllerContext.HttpContext.Request;
string title = request.Form.Get("Title");
string day = request.Form.Get("Day");
string month = request.Form.Get("Month");
string year = request.Form.Get("Year");
return new HomePageModels
{
Title = title,
Date = day + "/" + month + "/" + year
};
}
else
{
return base.BindModel(controllerContext, bindingContext);
}
}
}
Once we have completed coding our custom class we will need to register the class which I do in the Global.asax under Application_Start()
.
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
ModelBinders.Binders.Add(typeof(HomePageModels), new HomeCustomBinder());
}
Controller
Finally we need to inform the controller as to the binding we want it to use. This we can specify using attributes [ModelBinder(typeof(HomeCustomBinder))]
as below:
[HttpPost]
public ActionResult Index([ModelBinder(typeof(HomeCustomBinder))] HomePageModels home)
{
if (ModelState.IsValid)
{
ViewBag.Title = home.Title;
ViewBag.Date = home.Date;
}
return View();
}
Points of Interest
While you could argue that we can achieve the same by manually capturing the requests inside the controller and manipulating how the property is populated, the idea behind using custom binding is to facilitate reuse and thus avoid manually wiring up views and models.