web-dev-qa-db-ja.com

asp.net mvc 4でdatetimeをフォーマットする

Asp.net mvc 4でdatetimeの形式を強制するにはどうすればよいですか?表示モードでは必要に応じて表示されますが、編集モデルでは表示されません。 displayforとeditorforを使用し、dataformatstring = "{0:dd/MM/yyyy}"でapplyformatineditmode = trueを使用しています。

  • 私の文化とuicultureを使用したweb.config(両方とも)のグローバリゼーション。
  • application_start()でカルチャとUicultureを変更する
  • 日時用のカスタムmodelbinder

強制する方法がわかりません。日付をデフォルトではなくdd/MM/yyyyとして入力する必要があります。

詳細:私のviewmodelはこのようなものです

    [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のようなものを入力しようとするとエラーで失敗します有効な日付ではないこと(2000年12月13日と2000年12月13日は期待どおりに機能していますが、dd/MM/yyyyが必要です)。

カスタムmodelbinderはapplication_start()で呼び出されますb/c他にどこにあるかわかりません。

<globalization/>を使用して、culture="ro-RO", uiCulture="ro"およびdd/MM/yyyyを提供するその他のカルチャで試しました。 application_start()でスレッドごとに設定しようとしました(これを行う方法については、ここに多くの例があります)


この質問はすべて読んでください:クライアントの検証がない限り、Darin Dimitrovの答えは機能すると思われます。別のアプローチは、クライアント側の検証を含むカスタム検証を使用することです。アプリケーション全体を再作成する前にこれを見つけてうれしいです。

56
amb

ああ、今では明らかです。値のバインドに問題があるようです。ビューに表示するのではありません。確かに、それはデフォルトのモデルバインダーの欠点です。モデルの[DisplayFormat]属性を考慮に入れたカスタムのものを作成して使用できます。このようなカスタムモデルバインダーをここに示しました: https://stackoverflow.com/a/7836093/29407


どうやらまだいくつかの問題が残っています。 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);
    }
}

これで、web.config(<globalization>要素)または現在のスレッドカルチャで設定したカルチャに関係なく、カスタムモデルバインダーはDisplayFormat属性の日付形式を使用して、null許容日付を解析します。

103
Darin Dimitrov

クライアント検証の問題は、jquery.validate.unobtrusive.min.jsのMVCバグ(MVC 5でも)が原因で発生する可能性がありますしない任意の方法で日付/日付時刻形式を受け入れます。残念ながら、手動で解決する必要があります。

私の最終的に機能するソリューション:

$(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
1
lukyer

Darinに感謝します。私にとって、createメソッドに投稿できるようになったのは、BindModelコードを次のように変更した後でのみ機能しました。

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

これが他の誰かに役立つことを願っています...

0