MVC3 Razor DropDownListFor Enums

84

Intento actualizar mi proyecto a MVC3, algo que simplemente no puedo encontrar:

Tengo un tipo de datos simple de ENUMS:

public enum States()
{
  AL,AK,AZ,...WY
}

Que quiero usar como DropDown / SelectList en mi vista de un modelo que contiene este tipo de datos:

public class FormModel()
{
    public States State {get; set;}
}

Bastante sencillo: cuando uso la vista de generación automática para esta clase parcial, ignora este tipo.

Necesito una lista de selección simple que establezca el valor de la enumeración como el elemento seleccionado cuando presiono enviar y procesar a través de mi método AJAX - JSON POST.

Y que la vista (???!):

    <div class="editor-field">
        @Html.DropDownListFor(model => model.State, model => model.States)
    </div>

¡Gracias de antemano por el consejo!

jordan.baucke
fuente
8
Para cualquiera que se encuentre con este hilo y esté usando MVC 5.1 o superior, el método de ayuda @ Html.EnumDropDownListFor () ahora está integrado en MVC - vea asp.net/mvc/overview/releases/mvc51-release-notes
mecsco

Respuestas:

55

Acabo de hacer uno para mi propio proyecto. El siguiente código es parte de mi clase auxiliar, espero tener todos los métodos necesarios. Escribe un comentario si no funciona y lo comprobaré de nuevo.

public static class SelectExtensions
{

    public static string GetInputName<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
    {
        if (expression.Body.NodeType == ExpressionType.Call)
        {
            MethodCallExpression methodCallExpression = (MethodCallExpression)expression.Body;
            string name = GetInputName(methodCallExpression);
            return name.Substring(expression.Parameters[0].Name.Length + 1);

        }
        return expression.Body.ToString().Substring(expression.Parameters[0].Name.Length + 1);
    }

    private static string GetInputName(MethodCallExpression expression)
    {
        // p => p.Foo.Bar().Baz.ToString() => p.Foo OR throw...
        MethodCallExpression methodCallExpression = expression.Object as MethodCallExpression;
        if (methodCallExpression != null)
        {
            return GetInputName(methodCallExpression);
        }
        return expression.Object.ToString();
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) where TModel : class
    {
        string inputName = GetInputName(expression);
        var value = htmlHelper.ViewData.Model == null
            ? default(TProperty)
            : expression.Compile()(htmlHelper.ViewData.Model);

        return htmlHelper.DropDownList(inputName, ToSelectList(typeof(TProperty), value.ToString()));
    }

    public static SelectList ToSelectList(Type enumType, string selectedItem)
    {
        List<SelectListItem> items = new List<SelectListItem>();
        foreach (var item in Enum.GetValues(enumType))
        {
            FieldInfo fi = enumType.GetField(item.ToString());
            var attribute = fi.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
            var title = attribute == null ? item.ToString() : ((DescriptionAttribute)attribute).Description;
            var listItem = new SelectListItem
                {
                    Value = ((int)item).ToString(),
                    Text = title,
                    Selected = selectedItem == ((int)item).ToString()
                };
            items.Add(listItem);
        }

        return new SelectList(items, "Value", "Text", selectedItem);
    }
}

Úselo como:

Html.EnumDropDownListFor(m => m.YourEnum);

Actualizar

He creado Html Helpers alternativos. Todo lo que necesita hacer para usarlos es cambiar su página de vista base enviews\web.config .

Con ellos puedes simplemente hacer:

@Html2.DropDownFor(m => m.YourEnum);
@Html2.CheckboxesFor(m => m.YourEnum);
@Html2.RadioButtonsFor(m => m.YourEnum);

Más información aquí: http://blog.gauffin.org/2011/10/first-draft-of-my-alternative-html-helpers/

jgauffin
fuente
1
Bien, funciona de cualquier manera, solo obtengo un error de compilación: Línea 41: return htmlHelper.DropDownList (inputName, ToSelectList (typeof (TProperty), value.ToString ())); 'System.Web.Mvc.HtmlHelper <TModel>' no contiene una definición para 'DropDownList' y no se pudo encontrar ningún método de extensión 'DropDownList' aceptando un primer argumento de tipo 'System.Web.Mvc.HtmlHelper <TModel>' ( ¿Le falta una directiva de uso o una referencia de ensamblado?)
jordan.baucke
1
@jordan tengo el mismo error. ¿Conseguiste solucionar el problema?
SF Developer
9
@filu @jordan agregue using System.Web.Mvc.Html;lo que necesite para acceder alSelectExtensionsClass
Simon Hartcher
3
@Para tengo el mismo problema, el valor seleccionado no aparece seleccionado en la Vista. (Tuve que cambiar ((int)item).ToString()para Enum.GetName(enumType, item)obtener el SelectListItemguardado correctamente como seleccionado, pero aún no funciona)
Fernando Neira
1
Acabo de agregar una respuesta a continuación que cubre el problema de la selección: se debe a una mala comprensión de los comportamientos de las sobrecargas de DropDownList.
Jon Egerton
199

Encontré una solución más simple para esto aquí: http://coding-in.net/asp-net-mvc-3-method-extension/

using System;
using System.Linq.Expressions;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace EnumHtmlHelper.Helper
{    
    public static class EnumDropDownList
    {
        public static HtmlString EnumDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> modelExpression, string firstElement)
        {
            var typeOfProperty = modelExpression.ReturnType;
            if(!typeOfProperty.IsEnum)
                throw new ArgumentException(string.Format("Type {0} is not an enum", typeOfProperty));     
            var enumValues = new SelectList(Enum.GetValues(typeOfProperty));
            return htmlHelper.DropDownListFor(modelExpression, enumValues, firstElement);
}   }   }

Una línea de navaja lo hará:

@Html.DropDownListFor(model => model.State, new SelectList(Enum.GetValues(typeof(MyNamespace.Enums.States))))

También puede encontrar código para hacerlo con un método de extensión en el artículo vinculado.

Mike McLaughlin
fuente
6
Creo que este debería haber sido marcado como la solución. Lo mejor no está marcado por la complejidad sino por la sencillez.
Lord of Scripts
3
Para las personas que buscan una versión de DropDowList (como yo): @ Html.DropDownList ("listName", new SelectList (Enum.GetValues ​​(typeof (MyNamespace.Enums.States))))
dstr
2
@JonEgerton Si te refieres a lo mismo que yo, estoy de acuerdo. Si desea mostrar enumeraciones + descripción + una imagen, está perdido con la solución de Mike McLaughlin.
Elisabeth
1
El único problema que he encontrado con estas soluciones es que no asigna correctamente el valor seleccionado al cargarlo. Aparte de eso, bastante bien.
triangulito
3
@triangulito esto no es un problema en absoluto :)@Html.DropDownListFor(model => model.State, new SelectList(Enum.GetValues(typeof(MyNamespace.Enums.States)),model.State))
VladL
24

A partir de ASP.NET MVC 5.1 (RC1) , EnumDropDownListForse incluye de forma predeterminada como método de extensión de HtmlHelper.

Vincent Sels
fuente
17

Si desea algo realmente simple, hay otra forma, dependiendo de cómo almacene el estado en la base de datos.

Si tuvieras una entidad como esta:

public class Address
{
     //other address fields

     //this is what the state gets stored as in the db
     public byte StateCode { get; set; }

     //this maps our db field to an enum
     public States State
     {
         get
         {
             return (States)StateCode;
         }
         set
         {
             StateCode = (byte)value;
         }
     }
}

Entonces generar el menú desplegable sería tan fácil como esto:

@Html.DropDownListFor(x => x.StateCode,
    from State state in Enum.GetValues(typeof(States))
    select new SelectListItem() { Text = state.ToString(), Value = ((int)state).ToString() }
);

¿No es LINQ bonito?

sjmeverett
fuente
¿Dónde se define la enumeración de estados en el modelo o en la vista?
superartsy
en el modelo, como lo usa la clase de modelo
sjmeverett
1
@stewartml Cuando mi ViewModel tiene la propiedad enum + el "SelectedCodeProperty", entonces esta es una propiedad demasiado en su publicación. ¿Por qué no hacer que la enumeración en ambos como valor seleccionado se envíe al servidor + como valor del elemento?
Elisabeth
13

Pude hacer esto en una sola línea.

@Html.DropDownListFor(m=>m.YourModelProperty,new SelectList(Enum.GetValues(typeof(YourEnumType))))
JM1990
fuente
8

Basado en la respuesta aceptada por @jgauffin, he creado mi propia versión de EnumDropDownListFor , que trata el problema de seleccionar elementos.

El problema se detalla en otra respuesta SO aquí :, y se debe básicamente a un malentendido del comportamiento de las diferentes sobrecargas deDropDownList .

Mi código completo (que incluye sobrecargas para htmlAttributesetc. es:

public static class EnumDropDownListForHelper
{

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression
        ) where TModel : class
    {
        return EnumDropDownListFor<TModel, TProperty>(
                            htmlHelper, expression, null, null);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression, 
            object htmlAttributes
        ) where TModel : class
    {
        return EnumDropDownListFor<TModel, TProperty>(
                            htmlHelper, expression, null, htmlAttributes);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression, 
            IDictionary<string, object> htmlAttributes
        ) where TModel : class
    {
        return EnumDropDownListFor<TModel, TProperty>(
                            htmlHelper, expression, null, htmlAttributes);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression, 
            string optionLabel
        ) where TModel : class
    {
        return EnumDropDownListFor<TModel, TProperty>(
                            htmlHelper, expression, optionLabel, null);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression, 
            string optionLabel, 
            IDictionary<string,object> htmlAttributes
        ) where TModel : class
    {
        string inputName = GetInputName(expression);
        return htmlHelper.DropDownList(
                            inputName, ToSelectList(typeof(TProperty)), 
                            optionLabel, htmlAttributes);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression, 
            string optionLabel, 
            object htmlAttributes
        ) where TModel : class
    {
        string inputName = GetInputName(expression);
        return htmlHelper.DropDownList(
                            inputName, ToSelectList(typeof(TProperty)), 
                            optionLabel, htmlAttributes);
    }


    private static string GetInputName<TModel, TProperty>(
            Expression<Func<TModel, TProperty>> expression)
    {
        if (expression.Body.NodeType == ExpressionType.Call)
        {
            MethodCallExpression methodCallExpression 
                            = (MethodCallExpression)expression.Body;
            string name = GetInputName(methodCallExpression);
            return name.Substring(expression.Parameters[0].Name.Length + 1);

        }
        return expression.Body.ToString()
                    .Substring(expression.Parameters[0].Name.Length + 1);
    }

    private static string GetInputName(MethodCallExpression expression)
    {
        // p => p.Foo.Bar().Baz.ToString() => p.Foo OR throw...
        MethodCallExpression methodCallExpression 
                            = expression.Object as MethodCallExpression;
        if (methodCallExpression != null)
        {
            return GetInputName(methodCallExpression);
        }
        return expression.Object.ToString();
    }


    private static SelectList ToSelectList(Type enumType)
    {
        List<SelectListItem> items = new List<SelectListItem>();
        foreach (var item in Enum.GetValues(enumType))
        {
            FieldInfo fi = enumType.GetField(item.ToString());
            var attribute = fi.GetCustomAttributes(
                                       typeof(DescriptionAttribute), true)
                                  .FirstOrDefault();
            var title = attribute == null ? item.ToString() 
                              : ((DescriptionAttribute)attribute).Description;
            var listItem = new SelectListItem
            {
                Value = item.ToString(),
                Text = title,
            };
            items.Add(listItem);
        }

        return new SelectList(items, "Value", "Text");
    }
}

He escrito esto en mi blog aquí .

Jon Egerton
fuente
1
Esta es la única solución con la que me he encontrado que preselecciona correctamente el valor relevante para mi enumeración. ¡Gracias!
Edwin Groenendaal
Increíble. Esta debería ser definitivamente la respuesta aceptada: funciona; la respuesta aceptada no lo hace.
neminem
3

Esto sería útil para seleccionar un valor int de enum: Aquí SpecTypehay un intcampo ... y enmSpecTypees un enum.

@Html.DropDownList(
    "SpecType", 
     YourNameSpace.SelectExtensions.ToSelectList(typeof(NREticaret.Core.Enums.enmSpecType), 
     Model.SpecType.ToString()), "Tip Seçiniz", new 
     { 
         gtbfieldid = "33", 
         @class = "small" 
     })
usuario687314
fuente
3

Hice el siguiente cambio en el método SelectList para que funcione un poco mejor para mí. Quizás sea útil para otros.

public static SelectList ToSelectList<T>(T selectedItem)
        {
            if (!typeof(T).IsEnum) throw new InvalidEnumArgumentException("The specified type is not an enum");

            var selectedItemName = Enum.GetName(typeof (T), selectedItem);
            var items = new List<SelectListItem>();
            foreach (var item in Enum.GetValues(typeof(T)))
            {
                var fi = typeof(T).GetField(item.ToString());
                var attribute = fi.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();

                var enumName = Enum.GetName(typeof (T), item);
                var title = attribute == null ? enumName : ((DescriptionAttribute)attribute).Description;

                var listItem = new SelectListItem
                {
                    Value = enumName,
                    Text = title,
                    Selected = selectedItemName == enumName
                };
                items.Add(listItem);
            }

            return new SelectList(items, "Value", "Text");
        }
Jason
fuente
3
    public enum EnumStates
    {
        AL = 0,
        AK = 1,
        AZ = 2,
        WY = 3
    }


@Html.DropDownListFor(model => model.State, (from EnumStates e in Enum.GetValues(typeof(EnumStates))
                                                               select new SelectListItem { Value = ((int)e).ToString(), Text = e.ToString() }), "select", new { @style = "" })
                @Html.ValidationMessageFor(model => model.State)  //With select



//Or


@Html.DropDownListFor(model => model.State, (from EnumStates e in Enum.GetValues(typeof(EnumStates))
                                                               select new SelectListItem { Value = ((int)e).ToString(), Text = e.ToString() }), null, new { @style = "" })
                @Html.ValidationMessageFor(model => model.State)   //With out select
Thulasiram
fuente
¿Dónde se define EnumState?
superartsy
en la parte superior puede verlo ... enumeración pública EnumStates
Thulasiram
2

Igual que el de Mike (que está enterrado entre largas respuestas)

model.truckimagelocation es una propiedad de instancia de clase del tipo de enumeración TruckImageLocation

@Html.DropDownListFor(model=>model.truckimagelocation,Enum.GetNames(typeof(TruckImageLocation)).ToArray().Select(f=> new SelectListItem() {Text = f, Value = f, Selected = false}))
usuario794791
fuente
2

Este es el código más genérico que se utilizará para todas las enumeraciones.

public static class UtilitiesClass
{

    public static SelectList GetEnumType(Type enumType)
    {
        var value = from e in Enum.GetNames(enumType)
                    select new
                    {
                        ID = Convert.ToInt32(Enum.Parse(enumType, e, true)),
                        Name = e
                    };
        return new SelectList(value, "ID", "Name");
    }
}

Método de acción

ViewBag.Enum= UtilitiesClass.GetEnumType(typeof (YourEnumType));

View.cshtml

 @Html.DropDownList("Type", (IEnumerable<SelectListItem>)ViewBag.Enum, new { @class = "form-control"})
Muhammad Kamran
fuente
1

puedes usar enum en tu modelo

tu enumeración

public enum States()
{
  AL,AK,AZ,...WY
}

Haz un modelo

public class enumclass
{
public States statesprop {get; set;}
}

en vista

@Html.Dropdownlistfor(a=>a.statesprop)
MaTya
fuente
Últimas preguntas Responder kar.
Anup
1

La respuesta más fácil en MVC5 es Define Enum:

public enum ReorderLevels {
          zero = 0,
            five = 5,
            ten = 10,
            fifteen = 15,
            twenty = 20,
            twenty_five = 25,
            thirty = 30
        }

Enlazar en vista:

        <div class="form-group">
            <label>Reorder Level</label>
            @Html.EnumDropDownListFor(m => m.ReorderLevel, "Choose Me", new { @class = "form-control" })
        </div>
Mark Phillips
fuente