私はまだ ASP.NET と MVC にかなり慣れていないため、何日にもわたるグーグル検索と実験にもかかわらず、この問題を解決する最善の方法について空白を描いています。
EmailAddressAttribute と同様に機能するように、BirthdayAttribute を作成しました。birthday 属性は、3 つのドロップダウン リストを持つエディター テンプレートを使用して誕生日 DateTime がレンダリングされるように UI ヒントを設定します。この属性を使用して、年のドロップダウンに表示する年数を示す追加のメタ データを設定することもできます。
jQuery の日付ピッカーを使用できることはわかっていますが、誕生日の場合、3 つのドロップダウンの方がはるかに使いやすいことがわかりました。
@model DateTime
@using System;
@using System.Web.Mvc;
@{
UInt16 numberOfVisibleYears = 100;
if (ViewData.ModelMetadata.AdditionalValues.ContainsKey("NumberOfVisibleYears"))
{
numberOfVisibleYears = Convert.ToUInt16(ViewData.ModelMetadata.AdditionalValues["NumberOfVisibleYears"]);
}
var now = DateTime.Now;
var years = Enumerable.Range(0, numberOfVisibleYears).Select(x => new SelectListItem { Value = (now.Year - x).ToString(), Text = (now.Year - x).ToString() });
var months = Enumerable.Range(1, 12).Select(x => new SelectListItem{ Text = new DateTime( now.Year, x, 1).ToString("MMMM"), Value = x.ToString() });
var days = Enumerable.Range(1, 31).Select(x => new SelectListItem { Value = x.ToString("00"), Text = x.ToString() });
}
@Html.DropDownList("Year", years, "<Year>") /
@Html.DropDownList("Month", months, "<Month>") /
@Html.DropDownList("Day", days, "<Day>")
後で日付を再構築するための ModelBinder もあります。簡潔にするためにヘルパー関数の内容を削除しましたが、ここまではすべてうまく機能しています。通常の有効な日付は、メンバーの作成または編集に問題なく機能します。
public class DateSelector_DropdownListBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (controllerContext == null)
throw new ArgumentNullException("controllerContext");
if (bindingContext == null)
throw new ArgumentNullException("bindingContext");
if (IsDropdownListBound(bindingContext))
{
int year = GetData(bindingContext, "Year");
int month = GetData(bindingContext, "Month");
int day = GetData(bindingContext, "Day");
DateTime result;
if (!DateTime.TryParse(string.Format("{0}/{1}/{2}", year, month, day), out result))
{
//TODO: SOMETHING MORE USEFUL???
bindingContext.ModelState.AddModelError("", string.Format("Not a valid date."));
}
return result;
}
else
{
return base.BindModel(controllerContext, bindingContext);
}
}
private int GetData(ModelBindingContext bindingContext, string propertyName)
{
// parse the int using the correct value provider
}
private bool IsDropdownListBound(ModelBindingContext bindingContext)
{
//check model meta data UI hint for above editor template
}
}
今見てみると、おそらくnull可能なDateTimeを使用しているはずですが、それはここにもありません。
私が抱えている問題は、2 月 30 日や 9 月 31 日などの無効な日付の非常に基本的な検証にあります。検証自体はうまく機能しますが、無効な日付は保存されず、フォームがリロードされたときに保持されません。
私が望むのは、ドロップダウンをデフォルト値にリセットするのではなく、2 月 30 日の無効な日付を記憶し、検証メッセージとともに再表示することです。電子メール アドレス (EmailAddressAttribute で装飾されている) などのその他のフィールドは、無効なエントリを問題なく保持します。
現時点では、サーバー側の検証を機能させようとしています。正直なところ、クライアント側の検証についてはまだ考え始めていません。
この問題を解決するために javascript と ajax でできることがたくさんあることはわかっていますが、代わりに適切なサーバー側の検証を行うことをお勧めします。