35

In my ASP.net MVC app I have a view that looks like this:

...
<label>Due Date</label>
<%=Html.TextBox("due")%>
...

I am using a ModelBinder to bind the post to my model (the due property is of DateTime type). The problem is when I put "01/01/2009" into the textbox, and the post does not validate (due to other data being input incorrectly). The binder repopulates it with the date and time "01/01/2009 00:00:00".

Is there any way to tell the binder to format the date correctly (i.e. ToShortDateString())?

Corin Blaikie
  • 17,718
  • 10
  • 37
  • 39
  • I hit this yesterday as well. I wish they had an overload that let you pass in a format string. – Chris Sutton Sep 25 '08 at 14:27
  • I think the solution provided here is cleaner: http://stackoverflow.com/questions/8089316/input-with-date-format-set-from-viewmodel-and-html-class-attribute –  Mar 29 '12 at 09:24

12 Answers12

55

I just came across this very simple and elegant solution, available in MVC 2:

Link

Basically if you are using MVC 2.0, use the following in your view.

 <%=Html.LabelFor(m => m.due) %>
 <%=Html.EditorFor(m => m.due)%>

then create a partial view in /Views/Shared/EditorTemplates, called DateTime.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.DateTime?>" %>
<%=Html.TextBox("", (Model.HasValue ? Model.Value.ToShortDateString() : string.Empty), new { @class = "datePicker" }) %>

When the EditorFor<> is called it will find a matching Editor Template.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Nick Chadwick
  • 551
  • 4
  • 2
  • 2
    This worked for me, but surely it will replace *all* instances of DateTimes... what if you have different formatting requirements for different instances? – saw-lau Jul 23 '10 at 10:49
  • 8
    @saw-lau: you can create multiple templates as above with different names, then pass in the template name as the second argument to Html.EditorFor() as required. – Nick Chadwick Aug 05 '10 at 14:36
24

Decorate the property in your model with the DataType attribute, and specify that its a Date, and not a DateTime:

public class Model {
  [DataType(DataType.Date)]
  public DateTime? Due { get; set; }
}

You do have to use EditorFor instead of TextBoxFor in the view as well:

@Html.EditorFor(m => m.Due)
quentin-starin
  • 26,121
  • 7
  • 68
  • 86
Qerim Shahini
  • 711
  • 5
  • 12
9

It's a dirty hack, but it seems to work.

<%= Html.TextBoxFor(model => model.SomeDate,
    new Dictionary<string, object> { { "Value", Model.SomeDate.ToShortDateString() } })%>

You get the model binding, and are able to override the HTML "value" property of the text field with a formatted string.

Cephas
  • 794
  • 8
  • 14
  • 7
    me likey. short and sweet. here's my slight variation: <%= Html.TextBoxFor( model => model.SomeDate, new { @Value = Model.SomeDate.ToShortDateString() } )%> –  Nov 12 '10 at 21:00
  • +1 for both of you, this little trick saved me a lot of time :). – Dusda Nov 24 '10 at 08:18
  • Does not work with client side validation enabled, as that one will use the date from the model and if it is different in format for that locale it's validating against, it will block the submit. – Viezevingertjes Sep 20 '13 at 13:28
4

I found this question while searching for the answer myself. The solutions above did not work for me because my DateTime is nullable. Here's how I solved it with support for nullable DateTime objects.

<%= Html.TextBox(String.Format("{0:d}", Model.Property)) %>
Craig M
  • 5,598
  • 4
  • 32
  • 43
4

Why don't you use

<% =Html.TextBox("due", Model.due.ToShortDateString()) %>
Leon Bambrick
  • 26,009
  • 9
  • 51
  • 75
Casper
  • 1,242
  • 1
  • 11
  • 12
3

First, add this extension for getting property path:

public static class ExpressionParseHelper
{
    public static string GetPropertyPath<TEntity, TProperty>(Expression<Func<TEntity, TProperty>> property)
    {                       
         Match match = Regex.Match(property.ToString(), @"^[^\.]+\.([^\(\)]+)$");
         return match.Groups[1].Value;
    }
}

Than add this extension for HtmlHelper:

 public static MvcHtmlString DateBoxFor<TEntity>(
                this HtmlHelper helper,
                TEntity model,
                Expression<Func<TEntity, DateTime?>> property,
                object htmlAttributes)
            {
                DateTime? date = property.Compile().Invoke(model);
                var value = date.HasValue ? date.Value.ToShortDateString() : string.Empty;
                var name = ExpressionParseHelper.GetPropertyPath(property);

                return helper.TextBox(name, value, htmlAttributes);
            }

Also you should add this jQuery code:

$(function() {
    $("input.datebox").datepicker();
});

datepicker is a jQuery plugin.

And now you can use it:

<%= Html.DateBoxFor(Model, (x => x.Entity.SomeDate), new { @class = "datebox" }) %>

ASP.NET MVC2 and DateTime Format

Community
  • 1
  • 1
Serhiy
  • 4,357
  • 5
  • 37
  • 53
1

In order to get strongly typed access to your model in the code behind of your view you can do this:

public partial class SomethingView : ViewPage<T>
{
}

Where T is the ViewData type that you want to pass in from your Action.

Then in your controller you would have an action :

public ActionResult Something(){
    T myObject = new T();
    T.Property = DateTime.Today();

    Return View("Something", myObject);
}

After that you have nice strongly typed model data in your view so you can do :

<label>My Property</label>
<%=Html.TextBox(ViewData.Model.Property.ToShortDateString())%>
Switters
  • 1,553
  • 2
  • 12
  • 13
  • Thanks for the reply. I am aware of strongly typed pages and the benefits they bring, I believe this is more applicable in an editing view. I was specifically after information that is applicable to using a modelbinder (in a creation view) . – Corin Blaikie Sep 25 '08 at 15:50
1

I find the best way to do this is to reset the ModelValue

ModelState.SetModelValue("due", new ValueProviderResult(
       due.ToShortDateString(), 
       due.ToShortDateString(), 
       null));
Ollie
  • 885
  • 1
  • 7
  • 14
0

I guess personally I'd say its best or easiest to do it via a strongly typed page and some defined model class but if you want it to be something that lives in the binder I would do it this way:

public class SomeTypeBinder : IModelBinder
{
    public object GetValue(ControllerContext controllerContext, string modelName,
                              Type modelType, ModelStateDictionary modelState)
    {
        SomeType temp = new SomeType();
        //assign values normally
        //If an error then add formatted date to ViewState
        controllerContext.Controller.ViewData.Add("FormattedDate",
                              temp.Date.ToShortDateString());
    }
}

And then use that in the view when creating the textbox i.e. :

<%= Html.TextBox("FormattedDate") %>

Hope that helps.

Brock Adams
  • 90,639
  • 22
  • 233
  • 295
Switters
  • 1,553
  • 2
  • 12
  • 13
0

Try this

<%:Html.TextBoxFor(m => m.FromDate, new { @Value = (String.Format("{0:dd/MM/yyyy}", Model.FromDate)) }) %>
sgarizvi
  • 16,623
  • 9
  • 64
  • 98
Krushna
  • 26
  • 3
0

MVC4 EF5 View I was trying to preload a field with today's date then pass it to the view for approval.

ViewModel.SEnd = DateTime.Now    //preload todays date  
return View(ViewModel)           //pass to view

In the view, my first code allowed an edit:

@Html.EditedFor(item.SEnd)        //allow edit

Later I changed it to just display the date, the user cannot change it but the submit triggers the controller savechanges

 <td>
 @Html.DisplyFor(item.SEnd)       //show no edit
 </td>

When I changed to DisplayFor I needed to add this to ensure the preloaded value was passed back to the controller. I also need to add HiddenFor's for every field in the viewmodel.

    @Html.HiddenFor(model => model.SEnd)     //preserve value for passback.

Beginners stuff but it took a while to work this out.

user2887440
  • 51
  • 1
  • 8
0

This worked for me: mvc 2

<%: Html.TextBoxFor(m => m.myDate, new { @value = Model.myDate.ToShortDateString()}) %>

Simple and sweet!

A comment of user82646, thought I'd make it more visible.

RayLoveless
  • 19,880
  • 21
  • 76
  • 94