¿Cómo se vincula un Enum a un control DropDownList en ASP.NET?

126

Digamos que tengo la siguiente enumeración simple:

enum Response
{
    Yes = 1,
    No = 2,
    Maybe = 3
}

¿Cómo puedo vincular esta enumeración a un control DropDownList para que las descripciones se muestren en la lista, así como recuperar el valor numérico asociado (1,2,3) una vez que se ha seleccionado una opción?

Rayo
fuente

Respuestas:

112

Probablemente no vincularía los datos, ya que es una enumeración, y no cambiará después del tiempo de compilación (a menos que esté teniendo uno de esos momentos aburridos ).

Mejor solo iterar a través de la enumeración:

Dim itemValues As Array = System.Enum.GetValues(GetType(Response))
Dim itemNames As Array = System.Enum.GetNames(GetType(Response))

For i As Integer = 0 To itemNames.Length - 1
    Dim item As New ListItem(itemNames(i), itemValues(i))
    dropdownlist.Items.Add(item)
Next

O lo mismo en C #

Array itemValues = System.Enum.GetValues(typeof(Response));
Array itemNames = System.Enum.GetNames(typeof(Response));

for (int i = 0; i <= itemNames.Length - 1 ; i++) {
    ListItem item = new ListItem(itemNames[i], itemValues[i]);
    dropdownlist.Items.Add(item);
}
Mark Glorie
fuente
1
Muchas gracias por esta respuesta. GetType (Response) no funcionó para mí porque recibo una instancia de Enum, en lugar de la clase Enum. Entonces uso enumInstance.GetType () en su lugar.
Sebastian
2
Usar C # no me funciona, porque getValues ​​y getNames devuelven lo mismo, el primer objeto similar y el segundo como cadena. La definición de la enumeración es la siguiente: public enum eResult {Right = 1, NoncontrolledError = 2,}
Javiere
9
Por cierto, en C #, no puede acceder a Array con el índice itemNames [i], solo puede hacerlo con arrayObject.GetValue (i) y de esa manera, devuelve solo el nombre en ambos casos.
Javiere
1
Lo resolví mezclando esta solución con esta stackoverflow.com/questions/3213432/…
Javiere
55
¿Por qué esto tiene tantos votos a favor? El código (al menos c #) ahora funciona y contiene errores de sintaxis.
Dave
69

Utilice la siguiente clase de utilidad Enumerationpara obtener un IDictionary<int,string>(valor de Enum y par de nombre) de una enumeración ; luego vincula el IDictionary a un Control enlazable .

public static class Enumeration
{
    public static IDictionary<int, string> GetAll<TEnum>() where TEnum: struct
    {
        var enumerationType = typeof (TEnum);

        if (!enumerationType.IsEnum)
            throw new ArgumentException("Enumeration type is expected.");

        var dictionary = new Dictionary<int, string>();

        foreach (int value in Enum.GetValues(enumerationType))
        {
            var name = Enum.GetName(enumerationType, value);
            dictionary.Add(value, name);
        }

        return dictionary;
    }
}

Ejemplo: uso de la clase de utilidad para vincular datos de enumeración a un control

ddlResponse.DataSource = Enumeration.GetAll<Response>();
ddlResponse.DataTextField = "Value";
ddlResponse.DataValueField = "Key";
ddlResponse.DataBind();
Leyu
fuente
1
+1. Lo he usado, pero creo que la clave y el valor están al revés. Esto debería devolver un IDictionary <string, int>
Colin
Cabe señalar que esto no se comportará correctamente en todos los tipos de enumeración (como uint, ulong, long, etc.) Normalmente, el campo más eficiente para buscar es la clave. En este caso, ese sería el int ya que los enteros son una simple comparación <, =,> frente a la comparación <y> de una cadena para cada carácter.
Trisped
43

Lo uso para ASP.NET MVC :

Html.DropDownListFor(o => o.EnumProperty, Enum.GetValues(typeof(enumtype)).Cast<enumtype>().Select(x => new SelectListItem { Text = x.ToString(), Value = ((int)x).ToString() }))
Feryt
fuente
36

Mi versión es solo una forma comprimida de lo anterior:

foreach (Response r in Enum.GetValues(typeof(Response)))
{
    ListItem item = new ListItem(Enum.GetName(typeof(Response), r), r.ToString());
    DropDownList1.Items.Add(item);
}
VanOrman
fuente
44
debe ser (int r en Enum.GetValues ​​(typeof (Response))) o simplemente vinculará la descripción como el nombre y el valor ...
Evan
2
esto no funciona, ya que inserta el nombre del miembro de enumeración en el valor de ListItem. La conversión a int funcionaría en la mayoría de los casos, pero no si la enumeración es uint, ulong o long.
Trisped
Solución mucho mejor en mi humilde opinión.
Vippy
23
public enum Color
{
    RED,
    GREEN,
    BLUE
}

Cada tipo de Enum deriva de System.Enum. Hay dos métodos estáticos que ayudan a vincular datos a un control de lista desplegable (y recuperar el valor). Estos son Enum.GetNames y Enum.Parse. Con GetNames, puede vincular su control de lista desplegable de la siguiente manera:

protected System.Web.UI.WebControls.DropDownList ddColor;

private void Page_Load(object sender, System.EventArgs e)
{
     if(!IsPostBack)
     {
        ddColor.DataSource = Enum.GetNames(typeof(Color));
        ddColor.DataBind();
     }
}

Ahora, si desea que el valor de Enum vuelva a la selección ...

  private void ddColor_SelectedIndexChanged(object sender, System.EventArgs e)
  {
    Color selectedColor = (Color)Enum.Parse(typeof(Color),ddColor.SelectedValue
  }
p.campbell
fuente
2
buena respuesta pero un pequeño consejo: Color selectedColor = (Color) Enum.Parse (typeof (Color), ddColor.SelectedValue);
sma6871
11

Después de leer todas las publicaciones, se me ocurrió una solución integral para admitir mostrar la descripción de enumeración en la lista desplegable, así como seleccionar el valor adecuado del Modelo en el menú desplegable cuando se muestra en modo Editar:

enumeración:

using System.ComponentModel;
public enum CompanyType
{
    [Description("")]
    Null = 1,

    [Description("Supplier")]
    Supplier = 2,

    [Description("Customer")]
    Customer = 3
}

clase de extensión enum:

using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web.Mvc;

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

    public static IEnumerable<SelectListItem> ToSelectList<T>(this System.Enum enumValue)
    {
        return
            System.Enum.GetValues(enumValue.GetType()).Cast<T>()
                  .Select(
                      x =>
                      new SelectListItem
                          {
                              Text = ((System.Enum)(object) x).ToDescription(),
                              Value = x.ToString(),
                              Selected = (enumValue.Equals(x))
                          });
    }
}

Clase de modelo:

public class Company
{
    public string CompanyName { get; set; }
    public CompanyType Type { get; set; }
}

y Ver:

@Html.DropDownListFor(m => m.Type,
@Model.Type.ToSelectList<CompanyType>())

y si está usando ese menú desplegable sin vincular al Modelo, puede usar esto en su lugar:

@Html.DropDownList("type",                  
Enum.GetValues(typeof(CompanyType)).Cast<CompanyType>()
.Select(x => new SelectListItem {Text = x.ToDescription(), Value = x.ToString()}))

Al hacerlo, puede esperar que su menú desplegable muestre Descripción en lugar de valores de enumeración. Además, cuando se trata de Editar, su modelo se actualizará mediante el valor seleccionado desplegable después de publicar la página.

Amir Chatrbahr
fuente
1
Bien hecho, especialmente el bit con las anotaciones [Descripción]. Adoptaré esta técnica.
Baxter
Explicación ordenada y limpia. Kudos Amir !!
8

Como ya han dicho otros, no se vinculen datos a una enumeración, a menos que necesite vincularse a enumeraciones diferentes según la situación. Hay varias formas de hacer esto, un par de ejemplos a continuación.

ObjectDataSource

Una forma declarativa de hacerlo con ObjectDataSource. Primero, cree una clase BusinessObject que devolverá la Lista para vincular DropDownList a:

public class DropDownData
{
    enum Responses { Yes = 1, No = 2, Maybe = 3 }

    public String Text { get; set; }
    public int Value { get; set; }

    public List<DropDownData> GetList()
    {
        var items = new List<DropDownData>();
        foreach (int value in Enum.GetValues(typeof(Responses)))
        {
            items.Add(new DropDownData
                          {
                              Text = Enum.GetName(typeof (Responses), value),
                              Value = value
                          });
        }
        return items;
    }
}

Luego agregue un marcado HTML a la página ASPX para apuntar a esta clase BO:

<asp:DropDownList ID="DropDownList1" runat="server" 
    DataSourceID="ObjectDataSource1" DataTextField="Text" DataValueField="Value">
</asp:DropDownList>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" 
    SelectMethod="GetList" TypeName="DropDownData"></asp:ObjectDataSource>

Esta opción no requiere código detrás.

Código detrás de DataBind

Para minimizar el HTML en la página ASPX y enlazar en Code Behind:

enum Responses { Yes = 1, No = 2, Maybe = 3 }

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        foreach (int value in Enum.GetValues(typeof(Responses)))
        {
            DropDownList1.Items.Add(new ListItem(Enum.GetName(typeof(Responses), value), value.ToString()));
        }
    }
}

De todos modos, el truco es dejar que los métodos de tipo Enum de GetValues, GetNames, etc. trabajen para usted.

Johan Danforth
fuente
6

No estoy seguro de cómo hacerlo en ASP.NET, pero mira esta publicación ... ¿podría ayudar?

Enum.GetValues(typeof(Response));
rudigrobler
fuente
6

Podrías usar linq:

var responseTypes= Enum.GetNames(typeof(Response)).Select(x => new { text = x, value = (int)Enum.Parse(typeof(Response), x) });
    DropDownList.DataSource = responseTypes;
    DropDownList.DataTextField = "text";
    DropDownList.DataValueField = "value";
    DropDownList.DataBind();
KrishnaDhungana
fuente
5
Array itemValues = Enum.GetValues(typeof(TaskStatus));
Array itemNames = Enum.GetNames(typeof(TaskStatus));

for (int i = 0; i <= itemNames.Length; i++)
{
    ListItem item = new ListItem(itemNames.GetValue(i).ToString(),
    itemValues.GetValue(i).ToString());
    ddlStatus.Items.Add(item);
}
John Willemse
fuente
4
public enum Color
{
    RED,
    GREEN,
    BLUE
}

ddColor.DataSource = Enum.GetNames(typeof(Color));
ddColor.DataBind();
sankalp gurha
fuente
3

Código genérico usando la respuesta seis.

public static void BindControlToEnum(DataBoundControl ControlToBind, Type type)
{
    //ListControl

    if (type == null)
        throw new ArgumentNullException("type");
    else if (ControlToBind==null )
        throw new ArgumentNullException("ControlToBind");
    if (!type.IsEnum)
        throw new ArgumentException("Only enumeration type is expected.");

    Dictionary<int, string> pairs = new Dictionary<int, string>();

    foreach (int i in Enum.GetValues(type))
    {
        pairs.Add(i, Enum.GetName(type, i));
    }
    ControlToBind.DataSource = pairs;
    ListControl lstControl = ControlToBind as ListControl;
    if (lstControl != null)
    {
        lstControl.DataTextField = "Value";
        lstControl.DataValueField = "Key";
    }
    ControlToBind.DataBind();

}
Muhammed Qasim
fuente
3

Después de encontrar esta respuesta, se me ocurrió lo que creo que es una forma mejor (al menos más elegante) de hacer esto, pensé en volver y compartirlo aquí.

Page_Load:

DropDownList1.DataSource = Enum.GetValues(typeof(Response));
DropDownList1.DataBind();

Valores de carga:

Response rIn = Response.Maybe;
DropDownList1.Text = rIn.ToString();

Guardar valores:

Response rOut = (Response) Enum.Parse(typeof(Response), DropDownList1.Text);
Ben Hughes
fuente
2

Esta es probablemente una vieja pregunta ... pero así es como hice la mía.

Modelo:

public class YourEntity
{
   public int ID { get; set; }
   public string Name{ get; set; }
   public string Description { get; set; }
   public OptionType Types { get; set; }
}

public enum OptionType
{
    Unknown,
    Option1, 
    Option2,
    Option3
}

Luego, en la Vista: aquí se explica cómo usar el menú desplegable.

@Html.EnumDropDownListFor(model => model.Types, htmlAttributes: new { @class = "form-control" })

Esto debería llenar todo en su lista de enumeraciones. Espero que esto ayude..

Marie McDonley
fuente
Sin embargo, esto funciona, necesita una clase de extensión si desea incorporar literales de cadena con espacios.
1
Esta es la mejor respuesta. @Nikul, no necesitas una clase de extensión. Solo necesitas usar anotaciones. [Display(Name = "Option number one")] Option1,
rooter
1

¿Por qué no usarlo así para poder pasar cada lista?


public static void BindToEnum(Type enumType, ListControl lc)
        {
            // get the names from the enumeration
            string[] names = Enum.GetNames(enumType);
            // get the values from the enumeration
            Array values = Enum.GetValues(enumType);
            // turn it into a hash table
            Hashtable ht = new Hashtable();
            for (int i = 0; i < names.Length; i++)
                // note the cast to integer here is important
                // otherwise we'll just get the enum string back again
                ht.Add(names[i], (int)values.GetValue(i));
            // return the dictionary to be bound to
            lc.DataSource = ht;
            lc.DataTextField = "Key";
            lc.DataValueField = "Value";
            lc.DataBind();
        }
Y el uso es tan simple como:

BindToEnum(typeof(NewsType), DropDownList1);
BindToEnum(typeof(NewsType), CheckBoxList1);
BindToEnum(typeof(NewsType), RadoBuuttonList1);

Mostafa
fuente
1

Desde entonces, ASP.NET se ha actualizado con algunas funcionalidades más, y ahora puede usar la enumeración integrada para desplegar.

Si quieres unirte al Enum, usa esto:

@Html.DropDownList("response", EnumHelper.GetSelectList(typeof(Response)))

Si está vinculando una instancia de Response, use esto:

// Assuming Model.Response is an instance of Response
@Html.EnumDropDownListFor(m => m.Response)
bradlis7
fuente
0

Esta es mi solución para ordenar un Enum y DataBind (texto y valor) para desplegar usando LINQ

var mylist = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>().ToList<MyEnum>().OrderBy(l => l.ToString());
foreach (MyEnum item in mylist)
    ddlDivisao.Items.Add(new ListItem(item.ToString(), ((int)item).ToString()));
Diego Mendes
fuente
0

Si desea tener una descripción más fácil de usar en su cuadro combinado (u otro control), puede usar el atributo Descripción con la siguiente función:

    public static object GetEnumDescriptions(Type enumType)
    {
        var list = new List<KeyValuePair<Enum, string>>();
        foreach (Enum value in Enum.GetValues(enumType))
        {
            string description = value.ToString();
            FieldInfo fieldInfo = value.GetType().GetField(description);
            var attribute = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false).First();
            if (attribute != null)
            {
                description = (attribute as DescriptionAttribute).Description;
            }
            list.Add(new KeyValuePair<Enum, string>(value, description));
        }
        return list;
    }

Aquí hay un ejemplo de una enumeración con atributos de Descripción aplicados:

    enum SampleEnum
    {
        NormalNoSpaces,
        [Description("Description With Spaces")]
        DescriptionWithSpaces,
        [Description("50%")]
        Percent_50,
    }

Entonces Bind para controlar así ...

        m_Combo_Sample.DataSource = GetEnumDescriptions(typeof(SampleEnum));
        m_Combo_Sample.DisplayMember = "Value";
        m_Combo_Sample.ValueMember = "Key";

De esta forma, puede colocar el texto que desee en el menú desplegable sin que tenga que verse como un nombre de variable

Josh Stribling
fuente
0

También puede usar métodos de extensión. Para aquellos que no están familiarizados con las extensiones, sugiero consultar la documentación de VB y C # .


Extensión VB:

Namespace CustomExtensions
    Public Module ListItemCollectionExtension

        <Runtime.CompilerServices.Extension()> _
        Public Sub AddEnum(Of TEnum As Structure)(items As System.Web.UI.WebControls.ListItemCollection)
            Dim enumerationType As System.Type = GetType(TEnum)
            Dim enumUnderType As System.Type = System.Enum.GetUnderlyingType(enumType)

            If Not enumerationType.IsEnum Then Throw New ArgumentException("Enumeration type is expected.")

            Dim enumTypeNames() As String = System.Enum.GetNames(enumerationType)
            Dim enumTypeValues() As TEnum = System.Enum.GetValues(enumerationType)

            For i = 0 To enumTypeNames.Length - 1
                items.Add(New System.Web.UI.WebControls.ListItem(saveResponseTypeNames(i), TryCast(enumTypeValues(i), System.Enum).ToString("d")))
            Next
        End Sub
    End Module
End Namespace

Para usar la extensión:

Imports <projectName>.CustomExtensions.ListItemCollectionExtension

...

yourDropDownList.Items.AddEnum(Of EnumType)()

Extensión C #:

namespace CustomExtensions
{
    public static class ListItemCollectionExtension
    {
        public static void AddEnum<TEnum>(this System.Web.UI.WebControls.ListItemCollection items) where TEnum : struct
        {
            System.Type enumType = typeof(TEnum);
            System.Type enumUnderType = System.Enum.GetUnderlyingType(enumType);

            if (!enumType.IsEnum) throw new Exception("Enumeration type is expected.");

            string[] enumTypeNames = System.Enum.GetNames(enumType);
            TEnum[] enumTypeValues = (TEnum[])System.Enum.GetValues(enumType);

            for (int i = 0; i < enumTypeValues.Length; i++)
            {
                items.add(new System.Web.UI.WebControls.ListItem(enumTypeNames[i], (enumTypeValues[i] as System.Enum).ToString("d")));
            }
        }
    }
}

Para usar la extensión:

using CustomExtensions.ListItemCollectionExtension;

...

yourDropDownList.Items.AddEnum<EnumType>()

Si desea configurar el elemento seleccionado al mismo tiempo, reemplace

items.Add(New System.Web.UI.WebControls.ListItem(saveResponseTypeNames(i), saveResponseTypeValues(i).ToString("d")))

con

Dim newListItem As System.Web.UI.WebControls.ListItem
newListItem = New System.Web.UI.WebControls.ListItem(enumTypeNames(i), Convert.ChangeType(enumTypeValues(i), enumUnderType).ToString())
newListItem.Selected = If(EqualityComparer(Of TEnum).Default.Equals(selected, saveResponseTypeValues(i)), True, False)
items.Add(newListItem)

Al convertir a System.Enum en lugar de tamaño int y problemas de salida se evitan. Por ejemplo, 0xFFFF0000 sería 4294901760 como uint pero sería -65536 como int.

TryCast y como System.Enum son ligeramente más rápidos que Convert.ChangeType (enumTypeValues ​​[i], enumUnderType) .ToString () (12:13 en mis pruebas de velocidad).

Trisped
fuente
0

La solución aceptada no funciona, pero el código a continuación ayudará a otros a buscar la solución más corta.

 foreach (string value in Enum.GetNames(typeof(Response)))
                    ddlResponse.Items.Add(new ListItem()
                    {
                        Text = value,
                        Value = ((int)Enum.Parse(typeof(Response), value)).ToString()
                    });
Hakan
fuente
0

Puedes hacer esto mucho más corto

public enum Test
    {
        Test1 = 1,
        Test2 = 2,
        Test3 = 3
    }
    class Program
    {
        static void Main(string[] args)
        {

            var items = Enum.GetValues(typeof(Test));

            foreach (var item in items)
            {
                //Gives you the names
                Console.WriteLine(item);
            }


            foreach(var item in (Test[])items)
            {
                // Gives you the numbers
                Console.WriteLine((int)item);
            }
        }
    }
Henkie85
fuente