我正在尝试使用Html.DropDownList
扩展方法,但无法弄清楚如何将它与枚举一起使用.
假设我有一个这样的枚举:
public enum ItemTypes { Movie = 1, Game = 2, Book = 3 }
如何使用Html.DropDownList
扩展方法创建包含这些值的下拉列表?
或者我最好的办法是简单地创建一个for循环并手动创建Html元素?
@Html.EnumDropDownListFor( x => x.YourEnumField, "Select My Type", new { @class = "form-control" })
@Html.DropDownList("MyType", EnumHelper.GetSelectList(typeof(MyType)) , "Select My Type", new { @class = "form-control" })
我将Rune的答案转换为扩展方法:
namespace MyApp.Common { public static class MyExtensions{ public static SelectList ToSelectList(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
我知道我在这方面迟到了,但是你认为这个变种很有用,因为这个变体也允许你在下拉列表中使用描述性字符串而不是枚举常量.为此,请使用[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 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 (this HtmlHelper htmlHelper, Expression > expression) { return EnumDropDownListFor(htmlHelper, expression, null); } public static MvcHtmlString EnumDropDownListFor (this HtmlHelper htmlHelper, Expression > expression, object htmlAttributes) { ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); Type enumType = GetNonNullableModelType(metadata); IEnumerable values = Enum.GetValues(enumType).Cast (); IEnumerable 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日:微软刚刚发布MVC 5.1,现在有一个EnumDropDownListFor功能.遗憾的是,它似乎不尊重[Description]属性,因此上面的代码仍然有效.请参阅Microsoft的MVC 5.1发行说明中的Enum部分.
更新:它确实支持Display属性[Display(Name = "Sample")]
,因此可以使用它.
[更新 - 只是注意到这一点,代码看起来像这里的代码的扩展版本:https://blogs.msdn.microsoft.com/stuartleeks/2010/05/21/asp-net-mvc-creating-a- dropdownlist-helper-for-enums /,有几个附加功能.如果是这样,归属似乎是公平的;-)]
在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)
使用Tag Helper(ASP.NET MVC 6):
我碰到了同样的问题,发现了这个问题,并认为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
,可以像你在视图中习惯的那样使用:
Status: <%=Html.DropDownList("taskStatus")%>
匿名类型和LINQ使这个更优雅恕我直言.Ash,没有违法行为.:)
这是一个更好的封装解决方案:
https://www.spicelogic.com/Blog/enum-dropdownlistfor-asp-net-mvc-5
这里说的是你的型号:
样品用法:
生成的UI:
并生成HTML
Helper扩展源代码快照:
您可以从我提供的链接下载示例项目.
编辑:这是代码:
public static class EnumEditorHtmlHelper { ////// Creates the DropDown List (HTML Select Element) from LINQ /// Expression where the expression returns an Enum type. /// ///The type of the model. ///The type of the property. /// The HTML helper. /// The expression. ///public static MvcHtmlString DropDownListFor (this HtmlHelper htmlHelper, Expression > 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)); } /// /// Creates the select list. /// /// Type of the enum. /// The selected item. ///private static IEnumerable 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(); } }
Html.DropDownListFor只需要一个IEnumerable,因此Prize解决方案的替代方案如下.这将允许您简单地写:
@Html.DropDownListFor(m => m.SelectedItemType, Model.SelectedItemType.ToSelectList())
[其中SelectedItemType是类型为ItemTypes的模型上的字段,并且您的模型为非null]
此外,您实际上不需要对扩展方法进行泛化,因为您可以使用enumValue.GetType()而不是typeof(T).
编辑:此处集成了Simon的解决方案,并包含ToDescription扩展方法.
public static class EnumExtensions { public static IEnumerableToSelectList(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(); } }
所以没有扩展功能,如果你正在寻找简单和容易..这就是我做的
<%= Html.DropDownListFor(x => x.CurrentAddress.State, new SelectList(Enum.GetValues(typeof(XXXXX.Sites.YYYY.Models.State))))%>
其中XXXXX.Sites.YYYY.Models.State是一个枚举
可能更好地做辅助功能,但是当时间很短时,这将完成工作.
扩展Prize和Rune的答案,如果您希望将选择列表项的value属性映射到Enumeration类型的整数值,而不是字符串值,请使用以下代码:
public static SelectList ToSelectList(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的基本类型),以及运行时类型验证,以确保传入的结构确实是一个枚举.
更新10/23/12: 为影响.NET 4+的基础类型和修复的非编译问题添加了泛型类型参数.
使用Prize的扩展方法解决获取数字而不是文本的问题.
public static SelectList ToSelectList(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); }
我找到的最佳解决方案是将此博客与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 NullableCuriousQuestion{ get; set; } public virtual Nullable CuriousQuestionId { get { return (Nullable )CuriousQuestion; } set { CuriousQuestion = (Nullable )value; } }
视图:
@using MyProject.Extensions { //... @Html.EnumDropDownListFor(model => model.CuriousQuestion) //... }
扩展(直接来自Simon Goldstone的答案,包含在这里是为了完整性):
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 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 (this HtmlHelper htmlHelper, Expression > expression) { return EnumDropDownListFor(htmlHelper, expression, null); } public static MvcHtmlString EnumDropDownListFor (this HtmlHelper htmlHelper, Expression > expression, object htmlAttributes) { ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); Type enumType = GetNonNullableModelType(metadata); IEnumerable values = Enum.GetValues(enumType).Cast (); IEnumerable 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); } } }
完成这项工作的一种非常简单的方法 - 没有看起来有点过分的所有扩展内容是这样的:
你的枚举:
public enum SelectedLevel { Level1, Level2, Level3, Level4 }
在控制器内部将Enum绑定到List:
ListmyLevels = Enum.GetValues(typeof(SelectedLevel)).Cast ().ToList();
之后将其抛入ViewBag:
ViewBag.RequiredLevel = new SelectList(myLevels);
最后只需将其绑定到View:
@Html.DropDownList("selectedLevel", (SelectList)ViewBag.RequiredLevel, new { @class = "form-control" })
这是迄今为止我找到的最简单的方法,不需要任何扩展或任何疯狂的东西.
更新:请参阅下面的安德鲁斯评论.
你想看看使用类似的东西 Enum.GetValues
@Html.DropDownListFor(model => model.Type, Enum.GetNames(typeof(Rewards.Models.PropertyType)).Select(e => new SelectListItem { Text = e }))
这是Rune&Prize答案被更改为使用Enum int
值作为ID.
样本枚举:
public enum ItemTypes { Movie = 1, Game = 2, Book = 3 }
扩展方法:
public static SelectList ToSelectList(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()) %>
请记住导入包含Extension方法的命名空间
<%@ Import Namespace="MyNamespace.LocationOfExtensionMethod" %>
生成的HTML示例:
请注意,用于调用ToSelectList
on的项目是所选项目.
这是Razor的版本:
@{ var itemTypesList = new List(); itemTypesList.AddRange(Enum.GetValues(typeof(ItemTypes)).Cast ().Select( (item, index) => new SelectListItem { Text = item.ToString(), Value = (index).ToString(), Selected = Model.ItemTypeId == index }).ToList()); } @Html.DropDownList("ItemTypeId", itemTypesList)
现在,通过以下方式在MVC 5.1中可以立即使用此功能: @Html.EnumDropDownListFor()
检查以下链接:
https://docs.microsoft.com/zh-cn/aspnet/mvc/overview/releases/mvc51-release-notes#Enum
根据上面的投票,微软花了5年的时间来实现如此受欢迎的功能,真是太可惜了!