How do you create a dropdownlist from an enum in ASP.NET MVC?

后端 未结 30 1872
不知归路
不知归路 2020-11-21 16:36

I\'m trying to use the Html.DropDownList extension method but can\'t figure out how to use it with an enumeration.

Let\'s say I have an enumeration like

相关标签:
30条回答
  • 2020-11-21 16:54

    Building on Simon's answer, a similar approach is to get the Enum values to display from a Resource file, instead of in a description attribute within the Enum itself. This is helpful if your site needs to be rendered in more than one language and if you were to have a specific resource file for Enums, you could go one step further and have just Enum values, in your Enum and reference them from the extension by a convention such as [EnumName]_[EnumValue] - ultimately less typing!

    The extension then looks like:

    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();
    }
    

    Resources in the Enums.Resx file looking like ItemTypes_Movie : Film

    One other thing I like to do is, instead of calling the extension method directly, I'd rather call it with a @Html.EditorFor(x => x.MyProperty), or ideally just have the whole form, in one neat @Html.EditorForModel(). To do this I change the string template to look like this

    @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))
    }
    

    If this interests you, I've put a much more detailed answer here on my blog:

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

    0 讨论(0)
  • 2020-11-21 16:54

    If you want to add localization support just change the s.toString() method to something like this:

    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()) };
    

    In here the typeof(Resources) is the resource you want to load, and then you get the localized String, also useful if your enumerator has values with multiple words.

    0 讨论(0)
  • 2020-11-21 16:56

    Here is a better encapsulated solution:

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

    Say here is your model:

    enter image description here

    Sample Usage:

    enter image description here

    Generated UI: enter image description here

    And generated HTML

    enter image description here

    The Helper Extension Source Code snap shot:

    enter image description here

    You can download the sample project from the link I provided.

    EDIT: Here's the code:

    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();
        }
    }
    
    0 讨论(0)
  • 2020-11-21 16:56

    To solve the problem of getting the number instead of text using Prise's extension method.

    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);
    }
    
    0 讨论(0)
  • 2020-11-21 16:56

    The best solution I found for this was combining this blog with Simon Goldstone's answer.

    This allows use of the enum in the model. Essentially the idea is to use an integer property as well as the enum, and emulate the integer property.

    Then use the [System.ComponentModel.Description] attribute for annotating the model with your display text, and use an "EnumDropDownListFor" extension in your view.

    This makes both the view and model very readable and maintainable.

    Model:

    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; }
    }
    

    View:

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

    Extension (directly from Simon Goldstone's answer, included here for completeness):

    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);
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-21 16:57

    For MVC v5.1 use Html.EnumDropDownListFor

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

    For MVC v5 use EnumHelper

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

    For MVC 5 and lower

    I rolled Rune's answer into an extension method:

    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);
            }
        }
    }
    

    This allows you to write:

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

    by using MyApp.Common

    0 讨论(0)
提交回复
热议问题