Enum からドロップダウンリストを生成する
前回 検証付きドロップダウンリストを実装する では、ドロップダウンリストの実装方法について書きました。その続きになります。
現在 ASP.NET MVC 5 には、Enum からドロップダウンリストを生成してくれる HTML ヘルパーがありません。
(メタ情報を見て適切な Html ヘルパーを選択してくれる @Html.EditorFor でも対応していません。)
なので、Enum からドロップダウンリストを生成するには、以下の方法が挙げられます。
- 1. 自分で SelectList を組み立てる
- 2. 自分で Enum から SelectList を組み立てるヘルパーを作り、それを使う
ASP.NET MVC 5.1 !
が、しばやんさんの記事 ASP.NET MVC 5.1 の新機能を Nightly Build で試してみた によると、ASP.NET MVC 5.1 より Enum からドロップダウンリストを生成する HTML ヘルパーが搭載されるそうです。
→ (追記) ASP.NET MVC 5.1 がリリースされ、@Html.EnumDropDownListFor() でできるようになりました。
ASP.NET MVC 5.1 と ASP.NET Web API 2.1 の内容 - miso_soup3
ASP.NET MVC 5.1 がリリースされるまでは、先ほどの 1 か 2 で実装しなければなりません。
が、せっかくのオープンソースなので、2 の方法は ASP.NET MVC 5.1 に搭載されるという新しい HTML ヘルパーを拝借(コピペ)して実装してみます。
ということで、今回の記事は Enum からドロップダウンリストを生成する方法2つの紹介です。
1. 自分で SelectList を組み立てる
Enum のコード:
public enum Color { Red = 0, Yellow = 1, Green = 2 }
コントローラーからビューへ渡すモデルクラス:
{ public class CreatePersonModel { [Required(ErrorMessage = "{0} が必要です。")] [Display(Name = "名前")] public string Name { get; set; } [Required(ErrorMessage = "{0} を選択して下さい。")] [Display(Name = "好きな色")] public Color? FavoriteColor { get; set; } /// <summary> /// 色選択リスト /// </summary> public IEnumerable<SelectListItem> ColorSelectList { get; set; } }
前回のコードとは違い、プロパティの型が IEnumerable<SelectListItem> と Enum の Color に変更になっています。
コントローラーのコード:
public class PersonController : Controller { // GET: /Person/Create [HttpGet] public ActionResult Create() { //色のコレクションを取得します var items = new List<SelectListItem>() { new SelectListItem() { Value = Color.Red.ToString(), Text = "あか" }, new SelectListItem() { Value = Color.Yellow.ToString(), Text = "きいろ" }, new SelectListItem() { Value = Color.Green.ToString(), Text = "みどり" }, }; var model = new CreatePersonModel(); model.ColorSelectList = CreateColorSelectList(); return View(model); } //色を選択するためのセレクトリストオブジェクトを生成します private IEnumerable<SelectListItem> CreateColorSelectList() { return new List<SelectListItem>() { new SelectListItem() { Value = Color.Red.ToString(), Text = "あか" }, new SelectListItem() { Value = Color.Yellow.ToString(), Text = "きいろ" }, new SelectListItem() { Value = Color.Green.ToString(), Text = "みどり" }, }; } //...
ビューのドロップダウンリストを表示している部分:
<div class="form-group"> @Html.LabelFor(model => model.FavoriteColor, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.DropDownListFor(model => model.FavoriteColor, Model.ColorSelectList, "選択して下さい。", new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.FavoriteColor) </div> </div>
コードは以上です。
コントローラークラスの CreateColorSelectList() メソッドで、自分で SelectList を組み立てています。項目の表示名もここで指定します。
return new List<SelectListItem>() { new SelectListItem() { Value = Color.Red.ToString(), Text = "あか" }, new SelectListItem() { Value = Color.Yellow.ToString(), Text = "きいろ" }, new SelectListItem() { Value = Color.Green.ToString(), Text = "みどり" }, };
この方法はとても簡単ですが、各 Enum 毎に SelectList を組み立てなければいけないので若干面倒ではあります。(でも十分な実装方法だと思います。)
2. 自分で Enum から SelectList を組み立てるヘルパーを作り、それを使う
(※ASP.NET MVC 5.1 がリリースされたら、この方法ではなくリリースされた HTML ヘルパーを使うことを推奨します。)
この方法では、Enum の型情報を引数に IEnumerable<SelectList> を生成するヘルパーを作ります。
そのヘルパーは、冒頭で述べた通り、ASP.NET MVC のソースから拝借します。
Enum のコード:
public enum Color { [Display(Name="あか")] Red = 0, [Display(Name = "き")] Yellow = 1, [Display(Name = "みどり")] Green = 2 }
Enum では表示名を Display 属性で指定します。(詳細は ASP.NET MVC 5.1 の新機能を Nightly Build で試してみた をご覧ください。)
Enum から IEnumerable<SelectList> を生成するヘルパークラス:
aspnetwebstack / src / System.Web.Mvc / Html / EnumHelper.cs をコピペしました。(一部修正あり)
using System; using System.Collections.Generic; using System.Web.Mvc; using System.Reflection; using System.ComponentModel.DataAnnotations; namespace WebApplication5.Models { public static class MyHelper { public static IList<SelectListItem> GetSelectList(Type type) { if (type == null) { throw new ArgumentNullException("type"); } if (!IsValidForEnumHelper(type)) { throw new ArgumentException(String.Format("{0} の型はサポートされていません", type.FullName), "type"); } IList<SelectListItem> selectList = new List<SelectListItem>(); // According to HTML5: "The first child option element of a select element with a required attribute and // without a multiple attribute, and whose size is "1", must have either an empty value attribute, or must // have no text content." SelectExtensions.DropDownList[For]() methods often generate a matching // <select/>. Empty value for Nullable<T>, empty text for round-tripping an unrecognized value, or option // label serves in some cases. But otherwise, ignoring this does not cause problems in either IE or Chrome. Type checkedType = Nullable.GetUnderlyingType(type) ?? type; if (checkedType != type) { // Underlying type was non-null so handle Nullable<T>; ensure returned list has a spot for null selectList.Add(new SelectListItem { Text = String.Empty, Value = String.Empty, }); } // Populate the list const BindingFlags BindingFlags = BindingFlags.DeclaredOnly | BindingFlags.GetField | BindingFlags.Public | BindingFlags.Static; foreach (FieldInfo field in checkedType.GetFields(BindingFlags)) { // fieldValue will be an numeric type (byte, ...) object fieldValue = field.GetRawConstantValue(); selectList.Add(new SelectListItem { Text = GetDisplayName(field), Value = fieldValue.ToString(), }); } return selectList; } public static bool IsValidForEnumHelper(Type type) { bool isValid = false; if (type != null) { // Type.IsEnum is false for Nullable<T> even if T is an enum. Check underlying type (if any). // Do not support Enum type itself -- IsEnum property is false for that class. Type checkedType = Nullable.GetUnderlyingType(type) ?? type; if (checkedType.IsEnum) { isValid = !HasFlagsInternal(checkedType); } } return isValid; } private static bool HasFlagsInternal(Type type) { if (type == null) throw new ArgumentNullException("type"); FlagsAttribute attribute = type.GetCustomAttribute<FlagsAttribute>(inherit: false); return attribute != null; } public static bool IsValidForEnumHelper(ModelMetadata metadata) { return metadata != null && IsValidForEnumHelper(metadata.ModelType); } private static string GetDisplayName(FieldInfo field) { DisplayAttribute display = field.GetCustomAttribute<DisplayAttribute>(inherit: false); if (display != null) { string name = display.GetName(); if (!String.IsNullOrEmpty(name)) { return name; } } return field.Name; } } }
コントローラーのコード:
public class PersonController : Controller { // GET: /Person/Create [HttpGet] public ActionResult Create() { var model = new CreatePersonModel(); model.ColorSelectList = MyHelper.GetSelectList(typeof(Color)); return View(model); } //...
コードは以上です。ビューへ渡すモデルクラスと、ビューのコードは 1 と同じです。
コントローラークラスで、ヘルパーを呼び出して IEnumerable<SelectListItem>をモデルクラスにセットしています。
おしまい
2 の方法は、いろいろな実装方法が存在します。
- SelectList のおさらいと Enum から SelectList を作成
- How do you create a dropdownlist from an enum in ASP.NET MVC?
ASP.NET MVC 5.1 がリリースされたら、是非新しい Enum の HTML ヘルパーを使いたいですね!
バージョン 5 未満を使用していても、オープンソースなので参考にできます。