在ASP.NET MVC 4中格式化日期时间

57
如何在ASP.NET MVC 4中强制设置日期时间格式? 在显示模式下,它显示为我想要的格式,但在编辑模式下却没有。 我正在使用displayfor和editorfor,并将applyformatineditmode=true与dataformatstring="{0:dd/MM/yyyy}"一起使用。 我尝试过以下方法:
- 在web.config中的全局化设置(两个)中使用我的culture和uiculture。 - 修改application_start()中的culture和uiculture。 - 为datetime自定义模型绑定器。
我不知道如何强制它,我需要以dd/MM/yyyy的格式输入日期,而不是默认格式。
更多信息: 我的视图模型是这样的。
    [DisplayName("date of birth")]
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
    public DateTime? Birth { get; set; }

在视图中,我使用 @Html.DisplayFor(m=>m.Birth),它按预期工作(我能看到格式化后的日期)。 而在输入日期时,我使用 @Html.EditorFor(m=>m.Birth),但是如果我尝试输入像 13/12/2000 这样的日期,就会出现“不是有效日期”的错误(12/13/2000 和 2000/12/13 按预期工作,但我需要 dd/MM/yyyy 格式)。

我在 application_start() 中调用自定义模型绑定器,因为我不知道其他地方该怎么做。

使用 <globalization/> ,我已经尝试了 culture="ro-RO", uiCulture="ro" 和其他可以给出 dd/MM/yyyy 格式的区域设置。 我还尝试在 application_start() 中为每个线程设置区域设置(这里有很多示例说明如何实现)。


对于所有阅读此问题的人: 似乎 Darin Dimitrov 的答案只适用于没有客户端验证的情况。 另一个方法是使用自定义验证,包括客户端验证。 我很高兴在重新创建整个应用程序之前找到了这个解决方法。


请问您能否提供更多信息?您的模型、控制器和视图是什么?同时,请提供一些关于显示模板和编辑模板不同输出的示例。另外请注意,文化是按线程设置的。您提到了Application_Start,但这只在应用程序启动时执行一次。那么对于后续的请求呢?您是如何为它们设置文化的? - Darin Dimitrov
application_start 只会被执行一次!请使用 application_beginRequest 代替! - Nas
Nas,application_beginRequest应该在哪里?我只在Global中看到application_start。在MVC 4中,情况开始有了一些不同,与MVC 3不同。 - amb
3个回答

103

啊啊啊,现在清楚了。你似乎有问题将该值绑定回来,而不是在视图上显示它。确实,这是默认模型绑定器的问题。你可以编写并使用一个自定义绑定器,该绑定器将考虑你的模型上的[DisplayFormat]属性。我在这里演示了这样一个自定义模型绑定器:https://dev59.com/ymsz5IYBdhLWcg3wiISe#7836093


显然还存在一些问题。下面是我的完整设置,在 ASP.NET MVC 3 和 4 RC 上都能完美工作。

模型:

public class MyViewModel
{
    [DisplayName("date of birth")]
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
    public DateTime? Birth { get; set; }
}

控制器:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View(new MyViewModel
        {
            Birth = DateTime.Now
        });
    }

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

查看:

@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>
}

Application_Start 中注册自定义模型绑定器:

ModelBinders.Binders.Add(typeof(DateTime?), new MyDateTimeModelBinder());

还有自定义的模型绑定程序本身:

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);
    }
}

现在,无论你在<globalization>元素中设置了什么文化或当前线程的文化是什么,自定义模型绑定器都将在解析可空日期时使用DisplayFormat属性的日期格式。


1
这是我使用过的,但没有成功。我已经在datetime和datetime?中注册了它,它甚至没有显示我的错误消息,所以我认为它没有被使用。 - amb
1
那么我猜你将不得不展示你的完整代码,让我们能够重现这个问题,因为我正在使用这个模型绑定器而没有任何问题。 - Darin Dimitrov
完美,没有更多的英国日期被解释为MVC 5.2.2中jQuery UI日期选择器的美国日期。 - Richard
我有一个问题。它能同时适用于 DateTimeDatTime? 类型吗? - Celdor
这个解决方案是否适用于jQuery日期选择器?...以及在哪里添加这个自定义模型绑定器。 - Ibrahim Amer
显示剩余4条评论

1

客户端验证问题可能是由MVC中的bug(即使在MVC 5中也存在)在jquery.validate.unobtrusive.min.js中引起的,该文件无论如何都不接受日期/时间格式。不幸的是,您必须手动解决它。

我的最终解决方案:

$(function () {
    $.validator.methods.date = function (value, element) {
        return this.optional(element) || moment(value, "DD.MM.YYYY", true).isValid();
    }
});

你需要在之前包含:

@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")

您可以使用以下方式安装moment.js:

Install-Package Moment.js

0
感谢Darin, 对于我来说,只有在我修改了BindModel代码后才能将其发布到create方法中:
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);
}

希望这能帮助到其他人...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接