Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Model binding TimeSpan from integer

I would like to declare some properties of my View Model of type TimeSpan to display the TotalMinutes property and bind back to a TimeSpan.

I've bound the property without using the strongly-typed helper in order to retrieve the TotalMinutes property:

<%=Html.TextBox("Interval", Model.Interval.TotalMinutes)%>

When the field is bound back to the View Model class it parses the number as a day (1440 mins).

How can I override this behaviour on certain properties (preferably using attributes on the View Model itself)?

like image 713
David Neale Avatar asked Nov 02 '10 15:11

David Neale


1 Answers

Writing a custom model binder seems like a good idea here:

public class TimeSpanModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".TotalMinutes");
        int totalMinutes;
        if (value != null && int.TryParse(value.AttemptedValue, out totalMinutes))
        {
            return TimeSpan.FromMinutes(totalMinutes);
        }
        return base.BindModel(controllerContext, bindingContext);
    }
}

And registering it in Application_Start:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);
    ModelBinders.Binders.Add(typeof(TimeSpan), new TimeSpanModelBinder());
}

And finally always prefer strongly typed helpers in your view:

<% using (Html.BeginForm()) { %>
    <%= Html.EditorFor(x => x.Interval) %>
    <input type="submit" value="OK" />
<% } %>

And the corresponding editor template (~/Views/Home/EditorTemplates/TimeSpan.ascx):

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<TimeSpan>" %>
<%= Html.EditorFor(x => x.TotalMinutes) %>

Now your controller could be as simple as:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new MyViewModel
        {
            Interval = TimeSpan.FromDays(1)
        };
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        // The model will be properly bound here
        return View(model);
    }
}
like image 142
Darin Dimitrov Avatar answered Sep 19 '22 18:09

Darin Dimitrov