Navigation and service panel


Content

Custom date format in Sitecore MVC forms

By Kevin Brechbühl on 10. November 2014, No comments

Usually in .net solutions, date formats are based on the current culture. In Switzerland the situation is a bit complicated: We have three cultures (German, French and Italian) but almost every website also covers English, which is not an available culture. This means we don't have a valid combination of language and format infos. With this blog post I want to show a way to use custom date format in MVC forms.

In Switzerland the date format is always dd.MM.yyyy (day before the month). Because we don't have a valid culture for en-CH, we usually set another english language, e.g. en-US, which means that the date format will change to M/d/yyyy (month before the day). Without any customizations, this results in two issues:

  • Prefilled textboxes with a date have the wrong format
  • The validation fails if the user enters a wrong date format

Let's build a solution for this.

Visible date format

Starting point is a simple view model with only one property for the date:

public class DateExampleViewModel
{
    public DateTime Date { get; set; }
}

This view model is handled by our controller, which is added as a controller rendering to my Sitecore item presentation:

public class DateExampleController : Controller
{
    public ActionResult Index()
    {
        var model = new DateExampleViewModel {Date = new DateTime(2014, 8, 22)};
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(DateExampleViewModel model)
    {
        return View(model);
    }
}

In the razow view, we need to specify the format for the date in the second parameter of the @Html.TextBoxFor() call:

@model Website.Models.DateExampleViewModel

@using (Html.BeginForm())
{
    @Html.LabelFor(m => m.Date)
    @Html.TextBoxFor(m => m.Date, "{0:dd.MM.yyyy}")
    @Html.ValidationMessageFor(m => m.Date)

    <input type="submit" value="Submit" />
}

Of course the format string (here dd.MM.yyyy) should not be hard coded here. We usually extend the language items under /sitecore/system/Languages and read the format string from the current language item.

Validation

The ASP.net model binding is responsible for mapping the values from the form POST back to the view model. There is a specific model binder for each type, also for the DateTime. The default model binder tries to map the date in the format of the current culture, which won't work for our example. So we need to create a custom model binder. This is very easy as we only need to implement IModelBinder, parse the date with our format string and return it:

public class DateTimeModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // get the value
        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (value == null) return null;

        // set the culture
        var cultureInfo = (CultureInfo)CultureInfo.CurrentCulture.Clone();
        cultureInfo.DateTimeFormat.ShortDatePattern = "dd.MM.yyyy";

        // set the new modelstate
        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);

        try
        {
            return value.ConvertTo(typeof(DateTime), cultureInfo);
        }
        catch
        {
            return null;
        }
    }
}

Finally at the application start we need to register this new model binder for dates (be sure to install WebActivatorEx NuGet package):

[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(Website.App_Start.Bootstrapper), "PostStart")]
namespace Website.App_Start
{
    public class Bootstrapper
    {
        public static void PostStart()
        {
            ModelBinders.Binders.Add(typeof(DateTime), new DateTimeModelBinder());
        }
    }
}

An easier solution?

Some of you may know an easier solution for this example. There is: you can simply specify the date format with an attribute on the view model:

[DisplayFormat(DataFormatString = "{0:dd.MM.yyyy}")]
public DateTime Date { get; set }

But this is only possible, if you can hard code the format string. In many cases the format is dependent by a context (e.g. the user, or the current language) where the attribute can't be used anymore.

No comments

Add your comment

Your email address will not be published. Required fields are marked *

*