2017-02-02

有 EnumDropDownListFor 為什麼還要客製 Enum to Dropdownlist ?!

enum 可以讓資料儲存更有效率(string --> int), 擺脫字串比對時的不便(e.g. 手誤..),也可以有效降低無用資料進入資料庫的機會,ASP.NET MVC 5 也提供了 EnumDropDownListFor 的 HTML helper,那為什麼還有這篇文章呢? 就讓我細說分明

EnumDropDownListFor 不足之處

  1. 預設使用 Value 當做下拉選單選項文字
  • enum 的值無法有空白字元,移除空白字元後意思就不同了
  1. 無法自訂 html name
  • name 是 model binding 的基礎,在需要 post list 時,無法自訂 name 會造成程式複雜度提高

如何解決

大家可能都有不同想法,以下是我自己常用的方式,提供給大家參考,如果大家有更好的方式請教我幾招

  • 原始 enum
    public enum RegionEnum
    {
        Default,
        ROC,
        SouthKorea,
        USA,
        UK,
        Japan
    }
    
  • 前置作業
  1. 為 enum 加上正確顯示名稱

    以下兩種方法擇一即可

    • 1-1. Description("showName")
      • using System.ComponentModel;
        public enum RegionEnum
        {
            Default,
            [Description("Description:R.O.C")]
            ROC,
            [Description("Description:South Korea")]
            SouthKorea,
            [Description("Description:U.S.A")]
            USA,
            [Description("Description:U.K.")]
            UK,
            Japan
        }
        
    • 1-2. Display(Name="showName")
      • using System.ComponentModel.DataAnnotations;
        public enum RegionEnum
            {
                Default,
                [Display(Name= "DisplayName:R.O.C")]
                ROC,
                [Display(Name = "DisplayName:South Korea")]
                SouthKorea,
                [Display(Name = "DisplayName:U.S.A")]
                USA,
                [Display(Name = "DisplayName:U.K.")]
                UK,
                Japan
            }
        
  2. 加上取得正確名稱的 helper
    • Description("showName") 使用 GetEnumDescription
    • Display(Name="showName") 則使用 GetEnumDisplayName
      public static class CustomerEnumHelper
      {
          /// <summary>
          /// 取得 Enum 的 Description
          /// </summary>
          /// <param name="value">Enum</param>
          /// <returns>Enum 的 Description</returns>
          public static string GetEnumDescription(System.Enum value)
          {
              FieldInfo field = value.GetType().GetField(value.ToString());
      
              DescriptionAttribute customAttribute = field.GetCustomAttribute<DescriptionAttribute>(false);
      
              if (customAttribute != null)
              {
                  string name = string.IsNullOrWhiteSpace(customAttribute.Description) ? string.Empty : customAttribute.Description;
                  if (!string.IsNullOrEmpty(name))
                      return name;
              }
              return value.ToString();
          }
          /// <summary>
          /// 取得 Enum 的 DisplayName
          /// </summary>
          /// <param name="value">Enum</param>
          /// <returns>Enum 的 DisplayName</returns>
          public static string GetEnumDisplayName(System.Enum value)
          {
              FieldInfo field = value.GetType().GetField(value.ToString());
      
              DisplayAttribute customAttribute = field.GetCustomAttribute<DisplayAttribute>(false);
              if (customAttribute != null)
              {
                  string name = string.IsNullOrWhiteSpace(customAttribute.GetName()) ? string.Empty : customAttribute.GetName();
                  if (!string.IsNullOrEmpty(name))
                      return name;
              }
              return value.ToString();
          }
      }
      
  3. 自行組裝 SelectList
    • Controller(請記得選用正確方法)
      IList<SelectListItem> list = Enum.GetValues(typeof(RegionEnum))
                              .Cast<RegionEnum>()
                              .Select(x => new SelectListItem
                              {
                                  Text = CustomerEnumHelper.GetEnumDisplayName(x),//CustomerEnumHelper.GetEnumDescription(x) ,
                                  Value = ((int)x).ToString()
                              })
                              .ToList();
          ViewBag.list = list;
      
    • View
       @{
          var list= ViewBag.list as List<SelectListItem>;
        }
      @Html.DropDownList("a", list)
      
    • 效果

      1selectlist

  4. 前端 html 組裝
    • 需注意用量,避免影響效能問題
    • View (請記得選用正確方法)
       <select name="a">
      @foreach (RegionEnum enumItem in Enum.GetValues(typeof(RegionEnum)))
      {
          <option @((enumItem == RegionEnum.Default) ? "selected='selected'" : String.Empty) value="@(enumItem)">@CustomerEnumHelper.GetEnumDisplayName(enumItem)</option>
      }
      </select>
      
    • 效果

      2view

  5. 擴充 html helper
      public static MvcHtmlString EnumDropDownList(this HtmlHelper htmlHelper, string name, Type enumType, string defaultValue = "")
        {
            IList<SelectListItem> selectList = GetSelectList(enumType, defaultValue);
            return SelectExtensions.DropDownList(htmlHelper, name, selectList, defaultValue);
    
        }
      public static IList<SelectListItem> GetSelectList(Type type, string defaultValue)
        {
            IList<SelectListItem> selectList = GetSelectList(type);
            if (selectList.Count != 0 && string.IsNullOrEmpty(defaultValue))
            {
                selectList[0].Selected = true;
            }
            else
            {
                for (int index = selectList.Count - 1; index >= 0; --index)
                {
                    SelectListItem selectListItem = selectList[index];
                    selectListItem.Selected = selectListItem.Text == defaultValue;
                }
            }
            return selectList;
        }
       public static IList<SelectListItem> GetSelectList(Type type)
        {
            IList<SelectListItem> selectListItemList = (IList<SelectListItem>)new List<SelectListItem>();
            Type type1 = Nullable.GetUnderlyingType(type);
            if ((object)type1 == null)
                type1 = type;
            Type type2 = type1;
            if (type2 != type)
                selectListItemList.Add(new SelectListItem()
                {
                    Text = string.Empty,
                    Value = string.Empty
                });
            foreach (FieldInfo field in type2.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.GetField))
            {
                object rawConstantValue = field.GetRawConstantValue();
                Enum enumValue = (Enum)(Enum.Parse(type,field.Name));
                selectListItemList.Add(new SelectListItem()
                {
                    Text = GetEnumDisplayName(enumValue),//GetEnumDescription(enumValue)
                    Value = rawConstantValue.ToString()
                });
            }
            return selectListItemList;
        }
    
    • View @Html.EnumDropDownList("a", typeof(RegionEnum), " - Choice - ")
    • 效果

      2view

參考資料

  1. SelectExtensions.EnumDropDownListFor 方法
  2. ASP-NET-MVC/aspnetwebstack/src/System.Web.Mvc/Html/SelectExtensions.cs

沒有留言:

張貼留言