65.9K
CodeProject 正在变化。 阅读更多。
Home

在 ASP.NET MVC 中将 Enum 绑定到 DropdownList

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (12投票s)

2015年11月16日

CPOL

3分钟阅读

viewsIcon

74890

将下拉列表与枚举绑定的更好方法。

引言

有时,我们在开发过程中会遇到需要从 Enum 填充 dropdownlist 的情况。 有几种方法可以做到这一点。 但是每种方法都有其优点和缺点。 在编写代码时,应该始终牢记 SOLIDDRY 原则。 我将展示两种方法,您将能够了解哪种方法更好,以及为什么更好。

方法 1 (典型方法)

在我看来,第一种方法不是很优雅,但它肯定会实现我们的目标。

考虑以下我们要在下拉列表中填充的 Enum

 public enum eUserRole : int
 {
    SuperAdmin = 0,
    PhoenixAdmin = 1,
    OfficeAdmin = 2,
    ReportUser = 3,
    BillingUser = 4
  }

因此,通常,我们通过在 Action 中以以下方式添加每个 Enum 值来创建 SelectList

  var enumData = from eUserRole e in Enum.GetValues(typeof(eUserRole))
                 select new 
                      { 
                        ID = (int)e, 
                        Name = e.ToString() 
                      };

现在,将其设置为 ViewBag,以便我们可以在 View 中使用它

  ViewBag.EnumList = new SelectList(enumData,"ID","Name");

现在在 View

  @Html.DropDownList("EnumDropDown",ViewBag.EnumList as SelectList)

方法 1 中的问题

上述方法的问题是,每当我们有 Enum 要与某些 Html Helper 绑定时,我们都必须编写上述 Linq 查询代码,以在 Action 中获取 enum 的所有值,这有点麻烦,需要一遍又一遍地重写。 接下来,我们将看到如何使它更好、更可重用。

方法 2

现在,这里有一种使用扩展方法和泛型来实现它的优雅方法,它将返回 Enum 值作为任何类型的 EnumSelectList

public static class ExtensionMethods
{
     public static System.Web.Mvc.SelectList ToSelectList<TEnum>(this TEnum obj)
         where TEnum : struct, IComparable, IFormattable, IConvertible 
     {

     return new SelectList(Enum.GetValues(typeof(TEnum)).OfType<Enum>()
         .Select(x =>
             new SelectListItem
             {
                 Text = Enum.GetName(typeof(TEnum), x),
                 Value = (Convert.ToInt32(x)).ToString()
             }), "Value", "Text");
     }
 }

现在,我们只需要以这种方式在 Action 中对任何 Enum 调用它

  ViewBag.EnumList = eUserRole.SuperAdmin.ToSelectList();

我们也可以直接在 View 中使用它,我们只需要包含命名空间,以防它在单独的命名空间中

  @Html.DropDownList("EnumDropDown",eUserRole.SuperAdmin.ToSelectList())

当用户编辑记录时,您可能需要在 dropdownlist 中设置选定的值。

我们可以根据我们的需求扩展扩展方法。

使用选定值参数重载

以下是扩展方法重载,用于传递选定的值,以防我们想要设置选定的值,您也可以根据需要编写其他重载

public static class ExtensionMethods
{
    public static System.Web.Mvc.SelectList ToSelectList<TEnum>(this TEnum obj,object selectedValue)
  where TEnum : struct, IComparable, IFormattable, IConvertible 
    {
    return new SelectList(Enum.GetValues(typeof(TEnum)).OfType<Enum>()
        .Select(x =>
            new SelectListItem
            {
                Text = Enum.GetName(typeof(TEnum), x),
                Value = (Convert.ToInt32(x)).ToString()
            }), "Value", "Text",selectedValue);
    }
}

并以这种方式在 View 中使用

  @Html.DropDownList("EnumDropDownWithSelected",
	eUserRole.SuperAdmin.ToSelectList((int)eUserRole.OfficeAdmin))

现在,下拉列表将默认选择 OfficeAdmin

在大多数情况下,我们不希望在下拉列表中显示 Enum 值,而是希望显示用户友好的术语作为下拉文本。 为此,我们可以按照以下方式为 Enum 编写 Attribute

创建一个从 Attribute 类型继承的自定义类

 public class EnumDisplayNameAttribute : Attribute
 {
    private string _displayName;
    public string DisplayName
    {
       get { return _displayName; }
       set { _displayName = value; }
    }
  }

现在,在 Enum 上使用 attribute

 public enum eUserRole : int
 {
    [EnumDisplayName(DisplayName="Super Admin")]
    SuperAdmin = 0,
    [EnumDisplayName(DisplayName = "Phoenix Admin")]
    PhoenixAdmin = 1,
    [EnumDisplayName(DisplayName = "Office Admin")]
    OfficeAdmin = 2,
    [EnumDisplayName(DisplayName = "Report User")]
    ReportUser = 3,
    [EnumDisplayName(DisplayName = "Billing User")]
    BillingUser = 4
 }

现在,我们需要修改或编写另一个扩展方法,因为现在我们需要选择 DisplayName 属性的值。

我们现在有两个扩展方法,一个返回特定的 EnumDisplayName Attribute 值,第二个返回针对 EnumSelectList

public static class ExtensionMethods
{
    public static System.Web.Mvc.SelectList ToSelectList<TEnum>(this TEnum obj)
        where TEnum : struct, IComparable, IFormattable, IConvertible // correct one
    {

    return new SelectList(Enum.GetValues(typeof(TEnum)).OfType<Enum>()
        .Select(x =>
            new SelectListItem
            {
                Text = x.DisplayName(),
                Value = (Convert.ToInt32(x)).ToString()
            }), "Value", "Text");
    }

   public static string DisplayName(this Enum value)
   {
       FieldInfo field = value.GetType().GetField(value.ToString());

       EnumDisplayNameAttribute attribute
               = Attribute.GetCustomAttribute(field, typeof(EnumDisplayNameAttribute))
                   as EnumDisplayNameAttribute;

       return attribute == null ? value.ToString() : attribute.DisplayName;
   }
}

方法 2 的问题

第二种方法比第一种方法好得多,但是方法 2 仍然存在一个问题,那就是我们在扩展中硬编码了 Attribute 类型,我们很可能拥有多个 Enum 属性,并且可以使用不同的 Enum 装饰它们,并且在此类 Enum 上调用此扩展方法将无法正常工作。

方法 3

因此,我提出了一个更好的实现,以便消费者可以传递属性类型和需要使用的属性的属性。

我们将添加另一个扩展方法,该方法返回属性值,并且它是通用的,因此用户可以指定属性类型本身

public static string AttributeValue<TEnum,TAttribute>(this TEnum value,Func<TAttribute,string> func) 
    where T : Attribute
{
   FieldInfo field = value.GetType().GetField(value.ToString());

   T attribute = Attribute.GetCustomAttribute(field, typeof(T)) as T;

   return attribute == null ? value.ToString() : func(attribute);

}  

我们将在返回 Enum 作为 SelectList 的扩展方法中使用此扩展

public static System.Web.Mvc.SelectList ToSelectList<TEnum,TAttribute>
(this TEnum obj,Func<TAttribute,string> func,object selectedValue=null)
  where TEnum : struct, IComparable, IFormattable, IConvertible
  where TAttribute : Attribute
    {
        
        return new SelectList(Enum.GetValues(typeof(TEnum)).OfType<Enum>() 
             .Select(x => 
                 new SelectListItem 
                 { 
                    Text = x.AttributeValue<TEnum,TAttribute>(func), 
                    Value = (Convert.ToInt32(x)).ToString() 
                 }), 
             "Value", 
             "Text",
              selectedValue);
    }

现在,消费者可以通过传递属性及其要用于显示名称的属性来使用它,我们的 View 代码现在看起来像

 @Html.DropDownList("EnumDropDownWithSelected", eUserRole.SuperAdmin.ToSelectList<eUserRole,
EnumDisplayNameAttribute>(attr=>attr.DisplayName,(int)eUserRole.OfficeAdmin))
© . All rights reserved.