Pregunta ¿Cómo se crea una lista desplegable a partir de una enumeración en ASP.NET MVC?


Estoy tratando de usar el Html.DropDownList método de extensión, pero no puede encontrar la forma de usarlo con una enumeración.

Digamos que tengo una enumeración como esta:

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

¿Cómo hago para crear un menú desplegable con estos valores usando el Html.DropDownList método de extensión?

¿O es mi mejor opción simplemente crear un bucle for y crear los elementos Html manualmente?


586


origen


Respuestas:


Para MVC v5.1 use Html.EnumDropDownListFor

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

Para MVC v5 usa EnumHelper

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

Para MVC 5 y menor

Puse la respuesta de Rune en un método de extensión:

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

Esto te permite escribir:

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

por using MyApp.Common


732



Sé que llego tarde a la fiesta, pero pensé que esta variante podría resultarle útil, ya que esta también le permite usar cadenas descriptivas en lugar de constantes de enumeración en el menú desplegable. Para ello, decore cada entrada de enumeración con un atributo [System.ComponentModel.Description].

Por ejemplo:

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

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

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

Aquí está mi código:

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

A continuación, puede hacer esto en su vista:

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

¡Espero que esto te ayude!

EDIT 2014-ENE-23: Microsoft acaba de lanzar MVC 5.1, que ahora tiene una función EnumDropDownListFor. Lamentablemente, no parece respetar el atributo [Descripción] por lo que el código anterior sigue en pie. (Ver http://www.asp.net/mvc/overview/releases/mvc51-release-notes#Enum para las notas de la versión de Microsoft).

Actualización: Sí admite el Monitor atributo [Display(Name = "Sample")] sin embargo, entonces uno puede usar eso.

[Actualización: solo noté esto, y el código parece una versión extendida del código aquí: http://blogs.msdn.com/b/stuartleeks/archive/2010/05/21/asp-net-mvc-creating-a-dropdownlist-helper-for-enums.aspx, con un par de adiciones Si es así, la atribución parecería justa ;-)]


335



En ASP.NET MVC 5.1, agregaron el EnumDropDownListFor() ayudante, por lo que no es necesario extensiones personalizadas:

Modelo:

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

Ver:

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

Usando Tag Helper (ASP.NET MVC 6):

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

172



Me encontré con el mismo problema, encontré esta pregunta, y pensé que la solución provista por Ash no era lo que estaba buscando; Tener que crear el HTML por mí mismo significa menos flexibilidad en comparación con el built-in Html.DropDownList() función.

Resulta C # 3 etc. hace esto bastante fácil. Yo tengo un enum llamado 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);

Esto crea un buen ol ' SelectList que se puede usar como si estuvieras acostumbrado en la vista:

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

El tipo anónimo y LINQ lo hace mucho más elegante en mi humilde opinión. Sin ofender, Ash. :)


122



Aquí hay una mejor solución encapsulada:

http://www.spicelogic.com/Journal/ASP-NET-MVC-DropDownListFor-Html-Helper-Enum-5

Diga, aquí está su modelo:

enter image description here

Uso de la muestra:

enter image description here

UI generada: enter image description here

Y HTML generado

enter image description here

La foto instantánea del código fuente de la extensión de ayudante:

enter image description here

Puede descargar el proyecto de muestra desde el enlace que proporcioné.

EDITAR: Aquí está el código:

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

53



Html.DropDownListFor solo requiere un IEnumerable, por lo que una alternativa a la solución de Prize es la siguiente. Esto te permitirá simplemente escribir:

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

[Donde SelectedItemType es un campo en su modelo de tipo ItemTypes, y su modelo no es nulo]

Además, no es necesario genizar el método de extensión, ya que puede usar enumValue.GetType () en lugar de typeof (T).

EDITAR: Aquí también se incluye la solución integrada de Simon e incluye el método de extensión 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();
    }
}

46



Así que sin las funciones de extensión si buscas simple y fácil ... Esto es lo que hice

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

donde XXXXX.Sites.YYYY.Models.State es una enumeración

Probablemente sea mejor hacer la función auxiliar, pero cuando el tiempo es corto esto hará el trabajo.


31



Ampliando las respuestas de Prune y Rune, si desea que el atributo value de sus elementos de la lista de selección se asocie al valor entero del tipo Enumeration, en lugar del valor de la cadena, use el siguiente código:

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

En lugar de tratar cada valor de enumeración como un objeto TEnum, podemos tratarlo como un objeto y luego convertirlo en un número entero para obtener el valor no incluido.

Nota: También agregué una restricción de tipo genérico para restringir los tipos para los que esta extensión está disponible solo para estructuras (tipo de base de Enum), y una validación de tipo de tiempo de ejecución que asegura que la estructura pasada es en realidad un Enum.

Actualización 10/23/12: Se agregó un parámetro de tipo genérico para el tipo subyacente y un problema fijo de no compilación que afecta a .NET 4+.


21