MVC3 Razor DropDownListFor Enums


84

Sto cercando di aggiornare il mio progetto a MVC3, qualcosa che non riesco a trovare:

Ho un semplice tipo di dati di ENUMS:

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

Che desidero utilizzare come DropDown / SelectList nella mia vista di un modello che contiene questo tipo di dati:

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

Abbastanza semplice: quando uso la vista di generazione automatica per questa classe parziale, ignora questo tipo.

Ho bisogno di un semplice elenco di selezione che imposti il ​​valore dell'enumerazione come elemento selezionato quando premo invia ed elaboro tramite il mio metodo AJAX - JSON POST.

E poi la vista (???!):

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

Grazie in anticipo per il suggerimento!


8
Per chiunque si imbatta in questo thread e utilizza MVC 5.1 o versioni successive, il metodo di supporto @ Html.EnumDropDownListFor () è ora integrato in MVC: vedere asp.net/mvc/overview/releases/mvc51-release-notes
mecsco

Risposte:


55

Ne ho appena fatto uno per il mio progetto. Il codice seguente fa parte della mia classe helper, spero di avere tutti i metodi necessari. Scrivi un commento se non funziona e controllerò di nuovo.

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

Usalo come:

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

Aggiornare

Ho creato helper HTML alternativi. Tutto quello che devi fare per usarli è cambiare la tua pagina di visualizzazione di base in views\web.config.

Con loro puoi semplicemente fare:

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

Maggiori informazioni qui: http://blog.gauffin.org/2011/10/first-draft-of-my-alternative-html-helpers/


1
Ok, funziona in entrambi i casi, ricevo solo un errore di compilazione: Riga 41: return htmlHelper.DropDownList (inputName, ToSelectList (typeof (TProperty), value.ToString ())); "System.Web.Mvc.HtmlHelper <TModel>" non contiene una definizione per "DropDownList" e non è stato possibile trovare alcun metodo di estensione "DropDownList" che accetti un primo argomento di tipo "System.Web.Mvc.HtmlHelper <TModel>" ( ti manca una direttiva using o un riferimento a un assembly?)
jordan.baucke

1
@jordan ho lo stesso errore. Sei riuscito a risolvere il problema?
SF Developer

9
@filu @jordan aggiungi using System.Web.Mvc.Html;quanto ti serve per accedere aSelectExtensionsClass
Simon Hartcher

3
@Para Sto riscontrando lo stesso problema, il valore selezionato non appare selezionato nella vista. (Ho dovuto cambiare ((int)item).ToString()per Enum.GetName(enumType, item)ottenere il SelectListItemcorrettamente salvato come selezionato, ma ancora non funziona)
Fernando Neira

1
Ho appena aggiunto una risposta di seguito che copre il problema di selezione: deriva dall'incomprensione dei comportamenti dei sovraccarichi DropDownList.
Jon Egerton

199

Ho trovato una soluzione più semplice per questo qui: 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 riga nel rasoio lo farà:

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

Puoi anche trovare il codice per farlo con un metodo di estensione nell'articolo collegato.


6
Penso che questo avrebbe dovuto essere contrassegnato come la soluzione. Il meglio non è segnato dalla complessità ma dalla semplicità.
Lord of Scripts

3
Per le persone che cercano una versione DropDowList (come me): @ Html.DropDownList ("listName", new SelectList (Enum.GetValues ​​(typeof (MyNamespace.Enums.States))))
dstr

2
@ JonEgerton Se intendi lo stesso di me, allora sono d'accordo. Se vuoi mostrare enumerazioni + descrizione + un'immagine ti sei perso con la soluzione di Mike McLaughlin.
Elisabeth

1
L'unico problema che ho riscontrato con questa soluzione è che non mappa correttamente il valore selezionato durante il caricamento. A parte questo, abbastanza bene.
triangulito

3
@triangulito questo non è affatto un problema :)@Html.DropDownListFor(model => model.State, new SelectList(Enum.GetValues(typeof(MyNamespace.Enums.States)),model.State))
VladL


17

Se vuoi qualcosa di veramente semplice, c'è un altro modo, a seconda di come memorizzi lo stato nel database.

Se avessi un'entità come questa:

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

Quindi generare il menu a discesa sarebbe facile come questo:

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

LINQ non è carino?


dove definisci gli Stati enumerati nel Modello o nella Vista?
superartsy

nel modello, poiché è utilizzato dalla classe del modello
sjmeverett

1
@stewartml Quando il mio ViewModel ha la proprietà enum + "SelectedCodeProperty", questa è una proprietà di troppo nel tuo post. Perché non avere l'enumerazione in entrambi come valore selezionato postato di nuovo sul server + come valore dell'elemento.
Elisabeth

13

Sono stato in grado di farlo in una linea.

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

8

Sulla base della risposta accettata da @jgauffin, ho creato la mia versione di EnumDropDownListFor, che si occupa del problema della selezione degli elementi.

Il problema è dettagliato in un'altra risposta SO qui:, ed è fondamentalmente dovuto a un malinteso del comportamento dei diversi sovraccarichi di DropDownList.

Il mio codice completo (che include sovraccarichi per htmlAttributesecc. È:

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

L'ho scritto qui sul mio blog .


1
Questa è l'unica soluzione che ho trovato che preseleziona correttamente il valore pertinente per la mia enumerazione. Grazie!
Edwin Groenendaal

Eccezionale. Questa dovrebbe essere sicuramente la risposta accettata: funziona; la risposta accettata no.
neminem

3

Ciò sarebbe utile per selezionare un valore int da enum: Here SpecTypeis an intfield ... and enmSpecTypeis an enum.

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

3

Ho apportato la seguente modifica al metodo SelectList per farlo funzionare un po 'meglio per me. Forse sarà utile per gli altri.

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

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

dove definisci EnumState?
superartsy

in alto puoi vederlo ... enumerazione pubblica EnumStates
Thulasiram

2

Uguale a quello di Mike (che è sepolto tra risposte lunghe)

model.truckimagelocation è una proprietà di istanza di classe del tipo di enumerazione TruckImageLocation

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

2

Questo è il codice più generico che verrà utilizzato per tutti gli Enum.

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

Metodo di azione

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

View.cshtml

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

1

puoi usare enum nel tuo modello

il tuo Enum

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

fare un modello

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

in vista

@Html.Dropdownlistfor(a=>a.statesprop)

Ultime domande Risposta kar.
Anup

1

La risposta più semplice in MVC5 è Define Enum:

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

Associa in vista:

        <div class="form-group">
            <label>Reorder Level</label>
            @Html.EnumDropDownListFor(m => m.ReorderLevel, "Choose Me", new { @class = "form-control" })
        </div>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.