600 votos

¿Cómo se crean un dropdownlist de enum en ASP.NET MVC?

Yo estoy tratando de utilizar el método de extensión Html.DropDownList pero no se puede averiguar cómo usarlo con una enumeración.

Digamos que tengo una enumeración como este:

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

¿Cómo voy sobre la creación de un menú desplegable con estos valores utilizando el método de extensión Html.DropDownList? ¿O es mi mejor apuesta para crear simplemente un para de bucle y crear manualmente los elementos html?

746voto

Martin Faartoft Puntos 3250

Rodé 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 le permite escribir:

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

porusing MyApp.Common

340voto

SimonGoldstone Puntos 2901

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

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

Usted puede hacer esto en su punto de vista:

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

Espero que esto ayude!

EDICIÓN de 2014-JAN-23: Microsoft acaba de lanzar MVC 5.1, que ahora dispone de un EnumDropDownListFor característica. Lamentablemente no parece respetar el [Descripción] atributo para el código de arriba sigue en pie. (Ver http://www.asp.net/mvc/overview/releases/mvc51-release-notes#Enum para el lanzamiento de Microsoft notas).

Actualización: Se admite el DisplyName atributo [Display(Name = "Sample")] , aunque, así que se puede utilizar.

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

122voto

Rune Jacobsen Puntos 3621

Me encontré con el mismo problema, encontró esta cuestión, y pensó que la solución proporcionada por la Ceniza no era lo que yo estaba buscando; la necesidad de crear el código HTML a mí mismo significa menos flexibilidad en comparación con la incorporada en el Html.DropDownList() función.

Resulta C#3, etc. hace esto muy fácil. Tengo un Enum llamada 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 puede ser utilizada como usted está acostumbrado a que en la vista:

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

El tipo anónimo y LINQ hace esto de forma mucho más elegante en mi humilde opinión. No te lo tomes a mal, Ceniza. :)

53voto

Emran Hussain Puntos 1196

Esta es una mejor encapsulado solución:

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

Dicen que aquí es su número de modelo:

enter image description here

Ejemplo De Uso:

enter image description here

Genera interfaz de usuario: enter image description here

Y el HTML generado

enter image description here

El Ayudante Código Fuente de la Extensión snap shot:

enter image description here

Usted puede descargar el proyecto de ejemplo desde el enlace que te puse.

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

46voto

Zaid Masud Puntos 4536

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

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

[Donde SelectedItemType es un campo en el modelo de tipo de ItemTypes, y su modelo es distinto de null]

También, usted realmente no necesita genericize el método de extensión como se puede utilizar enumValue.GetType() en lugar de typeof(T).

EDIT: Integrado Simón solución aquí incluido ToDescription método de extensión.

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

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by: