Tuesday, December 18, 2012

DateTime EditorTemplates and DefaultModelBinder

In order to use EditorTemplates with ASP.NET MVC 3 and Razor you need to do the following:
1. Create a folder named EditorTemplates in Views\Shared.
2. Add a new MVC3 View Page (Razor)

Inside the cshtml file write the code for the template:


@model System.DateTime?
@* <div class="editor-label">
    @Html.Label("")
</div>*@
<div class="editor-field">
    @Html.TextBox("",
    String.Format(@"{0:dd.MM.yyyy}",
        (Model.HasValue && Model.Value > new DateTime(1,1,1) )? Model.Value.Date : DateTime.Today),
                    new { @class = "editor-field" }
                 )
</div>

<script type="text/javascript">
    $(document).ready(function () {
        $("#@ViewData.ModelMetadata.PropertyName").datepicker({
            changeMonth: true,
            changeYear: true,
            dateFormat: "dd.mm.yy",
            firstDay: 1 // starts on monday
        });
    });
</script>


In my model I had the DateTime fields as not nullable and therefore I always seemed to receive the date 01/01/01. If my model had no value defined, the textbox would show the date 01/01/01. To overcome this I added the condition that Model.Value > new DateTime(1,1,1).


The problem with the code above was that it returned an invalid date if the computer that was accessing the site had a different format of the date (eg.: MM/dd/yyyy). The "cure" for this was to override the BindProperty in a class that inherited from DefaultModelBinder:



public class DateFixModelBinder :  DefaultModelBinder
    {

        protected override void BindProperty(ControllerContext controllerContext,
            ModelBindingContext bindingContext,
            System.ComponentModel.PropertyDescriptor propertyDescriptor)
        {

            if (propertyDescriptor.PropertyType == typeof(DateTime))
            {
                var model = bindingContext.Model;
                PropertyInfo property = model.GetType().GetProperty(propertyDescriptor.Name);
                DateTime date = DateTime.Today.Date;

                try
                {

                    var value = bindingContext.ValueProvider.GetValue(propertyDescriptor.Name);
                    if (value != null)
                    {
                        date = DateTime.ParseExact(value.AttemptedValue, "dd.MM.yyyy", null);
                        property.SetValue(model, date, null);

                    }
                }
                catch (Exception)
                {

                    property.SetValue(model, date, null);
                }

            }
            else
            {
                base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
            }


        }


Also in Global.asax, in Application_Start,  you need to write the following code:

 ModelBinders.Binders.DefaultBinder = new DateFixModelBinder();