如何从ASP.NET MVC中的枚举创建下拉列表?


668

我正在尝试使用Html.DropDownList扩展方法,但无法弄清楚如何通过枚举使用它。

假设我有一个这样的枚举:

public enum ItemTypes
{
    Movie = 1,
    Game = 2,
    Book = 3
}

如何使用Html.DropDownList扩展方法使用这些值创建下拉列表?

还是我最好的选择就是简单地创建一个for循环并手动创建HTML元素?

Answers:


841

对于MVC v5.1,请使用Html.EnumDropDownListFor

@Html.EnumDropDownListFor(
    x => x.YourEnumField,
    "Select My Type", 
    new { @class = "form-control" })

对于MVC v5,请使用EnumHelper

@Html.DropDownList("MyType", 
   EnumHelper.GetSelectList(typeof(MyType)) , 
   "Select My Type", 
   new { @class = "form-control" })

对于MVC 5及更低版本

我将符文的答案扩展为扩展方法:

namespace MyApp.Common
{
    public static class MyExtensions{
        public static SelectList ToSelectList<TEnum>(this TEnum enumObj)
            where TEnum : struct, IComparable, IFormattable, IConvertible
        {
            var values = from TEnum e in Enum.GetValues(typeof(TEnum))
                select new { Id = e, Name = e.ToString() };
            return new SelectList(values, "Id", "Name", enumObj);
        }
    }
}

这使您可以编写:

ViewData["taskStatus"] = task.Status.ToSelectList();

通过 using MyApp.Common


13
我无法正常运作,请您帮忙。当我做Post.PostType.ToSelectList(); 它不识别扩展名吗?
巴巴罗斯阿尔卑斯山

3
我也无法使它正常工作。Status是您在任务类上的Enum属性吗?这不是枚举值之一吗?
达里尔

9
您可以限制它带有一点点:其中T:结构,IConvertible参见:stackoverflow.com/questions/79126/...
理查德·加赛德

8
这很酷。如果有人在努力与实施相抵触,这就是我的方法。在HtmlHelpers文件夹中添加了一个EnumHelpers类。使用了上面的代码。根据@TodK建议添加了名称空间:<add namespace =“ xxx.HtmlHelpers” />。然后,我在像这样的剃须刀页面中使用了它:@ Html.DropDownListFor(model => model.Status,@ Model.Status.ToSelectList())HTH
Jeff Borden

6
请注意,在较新的版本中,ASP.NET MVC有一种本机方式:stackoverflow.com/a/22295360/1361084
Ofiris 2015年

359

我知道我在这方面迟到了,但是我想您可能会发现此变体很有用,因为该变体还允许您使用描述性字符串而不是下拉列表中的枚举常量。为此,请使用[System.ComponentModel.Description]属性装饰每个枚举条目。

例如:

public enum TestEnum
{
  [Description("Full test")]
  FullTest,

  [Description("Incomplete or partial test")]
  PartialTest,

  [Description("No test performed")]
  None
}

这是我的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Reflection;
using System.ComponentModel;
using System.Linq.Expressions;

 ...

 private static Type GetNonNullableModelType(ModelMetadata modelMetadata)
    {
        Type realModelType = modelMetadata.ModelType;

        Type underlyingType = Nullable.GetUnderlyingType(realModelType);
        if (underlyingType != null)
        {
            realModelType = underlyingType;
        }
        return realModelType;
    }

    private static readonly SelectListItem[] SingleEmptyItem = new[] { new SelectListItem { Text = "", Value = "" } };

    public static string GetEnumDescription<TEnum>(TEnum value)
    {
        FieldInfo fi = value.GetType().GetField(value.ToString());

        DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

        if ((attributes != null) && (attributes.Length > 0))
            return attributes[0].Description;
        else
            return value.ToString();
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression)
    {
        return EnumDropDownListFor(htmlHelper, expression, null);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, object htmlAttributes)
    {
        ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
        Type enumType = GetNonNullableModelType(metadata);
        IEnumerable<TEnum> values = Enum.GetValues(enumType).Cast<TEnum>();

        IEnumerable<SelectListItem> items = from value in values
            select new SelectListItem
            {
                Text = GetEnumDescription(value),
                Value = value.ToString(),
                Selected = value.Equals(metadata.Model)
            };

        // If the enum is nullable, add an 'empty' item to the collection
        if (metadata.IsNullableValueType)
            items = SingleEmptyItem.Concat(items);

        return htmlHelper.DropDownListFor(expression, items, htmlAttributes);
    }

然后,您可以在自己的视图中执行此操作:

@Html.EnumDropDownListFor(model => model.MyEnumProperty)

希望这对您有所帮助!

**编辑2014年1月23日:Microsoft刚刚发布了MVC 5.1,该版本现在具有EnumDropDownListFor功能。遗憾的是,它似乎没有遵守[Description]属性,因此上面的代码仍然有效。请参阅Microsoft发行说明中的​​MVC 5.1 枚举部分

更新:虽然它确实支持Display属性[Display(Name = "Sample")],所以可以使用它。

[更新-刚刚注意到了这一点,代码看起来像是代码的扩展版本:https : //blogs.msdn.microsoft.com/stuartleeks/2010/05/21/asp-net-mvc-creating-a- dropdownlist-helper-for-enums /,还有几个附加功能。如果是这样,归属似乎是合理的;-)]


28
+1我发现这里所有答案中最有用的。我能够将其转换为高度可重用的代码段。谢谢!
Ed Charbeneau 2011年

43
Visual Studio有一个奇怪的错误,如果您不引用System.Web.Mvc.Html该错误,则会说DropDownListFor找不到该错误,但也无法解决该错误。您必须手动执行using System.Web.Mvc.Html;。就这么知道。
Kezzer 2011年

1
我在所有项目中都使用的要点中有一个变体:gist.github.com/1287511
kamranicus 2012年

1
好的解决方案,谢谢,如果您可以缓存GetEnumDescription的结果,那就更好了
M. Mennan Kara

17
新的MVC 5.1 EnumDropDownListFor不使用[Description(“”)],但使用[Display(Name =“”)]]!享受:)
Supergibbs 2014年

195

ASP.NET MVC 5.1中,他们添加了EnumDropDownListFor()帮助程序,因此不需要自定义扩展:

型号

public enum MyEnum
{
    [Display(Name = "First Value - desc..")]
    FirstValue,
    [Display(Name = "Second Value - desc...")]
    SecondValue
}

查看

@Html.EnumDropDownListFor(model => model.MyEnum)

使用标记助手(ASP.NET MVC 6)

<select asp-for="@Model.SelectedValue" asp-items="Html.GetEnumSelectList<MyEnum>()">

21
这需要以某种方式提高到第一位

3
您应该创建一个特定于MVC 5.1的新问题,并将其作为答案,然后给我发送指向该帖子的链接,以便我可以投票给喜欢的人。
凯文·海德

2
我对EnumDropDownListFor()的不满意之处在于,它将枚举的int值(而不是文本)保存到数据库中,因此,如果您选择添加新的枚举项,则必须将其放在列表的末尾,以免失去保存的数据库int值与枚举项的原始位置之间的关系。如果保存了文本,这是不必要的限制。另外,我更希望能够查看数据库并查看文本,而不是整数,然后我必须在其他地方查找文本值。否则,此html帮助程序将非常方便使用。
乔瓦尼

2
@Giovanni-您可以指定自己的数值。
汤米

1
@Giovanni Strict设计应为每个枚举项分配值(如果重要),否则该值应该无关紧要(因此将新值放在末尾应该没有问题)。当节省存储空间和提高性能时(执行某些搜索时),保存int值会更好。
King

130

我碰到了同样的问题,找到了这个问题,以为Ash所提供的解决方案不是我想要的。与内置Html.DropDownList()函数相比,必须自己创建HTML意味着灵活性较低。

事实证明,C#3等使这变得非常容易。我有一个enum电话TaskStatus

var statuses = from TaskStatus s in Enum.GetValues(typeof(TaskStatus))
               select new { ID = s, Name = s.ToString() };
ViewData["taskStatus"] = new SelectList(statuses, "ID", "Name", task.Status);

这会创建一个很好的“ ol” SelectList,可以像在视图中那样习惯使用它:

<td><b>Status:</b></td><td><%=Html.DropDownList("taskStatus")%></td></tr>

匿名类型和LINQ使此恕我直言更加优雅。不打算冒犯,灰。:)


好答案!我希望有人会使用linq和SelectList :)很高兴我首先在这里检查了!
Pure.Krome

1
ID = s给我DataTextField不是值吗?可能是什么原因?谢谢
巴尔巴罗斯阿尔普

1
符文,我使用了相同的方法,但DropDownList确实在提交到服务器时进行渲染,但不保存我选择的值。
clockwiseq

5
@BarbarosAlp要使ID为数字,您需要将枚举转换为int:select new { ID = (int)s, Name = s.ToString() };
Keith,

因为简单,这是我最喜欢的答案。遗憾的是,由于所选答案使用了您的解决方案,因此您没有得到足够的功劳。
anar khalilov

63

这是一个更好的封装解决方案:

https://www.spicelogic.com/Blog/enum-dropdownlistfor-asp-net-mvc-5

说这是您的模型:

在此处输入图片说明

用法示例:

在此处输入图片说明

生成的UI: 在此处输入图片说明

并生成HTML

在此处输入图片说明

Helper Extension源代码快照:

在此处输入图片说明

您可以从我提供的链接下载示例项目。

编辑:这是代码:

public static class EnumEditorHtmlHelper
{
    /// <summary>
    /// Creates the DropDown List (HTML Select Element) from LINQ 
    /// Expression where the expression returns an Enum type.
    /// </summary>
    /// <typeparam name="TModel">The type of the model.</typeparam>
    /// <typeparam name="TProperty">The type of the property.</typeparam>
    /// <param name="htmlHelper">The HTML helper.</param>
    /// <param name="expression">The expression.</param>
    /// <returns></returns>
    public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> expression) 
        where TModel : class
    {
        TProperty value = htmlHelper.ViewData.Model == null 
            ? default(TProperty) 
            : expression.Compile()(htmlHelper.ViewData.Model);
        string selected = value == null ? String.Empty : value.ToString();
        return htmlHelper.DropDownListFor(expression, createSelectList(expression.ReturnType, selected));
    }

    /// <summary>
    /// Creates the select list.
    /// </summary>
    /// <param name="enumType">Type of the enum.</param>
    /// <param name="selectedItem">The selected item.</param>
    /// <returns></returns>
    private static IEnumerable<SelectListItem> createSelectList(Type enumType, string selectedItem)
    {
        return (from object item in Enum.GetValues(enumType)
                let fi = enumType.GetField(item.ToString())
                let attribute = fi.GetCustomAttributes(typeof (DescriptionAttribute), true).FirstOrDefault()
                let title = attribute == null ? item.ToString() : ((DescriptionAttribute) attribute).Description
                select new SelectListItem
                  {
                      Value = item.ToString(), 
                      Text = title, 
                      Selected = selectedItem == item.ToString()
                  }).ToList();
    }
}

2
只是我的意见,但我认为这个答案比公认的答案要干净得多。我特别喜欢使用Description属性的选项。我添加了代码,以便人们无需下载即可复制/粘贴它。
本·米尔斯

将扩展方法称为EnumDropDownListFor而不是DropDownListFor用法:-> @ Html.EnumDropDownListFor(x => x.Gender)
sandeep talabathula

对于正在寻找添加另一个“请选择”元素的人,返回htmlHelper.DropDownListFor(expression,createSelectList(expression.ReturnType,selected,firstElement),“ Please Select”);
桑迪普

1
工作良好!但是,在“详细信息”页面上,DisplayFor()会显示枚举的选定值,而不是相应的描述。我想这要求枚举类型的DisplayFor()重载。有人对此有解决方案吗?
corix010

48

Html.DropDownListFor仅需要IEnumerable,因此,Prise解决方案的替代方法如下。这将使您可以简单地编写:

@Html.DropDownListFor(m => m.SelectedItemType, Model.SelectedItemType.ToSelectList())

[其中SelectedItemType是模型上类型为ItemTypes的字段,并且模型为非null]

另外,您实际上不需要泛化扩展方法,因为可以使用enumValue.GetType()而不是typeof(T)。

编辑:这里也集成了Simon的解决方案,并包括ToDescription扩展方法。

public static class EnumExtensions
{
    public static IEnumerable<SelectListItem> ToSelectList(this Enum enumValue)
    {
        return from Enum e in Enum.GetValues(enumValue.GetType())
               select new SelectListItem
               {
                   Selected = e.Equals(enumValue),
                   Text = e.ToDescription(),
                   Value = e.ToString()
               };
    }

    public static string ToDescription(this Enum value)
    {
        var attributes = (DescriptionAttribute[])value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
        return attributes.Length > 0 ? attributes[0].Description : value.ToString();
    }
}

对我不起作用(“ System.NullReferenceException:对象引用未设置为对象的实例。”)...我的“模型”为空...可能与Simon拥有的“ GetNonNullableModelType”有关包括
学习者2012年

@Cristi,您是对的,此解决方案不能在Model为空的情况下使用。我会尽量避免这种设计,并在这种情况下初始化为“空”模型。
Zaid Masud 2012年

好吧,我是ASP MVC的新手,但是我在.Net中有相当的经验。谢谢,我将调查您的建议。顺便说一句,您的ToDescription扩展名超出了“枚举”范围。我猜想“对象”本身就很好。这是我使用Simon的代码并对其进行更多清理时所使用的。
学习者2012年

@Cristi很难理解“远远超出'Enum'范围”的含义,但这听起来像是您在说ToDescription扩展方法没有严格键入ItemTypes枚举?这是有意的,并使扩展方法通常可用于所有枚举。如果将其与通用扩展方法进行比较,则每种方法都有其优缺点。特别是,如果您泛化成文,就不能仅将其约束在枚举上。
Zaid Masud 2012年

1
很好,谢谢。我更改了value.ToString以使用扩展FromCamelCase,以防万一没有描述。我就是这样滚动的:)
Valamas

33

因此,如果您要寻找简单易用的功能,则无需扩展功能。.这就是我所做的

<%= Html.DropDownListFor(x => x.CurrentAddress.State, new SelectList(Enum.GetValues(typeof(XXXXX.Sites.YYYY.Models.State))))%>

其中XXXXX.Sites.YYYY.Models.State是一个枚举

做助手功能可能更好,但是当时间短时可以完成工作。


很好,这可以填充下拉列表,但是如何在Razor语法中为Html.DropDownListFor设置默认选择的值?我想显示一个带有枚举组合框的表,我还需要根据之前的值设置选定的值。
Johncl 2011年

2
应该能够将具有所选值的第二个参数传递给新的SelectList(IEnumerable,object)函数。MSDN文件:msdn.microsoft.com/en-us/library/dd460123.aspx
Marty Trenouth 2011年

23

扩展奖赏和符文的答案,如果您希望选择列表项的value属性映射到Enumeration类型的整数值而不是字符串值,请使用以下代码:

public static SelectList ToSelectList<T, TU>(T enumObj) 
    where T : struct
    where TU : struct
{
    if(!typeof(T).IsEnum) throw new ArgumentException("Enum is required.", "enumObj");

    var values = from T e in Enum.GetValues(typeof(T))
                 select new { 
                    Value = (TU)Convert.ChangeType(e, typeof(TU)),
                    Text = e.ToString() 
                 };

    return new SelectList(values, "Value", "Text", enumObj);
}

不必将每个Enumeration值视为TEnum对象,我们可以将其视为对象,然后将其强制转换为整数以获得未装箱的值。

注意: 我还添加了一个通用类型约束,以限制此扩展名只能用于结构体的类型(Enum的基本类型),并添加了运行时类型验证,以确保传入的结构确实是Enum。

更新10/23/12: 为基础类型添加了通用类型参数,并修复了影响.NET 4+的非编译问题。


谢谢!这就是我需要的答案。我将一个Enum的整数值存储为数据库中的一列,此解决方案似乎运行良好。
grimus 2010年

但是如果您存储的是char而不是int怎么办?这是我的情况。显然,我可以将(int)更改为(char),但如何使该泛型也好。怎么做?
Stefanvds 2010年

@Stefandvds对于强制转换为正确的表示类型,这是一个很大的问题。根据我刚刚执行的测试,似乎可以实现此目的的唯一方法是将实际类型指定为另一个类型参数。ToSelectList<TEnum, TEnumValue>(this TEnum enumObj) { ... }
内森·泰勒


如果您的枚举值是int,则可以简单地使用Value = Convert.ToInt32(e)(int)e无法编译。:(
安德鲁

11

解决使用Prise的扩展方法获取数字而不是文本的问题。

public static SelectList ToSelectList<TEnum>(this TEnum enumObj)
{
  var values = from TEnum e in Enum.GetValues(typeof(TEnum))
               select new { ID = (int)Enum.Parse(typeof(TEnum),e.ToString())
                         , Name = e.ToString() };

  return new SelectList(values, "Id", "Name", enumObj);
}

那是我一直在寻找的东西,尽管这比我想象的要丑陋。我不知道为什么Visual Studio不会让您直接投射eint
安德鲁

或者您可以简单地使用ID = Convert.ToInt32(e)
安德鲁

11

一个超级简单的方法来完成此任务-无需所有似乎过大的扩展程序,这是这样的:

您的枚举:

    public enum SelectedLevel
    {
       Level1,
       Level2,
       Level3,
       Level4
    }

在控制器内部,将枚举绑定到列表:

    List<SelectedLevel> myLevels = Enum.GetValues(typeof(SelectedLevel)).Cast<SelectedLevel>().ToList();

之后,将其放入ViewBag:

    ViewBag.RequiredLevel = new SelectList(myLevels);

最后,只需将其绑定到视图:

    @Html.DropDownList("selectedLevel", (SelectList)ViewBag.RequiredLevel, new { @class = "form-control" })

到目前为止,这是我找到的最简单的方法,不需要任何扩展名或任何疯狂的内容。

更新:请参阅下面的安德鲁斯评论。


3
仅当您尚未为枚举分配任何值时,此方法才有效。如果有Level1 = 1,则下拉列表的值为,"Level1"而不是1
安德鲁

11

我为此找到的最佳解决方案是将此博客Simon Goldstone的答案相结合

这允许在模型中使用枚举。本质上,该想法是使用整数属性以及枚举,并模拟整数属性。

然后,使用[System.ComponentModel.Description]属性用显示文本注释模型,并在视图中使用“ EnumDropDownListFor”扩展名。

这使得视图和模型都非常易读和可维护。

模型:

public enum YesPartialNoEnum
{
    [Description("Yes")]
    Yes,
    [Description("Still undecided")]
    Partial,
    [Description("No")]
    No
}

//........

[Display(Name = "The label for my dropdown list")]
public virtual Nullable<YesPartialNoEnum> CuriousQuestion{ get; set; }
public virtual Nullable<int> CuriousQuestionId
{
    get { return (Nullable<int>)CuriousQuestion; }
    set { CuriousQuestion = (Nullable<YesPartialNoEnum>)value; }
}

视图:

@using MyProject.Extensions
{
//...
    @Html.EnumDropDownListFor(model => model.CuriousQuestion)
//...
}

扩展名(直接从西蒙·戈德斯通的答案(出于完整性考虑,此处包括):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.ComponentModel;
using System.Reflection;
using System.Linq.Expressions;
using System.Web.Mvc.Html;

namespace MyProject.Extensions
{
    //Extension methods must be defined in a static class
    public static class MvcExtensions
    {
        private static Type GetNonNullableModelType(ModelMetadata modelMetadata)
        {
            Type realModelType = modelMetadata.ModelType;

            Type underlyingType = Nullable.GetUnderlyingType(realModelType);
            if (underlyingType != null)
            {
                realModelType = underlyingType;
            }
            return realModelType;
        }

        private static readonly SelectListItem[] SingleEmptyItem = new[] { new SelectListItem { Text = "", Value = "" } };

        public static string GetEnumDescription<TEnum>(TEnum value)
        {
            FieldInfo fi = value.GetType().GetField(value.ToString());

            DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

            if ((attributes != null) && (attributes.Length > 0))
                return attributes[0].Description;
            else
                return value.ToString();
        }

        public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression)
        {
            return EnumDropDownListFor(htmlHelper, expression, null);
        }

        public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, object htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            Type enumType = GetNonNullableModelType(metadata);
            IEnumerable<TEnum> values = Enum.GetValues(enumType).Cast<TEnum>();

            IEnumerable<SelectListItem> items = from value in values
                                                select new SelectListItem
                                                {
                                                    Text = GetEnumDescription(value),
                                                    Value = value.ToString(),
                                                    Selected = value.Equals(metadata.Model)
                                                };

            // If the enum is nullable, add an 'empty' item to the collection
            if (metadata.IsNullableValueType)
                items = SingleEmptyItem.Concat(items);

            return htmlHelper.DropDownListFor(expression, items, htmlAttributes);
        }
    }
}

这不起作用,MVC 4 Razor。在视图或运行时中,错误=“以下方法或属性'LDN.Extensions.MvcExtensions.EnumDropDownListFor <MyModel,LDN.Models.YesPartialNoEnum?>(System.Web.Mvc.HtmlHelper <MyModel> .Linq.Expressions.Expression <System.Func <MyModel,LDN.Models.YesPartialNoEnum?>>)'和....“,并再次重复具有相同道具的完全相同的方法(此处不允许使用足够的字符)。
Marc 2012年


8
@Html.DropDownListFor(model => model.Type, Enum.GetNames(typeof(Rewards.Models.PropertyType)).Select(e => new SelectListItem { Text = e }))

好!如何以这种方式从枚举中获取价值和文本?我的意思是我有SomeEnum {some1 = 1,some2 = 2}我需要为选择列表的文本获取数字(1、2),为选择列表文本获取文本(some1,some2)
Dmitresky

7

这是符文与奖赏答案,更改为使用Enum int值作为ID。

样本枚举:

public enum ItemTypes
{
    Movie = 1,
    Game = 2,
    Book = 3
}

扩展方式:

    public static SelectList ToSelectList<TEnum>(this TEnum enumObj)
    {
        var values = from TEnum e in Enum.GetValues(typeof(TEnum))
                     select new { Id = (int)Enum.Parse(typeof(TEnum), e.ToString()), Name = e.ToString() };

        return new SelectList(values, "Id", "Name", (int)Enum.Parse(typeof(TEnum), enumObj.ToString()));
    }

用法样本:

 <%=  Html.DropDownList("MyEnumList", ItemTypes.Game.ToSelectList()) %>

记住要导入包含扩展方法的名称空间

<%@ Import Namespace="MyNamespace.LocationOfExtensionMethod" %>

生成的HTML示例:

<select id="MyEnumList" name="MyEnumList">
    <option value="1">Movie</option>
    <option selected="selected" value="2">Game</option>
    <option value="3">Book </option>
</select>

请注意,用于调用ToSelectListon的项目是所选项目。


或者您可以简单地使用Id = Convert.ToInt32(e)
安德鲁

6

这是Razor的版本:

@{
    var itemTypesList = new List<SelectListItem>();
    itemTypesList.AddRange(Enum.GetValues(typeof(ItemTypes)).Cast<ItemTypes>().Select(
                (item, index) => new SelectListItem
                {
                    Text = item.ToString(),
                    Value = (index).ToString(),
                    Selected = Model.ItemTypeId == index
                }).ToList());
 }


@Html.DropDownList("ItemTypeId", itemTypesList)

仅当您的枚举包含以0开头的连续值时,此方法才有效。Flags枚举不适用于此枚举。不过,可以创造性地使用索引的Select。
Suncat2000

6

在.NET Core中,您可以使用以下命令:

@Html.DropDownListFor(x => x.Foo, Html.GetEnumSelectList<MyEnum>())

1
或使用标签助手<select asp-for="Model.Foo" class="form-control" asp-items="@Html.GetEnumSelectList<MyEnum>()"></select>
Pascal R.

是的,我说标签助手更好,因为格式更接近纯HTML;)
GoldenAge

您也可以执行此操作@ Html.DropDownListFor(x => x.Foo,Html.GetEnumSelectList(typeof(FooEnum)))
Fereydoon Barikzehy


5

基于Simon的答案,一种类似的方法是从资源文件中获取要显示的Enum值,而不是在Enum本身的description属性中显示。如果您的网站需要使用多种语言呈现,并且要为Enums使用特定的资源文件,这将很有帮助,您可以再进一步一步,在Enum中仅包含Enum值,并通过扩展名引用它们约定,例如[EnumName] _ [EnumValue]-最终减少了打字!

扩展名如下所示:

public static IHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> html, Expression<Func<TModel, TEnum>> expression)
{            
    var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);

    var enumType = Nullable.GetUnderlyingType(metadata.ModelType) ?? metadata.ModelType;

    var enumValues = Enum.GetValues(enumType).Cast<object>();

    var items = from enumValue in enumValues                        
                select new SelectListItem
                {
                    Text = GetResourceValueForEnumValue(enumValue),
                    Value = ((int)enumValue).ToString(),
                    Selected = enumValue.Equals(metadata.Model)
                };


    return html.DropDownListFor(expression, items, string.Empty, null);
}

private static string GetResourceValueForEnumValue<TEnum>(TEnum enumValue)
{
    var key = string.Format("{0}_{1}", enumValue.GetType().Name, enumValue);

    return Enums.ResourceManager.GetString(key) ?? enumValue.ToString();
}

Enums.Resx文件中的资源类似于ItemTypes_Movie:Film

我想做的另一件事是,与其直接调用扩展方法,不如使用@ Html.EditorFor(x => x.MyProperty)进行调用,或者理想情况下使用整齐的@形式Html.EditorForModel()。为此,我将字符串模板更改为如下形式

@using MVCProject.Extensions

@{
    var type = Nullable.GetUnderlyingType(ViewData.ModelMetadata.ModelType) ?? ViewData.ModelMetadata.ModelType;

    @(typeof (Enum).IsAssignableFrom(type) ? Html.EnumDropDownListFor(x => x) : Html.TextBoxFor(x => x))
}

如果您对此感兴趣,我会在这里在博客上提供更详细的答案:

http://paulthecyclist.com/2013/05/24/enum-dropdown/


5

好吧,我参加聚会真的很晚,但是对于它的价值,我已经在博客中讨论了这个主题,从而创建了一个EnumHelper可以轻松进行转换的类。

http://jnye.co/Posts/4/creating-a-dropdown-list-from-an-enum-in-mvc-and-c%23

在您的控制器中:

//If you don't have an enum value use the type
ViewBag.DropDownList = EnumHelper.SelectListFor<MyEnum>();

//If you do have an enum value use the value (the value will be marked as selected)    
ViewBag.DropDownList = EnumHelper.SelectListFor(MyEnum.MyEnumValue);

在您看来:

@Html.DropDownList("DropDownList")
@* OR *@
@Html.DropDownListFor(m => m.Property, ViewBag.DropDownList as SelectList, null)

助手类:

public static class EnumHelper
{
    // Get the value of the description attribute if the   
    // enum has one, otherwise use the value.  
    public static string GetDescription<TEnum>(this TEnum value)
    {
        var fi = value.GetType().GetField(value.ToString());

        if (fi != null)
        {
            var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attributes.Length > 0)
            {
                return attributes[0].Description;
            }
        }

        return value.ToString();
    }

    /// <summary>
    /// Build a select list for an enum
    /// </summary>
    public static SelectList SelectListFor<T>() where T : struct
    {
        Type t = typeof(T);
        return !t.IsEnum ? null
                         : new SelectList(BuildSelectListItems(t), "Value", "Text");
    }

    /// <summary>
    /// Build a select list for an enum with a particular value selected 
    /// </summary>
    public static SelectList SelectListFor<T>(T selected) where T : struct
    {
        Type t = typeof(T);
        return !t.IsEnum ? null
                         : new SelectList(BuildSelectListItems(t), "Text", "Value", selected.ToString());
    }

    private static IEnumerable<SelectListItem> BuildSelectListItems(Type t)
    {
        return Enum.GetValues(t)
                   .Cast<Enum>()
                   .Select(e => new SelectListItem { Value = e.ToString(), Text = e.GetDescription() });
    }
}

4

我对此很迟,但是如果您乐于添加“ 不受约束的旋律”,我只是找到了一种非常不错的方法来使用一行代码来实现 NuGet包(Jon Skeet的一个不错的小型库),。

此解决方案更好,因为:

  1. 它确保(带有通用类型约束)该值确实是一个枚举值(由于不受约束的旋律)
  2. 避免不必要的拳击(由于不受限制的旋律)
  3. 它缓存所有描述,以避免在每个呼叫中​​使用反射(由于无约束旋律)
  4. 它比其他解决方案少的代码!

因此,以下是使此工作正常进行的步骤:

  1. 在程序包管理器控制台中,“安装程序包不受约束的旋律”
  2. 在模型上添加一个属性,如下所示:

    //Replace "YourEnum" with the type of your enum
    public IEnumerable<SelectListItem> AllItems
    {
        get
        {
            return Enums.GetValues<YourEnum>().Select(enumValue => new SelectListItem { Value = enumValue.ToString(), Text = enumValue.GetDescription() });
        }
    }

现在,您已经在模型上暴露了SelectListItem的列表,您可以使用@ Html.DropDownList或@ Html.DropDownListFor将此属性用作源。


使用Jon Skeet的代码+1 :),虽然只是在开玩笑
Vamsi

3

此扩展方法的另一种解决方法-当前版本未选择枚举的当前值。我固定了最后一行:

public static SelectList ToSelectList<TEnum>(this TEnum enumObj) where TEnum : struct
    {
        if (!typeof(TEnum).IsEnum) throw new ArgumentException("An Enumeration type is required.", "enumObj");

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


        return new SelectList(values, "ID", "Name", ((int)Enum.Parse(typeof(TEnum), enumObj.ToString())).ToString());
    }

3

如果要添加本地化支持,只需将s.toString()方法更改为如下所示:

ResourceManager rManager = new ResourceManager(typeof(Resources));
var dayTypes = from OperatorCalendarDay.OperatorDayType s in Enum.GetValues(typeof(OperatorCalendarDay.OperatorDayType))
               select new { ID = s, Name = rManager.GetString(s.ToString()) };

在这里,typeof(Resources)是要加载的资源,然后获取本地化的String,如果枚举器的值包含多个单词,则该字符串也很有用。


3

这是我的辅助方法版本。我用这个:

var values = from int e in Enum.GetValues(typeof(TEnum))
             select new { ID = e, Name = Enum.GetName(typeof(TEnum), e) };

而不是:

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

这里是:

public static SelectList ToSelectList<TEnum>(this TEnum self) where TEnum : struct
    {
        if (!typeof(TEnum).IsEnum)
        {
            throw new ArgumentException("self must be enum", "self");
        }

        Type t = typeof(TEnum);

        var values = from int e in Enum.GetValues(typeof(TEnum))
                     select new { ID = e, Name = Enum.GetName(typeof(TEnum), e) };

        return new SelectList(values, "ID", "Name", self);
    }

3

您也可以在Griffin.MvcContrib中使用我的自定义HtmlHelpers。如下代码:

@Html2.CheckBoxesFor(model => model.InputType) <br />
@Html2.RadioButtonsFor(model => model.InputType) <br />
@Html2.DropdownFor(model => model.InputType) <br />

产生:

在此处输入图片说明

https://github.com/jgauffin/griffin.mvccontrib


3

我想以不同的方式回答这个问题,即用户无需在其中controllerLinq表达式中做任何事情。这条路...

我有一个 ENUM

public enum AccessLevelEnum
    {
        /// <summary>
        /// The user cannot access
        /// </summary>
        [EnumMember, Description("No Access")]
        NoAccess = 0x0,

        /// <summary>
        /// The user can read the entire record in question
        /// </summary>
        [EnumMember, Description("Read Only")]
        ReadOnly = 0x01,

        /// <summary>
        /// The user can read or write
        /// </summary>
        [EnumMember, Description("Read / Modify")]
        ReadModify = 0x02,

        /// <summary>
        /// User can create new records, modify and read existing ones
        /// </summary>
        [EnumMember, Description("Create / Read / Modify")]
        CreateReadModify = 0x04,

        /// <summary>
        /// User can read, write, or delete
        /// </summary>
        [EnumMember, Description("Create / Read / Modify / Delete")]
        CreateReadModifyDelete = 0x08,

        /*/// <summary>
        /// User can read, write, or delete
        /// </summary>
        [EnumMember, Description("Create / Read / Modify / Delete / Verify / Edit Capture Value")]
        CreateReadModifyDeleteVerify = 0x16*/
    }

现在,我可以dropdown使用this来简单地创建一个enum

@Html.DropDownList("accessLevel",new SelectList(AccessLevelEnum.GetValues(typeof(AccessLevelEnum))),new { @class = "form-control" })

要么

@Html.DropDownListFor(m=>m.accessLevel,new SelectList(AccessLevelEnum.GetValues(typeof(AccessLevelEnum))),new { @class = "form-control" })

如果要选择索引,请尝试此操作

@Html.DropDownListFor(m=>m.accessLevel,new SelectList(AccessLevelEnum.GetValues(typeof(AccessLevelEnum)) , AccessLevelEnum.NoAccess ),new { @class = "form-control" })

在这里,我用作AccessLevelEnum.NoAccess默认选择下拉菜单的额外参数。


3

我在这里找到了答案。但是,我的某些枚举具有[Description(...)]属性,因此我修改了代码以提供对此的支持:

    enum Abc
    {
        [Description("Cba")]
        Abc,

        Def
    }


    public static MvcHtmlString EnumDropDownList<TEnum>(this HtmlHelper htmlHelper, string name, TEnum selectedValue)
    {
        IEnumerable<TEnum> values = Enum.GetValues(typeof(TEnum))
            .Cast<TEnum>();

        List<SelectListItem> items = new List<SelectListItem>();
        foreach (var value in values)
        {
            string text = value.ToString();

            var member = typeof(TEnum).GetMember(value.ToString());
            if (member.Count() > 0)
            {
                var customAttributes = member[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
                if (customAttributes.Count() > 0)
                {
                    text = ((DescriptionAttribute)customAttributes[0]).Description;
                }
            }

            items.Add(new SelectListItem
            {
                Text = text,
                Value = value.ToString(),
                Selected = (value.Equals(selectedValue))
            });
        }

        return htmlHelper.DropDownList(
            name,
            items
            );
    }

希望能有所帮助。


我想返回类型= DropdownList的成员。我对Text = DescriptionAttribute很满意,但是很难从Value中获取int值
NanaFadanvis

2

@Simon Goldstone:感谢您的解决方案,它可以完美地应用于我的案例中。唯一的问题是我必须将其转换为VB。但是现在完成了,并且为了节省其他人的时间(以防他们需要),我将其放在此处:

Imports System.Runtime.CompilerServices
Imports System.ComponentModel
Imports System.Linq.Expressions

Public Module HtmlHelpers
    Private Function GetNonNullableModelType(modelMetadata As ModelMetadata) As Type
        Dim realModelType = modelMetadata.ModelType

        Dim underlyingType = Nullable.GetUnderlyingType(realModelType)

        If Not underlyingType Is Nothing Then
            realModelType = underlyingType
        End If

        Return realModelType
    End Function

    Private ReadOnly SingleEmptyItem() As SelectListItem = {New SelectListItem() With {.Text = "", .Value = ""}}

    Private Function GetEnumDescription(Of TEnum)(value As TEnum) As String
        Dim fi = value.GetType().GetField(value.ToString())

        Dim attributes = DirectCast(fi.GetCustomAttributes(GetType(DescriptionAttribute), False), DescriptionAttribute())

        If Not attributes Is Nothing AndAlso attributes.Length > 0 Then
            Return attributes(0).Description
        Else
            Return value.ToString()
        End If
    End Function

    <Extension()>
    Public Function EnumDropDownListFor(Of TModel, TEnum)(ByVal htmlHelper As HtmlHelper(Of TModel), expression As Expression(Of Func(Of TModel, TEnum))) As MvcHtmlString
        Return EnumDropDownListFor(htmlHelper, expression, Nothing)
    End Function

    <Extension()>
    Public Function EnumDropDownListFor(Of TModel, TEnum)(ByVal htmlHelper As HtmlHelper(Of TModel), expression As Expression(Of Func(Of TModel, TEnum)), htmlAttributes As Object) As MvcHtmlString
        Dim metaData As ModelMetadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData)
        Dim enumType As Type = GetNonNullableModelType(metaData)
        Dim values As IEnumerable(Of TEnum) = [Enum].GetValues(enumType).Cast(Of TEnum)()

        Dim items As IEnumerable(Of SelectListItem) = From value In values
            Select New SelectListItem With
            {
                .Text = GetEnumDescription(value),
                .Value = value.ToString(),
                .Selected = value.Equals(metaData.Model)
            }

        ' If the enum is nullable, add an 'empty' item to the collection
        If metaData.IsNullableValueType Then
            items = SingleEmptyItem.Concat(items)
        End If

        Return htmlHelper.DropDownListFor(expression, items, htmlAttributes)
    End Function
End Module

结束您可以这样使用它:

@Html.EnumDropDownListFor(Function(model) (model.EnumField))


2
@Html.DropdownListFor(model=model->Gender,new List<SelectListItem>
{
 new ListItem{Text="Male",Value="Male"},
 new ListItem{Text="Female",Value="Female"},
 new ListItem{Text="--- Select -----",Value="-----Select ----"}
}
)

2
@Html.DropDownListFor(model => model.MaritalStatus, new List<SelectListItem> 
{  

new SelectListItem { Text = "----Select----", Value = "-1" },


new SelectListItem { Text = "Marrid", Value = "M" },


 new SelectListItem { Text = "Single", Value = "S" }

})

我认为这不是一个有效的答案,它根本没有使用枚举来填充下拉列表。
安德鲁
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.