Change date format in ASP.NET MVC application - asp.net-mvc

I need to change date format to be dd.MM.yyyy. I am getting client side validation error because ASP.NET MVC date format is different from what I expect on the server.
In order to change ASP.NET MVC date format I tried:
Web.config:
<globalization uiCulture="ru-RU" culture="ru-RU" />
Model:
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd.MM.yyyy}", ApplyFormatInEditMode = true)]
public DateTime? ServiceCreatedFrom { get; set; }
Editor template:
#model DateTime?
#Html.TextBox(string.Empty, (Model.HasValue
? Model.Value.ToString("dd.MM.yyyy")
: string.Empty), new { #class = "date" })
View:
#Html.EditorFor(m => m.ServiceCreatedFrom, new { #class = "date" })
Even Global.asax:
public MvcApplication()
{
BeginRequest += (sender, args) =>
{
var culture = new System.Globalization.CultureInfo("ru");
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
};
}
Nothing worked for me.

The following should work:
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd.MM.yyyy}", ApplyFormatInEditMode = true)]
public DateTime? ServiceCreatedFrom { get; set; }
and in your editor template:
#model DateTime?
#Html.TextBox(
string.Empty,
ViewData.TemplateInfo.FormattedModelValue,
new { #class = "date" }
)
and then:
#Html.EditorFor(x => x.ServiceCreatedFrom)
The second argument you were passing to the EditorFor call doesn't do what you think it does.
For this custom editor template, since you specified the format explicitly on your view model property the <globalization> element in your web.config and the current thread culture will have 0 effect. The current thread culture is used with the standard templates and when you didn't override the format with the [DisplayFormat] attribute.

You can change the current culture in your Global.asax file, for application level
For Example,
using System.Globalization;
using System.Threading;
protected void Application_BeginRequest(Object sender, EventArgs e)
{
CultureInfo newCulture = (CultureInfo) System.Threading.Thread.CurrentThread.CurrentCulture.Clone();
newCulture.DateTimeFormat.ShortDatePattern = "dd-MMM-yyyy";
newCulture.DateTimeFormat.DateSeparator = "-";
Thread.CurrentThread.CurrentCulture = newCulture;
}

As a potential aid for identifying the issue, are you able to:
1. Set a breakpoint at the point where you're trying to format the date
2. Use something like the Immediate Window in Visual Studio to evaluate the value of
Thread.CurrentThread.CurrentCulture.Name
If you do this, does it come back with the "ru-RU" culture?
I'm sure I'm not the only one that would be happy to help you work through debugging this. That said, perhaps somebody quicker than me can see the problem straight away :).
Edit:
It looks like you're using Razor, so you should be able to set a breakpoint directly in the view file on the line where you're trying to format the date.
Edit #2:
There may be a cleaner way to do this, but if the form data is being posted in dd.MM.yyyy then you might need a custom model binder, something like:
public class CustomModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// custom bind the posted date
}
}
...which would then get assigned as a model binder in e.g. ApplicationStart in Global.asax.cs.
Let me know if you think this might help and I can elaborate.

finally this one worked for me to get this dd/mm/yyyy format i used date = string.Format("{0}/{1}/{2}", Model.Value.Day, Model.Value.Month, Model.Value.Year);
first create EditorTemplates folder in Sharedfolder,
then create a Datetime editor template in Sharedfolder/EditorTemplates/Datetime.cshtml then follow the above link.
in view
#Html.EditorFor(x => x.ServiceCreatedFrom)
hope helps someone.

Related

In ModelState the Date format is not valid while editing Kedno grid

I am developing MVC application with Kedno grid.
I have a model Item with properties:
public bool IsPaid { get; set; }
public Nullable<DateTime> PaymentDate { get; set; }
In Kedno grid in the View:
columns.Bound(c => c.PaymentDate).Format("{0:dd-MMM-yyyy}");
When I am editing in the grid and set IsPaid to false to logic has to be setting PaymentDate to null.
For example I have an Item with PaymentDate 22-Sep-2016 and IsPaid to true.
After editing in Kendo grid this Item setting IsPaid to false, and when I click Save Changes, it is invoked the method Update in the controller:
public ActionResult Update([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<ItemViewModel> items)
{
if (ModelState.IsValid)
{
//
}
}
But the ModelState is not valid: "The value '9/22/2016 12:00:00 AM' is not valid for...".
If the PaymentDate is 01-Sep-2016, the ModelState is valid.
Also, I followed the steps in the Globalization section.
In web.config:
<globalization uiCulture="bg-BG" culture="bg-BG"></globalization>
In Controller:
protected override void Initialize(RequestContext requestContext)
{
Thread.CurrentThread.CurrentCulture =
Thread.CurrentThread.CurrentUICulture =
new CultureInfo(requestContext.HttpContext.Request.UserLanguages[0]);
base.Initialize(requestContext);
}
In View:
#{
var culture = System.Globalization.CultureInfo.CurrentCulture.ToString();
}
<script src="#Url.Content("~/scripts/cultures/kendo.culture." + culture + ".min.js")"></script>
<script>
kendo.culture("#culture");
</script>
Any suggestions?
Thanks a lot!
The issue is not related to matching cultures on the Server and on the Client. As to my observations, you have correctly followed the article.
The reason why the model view is invalid is that you have set the Format method of the Column to a format that is like "dd-MMM-yyyy". Try setting the format as:
columns.Bound(c => c.PaymentDate).Format("{0:dd/mm/yyyy}");
Alternatively, you can remove the formatting for testing purposes and check if the date is going to be passed correctly. You can put it back later on.

Thread.Culture not being respected for model-binding in MVC2

To allow for culture-specific values to be bound correctly I have this ModelBinder:
public class CultureAwareModelBinder : DefaultModelBinder {
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
BaseController controller = (BaseController)controllerContext.Controller;
CultureInfo culture = controller.Settings.Culture;
CultureInfo language = controller.Settings.Language;
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = language;
return base.BindModel(controllerContext, bindingContext);
}
}
(BaseController.Settings is a property that exposes the correct CultureInfo for the current application's user).
I set it up like so
protected void Application_Start() {
ModelBinders.Binders.DefaultBinder = new CultureAwareModelBinder();
}
When I debug and step-through my code, the Thread.Culture is being set correctly, however my model keeps getting the wrong values.
This is my model:
public class EventModel {
public DateTime Start { get; set; }
public DateTime End { get; set; }
}
When I specify "10/6/2013" for either field in my web-browser and hit submit, and when the culture is "en-GB" (and I checked the thread's DateTimeFormat is indeed set to dd/MM/yyyy), MVC receives it as the 6th October 2013, not the 10th June 2013.
I don't know why this is happening, and unfortunately I can't source-step into the actual model-binding. Why is it not respecting the thread culture?
It's way too late to set the current culture in the model binder. This should be done much earlier in the execution pipeline. For example in the Application_BeginRequest event in your Global.asax.
I faced the same issue. My solution was to use the DefaultModelBinder however instead of using an ActionFilter to set the desired culture I used a IAuthorizationFilter which has the same effect, however is executed before model binding unlike the 'ActionFilter' which is executed after model binding.
I appreciate that it is slightly inelegant/unorthodox use of the IAuthorizationFilter however it did the trick.

DisplayFormat for TextBoxFor in MVC

I need to round off 4 digit decimal to 2 digits and show in MVC 3 UI
Something like this 58.8964 to 58.90
Tried following this How should I use EditorFor() in MVC for a currency/money type? but not working.
As i am using TextBoxFor=> i removed ApplyFormatInEditMode here. Even
i tried with ApplyFormatInEditMode , but nothing works. Still showing
me 58.8964.
MyModelClass
[DisplayFormat(DataFormatString = "{0:F2}")]
public decimal? TotalAmount { get; set; }
#Html.TextBoxFor(m=>m.TotalAmount)
How can i achieve this round off?
I can't use EditorFor(m=>m.TotalAmount) here, as i need to pass some htmlAttributes
Edit:
After debugging with MVC source code, they internally use
string valueParameter = Convert.ToString(value, CultureInfo.CurrentCulture);
in MvcHtmlString InputHelper() method of InputExtension.cs that takes object value as parameter and converting. They are not using any display format there. How could we fix?
I managed to fix in this way. As i have a custom helper, i can able to manage with the below code
if (!string.IsNullOrEmpty(modelMetaData.DisplayFormatString))
{
string formatString = modelMetaData.DisplayFormatString;
string formattedValue = String.Format(CultureInfo.CurrentCulture, formatString, modelMetaData.Model);
string name = ExpressionHelper.GetExpressionText(expression);
string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
return htmlHelper.TextBox(fullName, formattedValue, htmlAttributes);
}
else
{
return htmlHelper.TextBoxFor(expression, htmlAttributes);
}
This works in MVC5
#Html.TextBoxFor(m => m.TotalAmount, "{0:0.00}")
You should use Html.EditorFor instead of Html.TextBoxFor if you want the custom format to be taken into account:
#Html.EditorFor(m => m.TotalAmount)
Also make sure that you have set ApplyFormatInEditMode to true:
[DisplayFormat(DataFormatString = "{0:F2}", ApplyFormatInEditMode = true)]
public decimal? TotalAmount { get; set; }
The DisplayFormat attribute is intended to be used only with templated helpers such as EditorFor and DisplayFor. This is the recommended approach instead of using TextBoxFor.
Try like this:
#{
var format = String.Format("{0:0.00}", Model.TotalAmount);
}
#Html.TextBoxFor(m => m.TotalAmount, format)
Hope it helps.
If you need more control of the field being displayed (vs. a built in EditorFor template) create a custom EditorFor template yourself. Inside an EditorFor template, the built in Html Helpers like #Html.TextBox() can be used with automatic client side validation and Display attributes which are usually only available to EditorFor and DisplayFor.
For example looping through a List of items. The input name has to have an unbroken index.
// Optional, if you require a custom input name for binding
String fieldName = String.Format("FieldNameForBinding[{0}].Property", index)
#Html.EditorFor(m => m.Property, "MyCustomEditorTemplate", fieldName)
Then you can setup your model
[CustomValidation(..optional..)]
[DisplayFormat(DataFormatString = "{0:F2}", ApplyFormatInEditMode = true)]
public decimal? TotalAmount { get; set; }
The EditorFor template (in e.g. ~/Views/Shared/EditorFor/MyCustomEditorTemplate.cshtml)
Note the name is left empty, it comes from the fieldName automatically. You can see it in the ViewData.TemplateInfo.HtmlFieldPrefix. Now you have complete control over the display of the field.
#model object
#{
Decimal? actualValue = (Decimal?)Model;
}
// The TextBox and ValidationMessage "names" are empty, they are filled
// from the htmlFieldName given via the #Html.EditorFor() call.
#Html.TextBox("", actualValue, new { #class = "cssClass" })
#Html.ValidationMessage("")
The idea is that you can customize the input field however you would like, and use e.g. #Html.TextBox() which outside of a custom EditorFor template would not utilize the built in client-side validation. You don't need to use the custom naming of the input field, that was simply an example of the usefulness of this solution. You can customize the way the data is presented (CSS, etc.) instead of relying on the built in EditorFor templates.
I solve that issue in this way:
#Html.TextBoxFor(model => model.dtArrivalDate, ModelMetadata.FromLambdaExpression(model => model.dtArrivalDate, ViewData).EditFormatString)
or create next extension:
public static class HtmlExtensions
{
public static MvcHtmlString TextBoxWithFormatFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
{
return htmlHelper.TextBoxFor(expression, ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).EditFormatString, htmlAttributes);
}
}
But you need to set ApplyFormatInEditMode=true in DisplayFormatAttribute on your field.

ASP.NET Web API Model Binding

I'm using Web API within ASP .NET MVC 4 RC, and I have a method that takes a complex object with nullable DateTime properties. I want the values of the input to be read from the query string, so I have something like this:
public class MyCriteria
{
public int? ID { get; set; }
public DateTime? Date { get; set; }
}
[HttpGet]
public IEnumerable<MyResult> Search([FromUri]MyCriteria criteria)
{
// Do stuff here.
}
This works well if I pass a standard date format in the query string such as 01/15/2012:
http://mysite/Search?ID=1&Date=01/15/2012
However, I want to specify a custom format for the DateTime (maybe MMddyyyy)... for example:
http://mysite/Search?ID=1&Date=01152012
Edit:
I've tried to apply a custom model binder, but I haven't had any luck applying it to only DateTime objects. The ModelBinderProvider I've tried looks something like this:
public class DateTimeModelBinderProvider : ModelBinderProvider
{
public override IModelBinder GetBinder(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType == typeof(DateTime) || bindingContext.ModelType == typeof(DateTime?))
{
return new DateTimeModelBinder();
}
return null;
}
}
// In the Global.asax
GlobalConfiguration.Configuration.Services.Add(typeof(ModelBinderProvider), new DateTimeModelBinderProvider());
The new model binder provider is created, but GetBinder is only called once (for the complex model parameter, but not for each property within the model). This makes sense, but I would like to find a way to make it to use my DateTimeModelBinder for DateTime properties, while using the default binding for non-DateTime properties. Is there a way to override the default ModelBinder and specify how each property is bound?
Thanks!!!
Consider setting your view-model's Date property to type string
Then either write a utility function to handle the mapping between the viewmodel type and the domain-model type:
public static MyCriteria MapMyCriteriaViewModelToDomain(MyCriteriaViewModel model){
var date = Convert.ToDateTime(model.Date.Substring(0,2) + "/" model.Date.Substring(2,2) + "/" model.Date.Substring(4,2));
return new MyCriteria
{
ID = model.ID,
Date = date
};
}
or use a tool like AutoMapper, like this:
in Global.asax
//if passed as MMDDYYYY:
Mapper.CreateMap<MyCriteriaViewModel, MyCriteria>().
.ForMember(
dest => dest.Date,
opt => opt.MapFrom(src => Convert.ToDateTime(src.Date.Substring(0,2) + "/" src.Date.Substring(2,2) + "/" src.Date.Substring(4,2)))
);
and in the controller:
public ActionResult MyAction(MyCriteriaViewModel model)
{
var myCriteria = Mapper.Map<MyCriteriaViewModel, MyCriteria>(model);
// etc.
}
From this example it might not seem that AutoMapper is providing any added value. It's value comes when you are configuring several or many mappings with objects that generally have more properties than this example. CreateMap will automatically map properties with the same name and type, so it saves lots of typing and it's much DRYer.

Format datetime in asp.net mvc 4

How can I force the format of datetime in asp.net mvc 4 ?
In display mode it shows as I want but in edit model it doesn't.
I am using displayfor and editorfor and applyformatineditmode=true with dataformatstring="{0:dd/MM/yyyy}"
What I have tried:
globalization in web.config (both of them) with my culture and uiculture.
modifying the culture and uiculture in application_start()
custom modelbinder for datetime
I have no idea how to force it and I need to input the date as dd/MM/yyyy not the default.
MORE INFO:
my viewmodel is like this
[DisplayName("date of birth")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime? Birth { get; set; }
in view I use #Html.DisplayFor(m=>m.Birth) but this works as expected (I see the formatting)
and to input the date I use #Html.EditorFor(m=>m.Birth) but if I try and input something like 13/12/2000 is fails with the error that it is not a valid date (12/13/2000 and 2000/12/13 are working as expected but I need dd/MM/yyyy).
The custom modelbinder is called in application_start() b/c I don't know where else.
Using <globalization/> I have tried with culture="ro-RO", uiCulture="ro" and other cultures that would give me dd/MM/yyyy.
I have also tried to set it on a per thread basis in application_start() (there are a lot of examples here, on how to do this)
For all that will read this question:
It seems that Darin Dimitrov's answer will work as long as I don't have client validation.
Another approach is to use custom validation including client side validation.
I'm glad I found this out before recreating the entire application.
Ahhhh, now it is clear. You seem to have problems binding back the value. Not with displaying it on the view. Indeed, that's the fault of the default model binder. You could write and use a custom one that will take into consideration the [DisplayFormat] attribute on your model. I have illustrated such a custom model binder here: https://stackoverflow.com/a/7836093/29407
Apparently some problems still persist. Here's my full setup working perfectly fine on both ASP.NET MVC 3 & 4 RC.
Model:
public class MyViewModel
{
[DisplayName("date of birth")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime? Birth { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel
{
Birth = DateTime.Now
});
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
View:
#model MyViewModel
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.Birth)
#Html.EditorFor(x => x.Birth)
#Html.ValidationMessageFor(x => x.Birth)
<button type="submit">OK</button>
}
Registration of the custom model binder in Application_Start:
ModelBinders.Binders.Add(typeof(DateTime?), new MyDateTimeModelBinder());
And the custom model binder itself:
public class MyDateTimeModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var displayFormat = bindingContext.ModelMetadata.DisplayFormatString;
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (!string.IsNullOrEmpty(displayFormat) && value != null)
{
DateTime date;
displayFormat = displayFormat.Replace("{0:", string.Empty).Replace("}", string.Empty);
// use the format specified in the DisplayFormat attribute to parse the date
if (DateTime.TryParseExact(value.AttemptedValue, displayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out date))
{
return date;
}
else
{
bindingContext.ModelState.AddModelError(
bindingContext.ModelName,
string.Format("{0} is an invalid date format", value.AttemptedValue)
);
}
}
return base.BindModel(controllerContext, bindingContext);
}
}
Now, no matter what culture you have setup in your web.config (<globalization> element) or the current thread culture, the custom model binder will use the DisplayFormat attribute's date format when parsing nullable dates.
Client validation issues can occur because of MVC bug (even in MVC 5) in jquery.validate.unobtrusive.min.js which does not accept date/datetime format in any way. Unfortunately you have to solve it manually.
My finally working solution:
$(function () {
$.validator.methods.date = function (value, element) {
return this.optional(element) || moment(value, "DD.MM.YYYY", true).isValid();
}
});
You have to include before:
#Scripts.Render("~/Scripts/jquery-3.1.1.js")
#Scripts.Render("~/Scripts/jquery.validate.min.js")
#Scripts.Render("~/Scripts/jquery.validate.unobtrusive.min.js")
#Scripts.Render("~/Scripts/moment.js")
You can install moment.js using:
Install-Package Moment.js
Thanks Darin,
For me, to be able to post to the create method, It only worked after I modified the BindModel code to :
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var displayFormat = bindingContext.ModelMetadata.DisplayFormatString;
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (!string.IsNullOrEmpty(displayFormat) && value != null)
{
DateTime date;
displayFormat = displayFormat.Replace("{0:", string.Empty).Replace("}", string.Empty);
// use the format specified in the DisplayFormat attribute to parse the date
if (DateTime.TryParse(value.AttemptedValue, CultureInfo.GetCultureInfo("en-GB"), DateTimeStyles.None, out date))
{
return date;
}
else
{
bindingContext.ModelState.AddModelError(
bindingContext.ModelName,
string.Format("{0} is an invalid date format", value.AttemptedValue)
);
}
}
return base.BindModel(controllerContext, bindingContext);
}
Hope this could help someone else...

Resources