Come si crea un elenco a discesa da un enum in ASP.NET MVC?


671

Sto cercando di utilizzare il Html.DropDownListmetodo di estensione ma non riesco a capire come usarlo con un elenco.

Diciamo che ho un elenco come questo:

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

Come posso creare un menu a discesa con questi valori usando il Html.DropDownListmetodo di estensione?

O la mia scommessa migliore è semplicemente creare un ciclo for e creare manualmente gli elementi HTML?

Risposte:


842

Per MVC v5.1 utilizzare Html.EnumDropDownListFor

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

Per MVC v5 utilizzare EnumHelper

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

Per MVC 5 e versioni precedenti

Ho inserito la risposta di Rune in un metodo di estensione:

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

Questo ti permette di scrivere:

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

di using MyApp.Common


13
Non riuscivo a farlo funzionare, potresti aiutarmi, per favore. Quando faccio Post.PostType.ToSelectList (); non riconosce l'estensione?
Barbaros Alp,

3
Neanche questo è riuscito a farlo funzionare. Status è la tua proprietà Enum nella classe di attività? Questo non è uno dei valori elencati?
Daryl,

9
Puoi limitarlo un po 'con: dove T: struct, IConvertible Vedi: stackoverflow.com/questions/79126/…
Richard Garside,

8
Questo è fico. Se qualcuno sta lottando con l'implementazione, ecco come l'ho fatto. Aggiunta una classe EnumHelpers alla cartella HtmlHelpers. Usato il codice sopra. Aggiunto lo spazio dei nomi per la raccomandazione @TodK: <add namespace = "xxx.HtmlHelpers" />. Quindi l'ho usato in una pagina di rasoio come questa: @ Html.DropDownListFor (model => model.Status, @ Model.Status.ToSelectList ()) HTH
Jeff Borden

6
Si noti che nel più recente ASP.NET MVCc'è un modo nativo: stackoverflow.com/a/22295360/1361084
Ofiris

359

So che sono in ritardo alla festa su questo, ma ho pensato che potresti trovare utile questa variante, poiché questa ti consente anche di utilizzare stringhe descrittive piuttosto che costanti di enumerazione nel menu a discesa. A tale scopo, decorare ogni voce di enumerazione con un attributo [System.ComponentModel.Description].

Per esempio:

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

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

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

Ecco il mio codice:

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

Puoi quindi farlo a tuo avviso:

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

Spero che questo ti aiuti!

** MODIFICA 2014-GEN-23: Microsoft ha appena rilasciato MVC 5.1, che ora ha una funzione EnumDropDownListFor. Purtroppo non sembra rispettare l'attributo [Descrizione] quindi il codice sopra rimane valido. Vedi la sezione Enum in note di rilascio di Microsoft per MVC 5.1.

Aggiornamento: tuttavia supporta l' attributo Display[Display(Name = "Sample")] , quindi è possibile utilizzarlo.

[Aggiornamento - l'ho appena notato, e il codice sembra una versione estesa del codice qui: https://blogs.msdn.microsoft.com/stuartleeks/2010/05/21/asp-net-mvc-creating-a- dropdownlist-helper-for-enums / , con un paio di aggiunte. In tal caso, l'attribuzione sembrerebbe giusta ;-)]


28
+1 Ho trovato questa la più utile di tutte le risposte qui. Sono stato in grado di trasformare questo in un pezzo di codice altamente riutilizzabile. Grazie!
Ed Charbeneau,

43
Visual Studio ha uno strano bug in cui se non fai riferimento System.Web.Mvc.Htmlallora dice che DropDownListFornon può essere trovato, ma nemmeno può risolverlo. Devi fare manualmente using System.Web.Mvc.Html;. Proprio così lo sai.
Kezzer,

1
Ne ho una variante in un senso che usiamo in tutti i nostri progetti: gist.github.com/1287511
kamranicus

1
Un'ottima soluzione, grazie, sarebbe ancora migliore se fosse possibile memorizzare nella cache i risultati di GetEnumDescription
M. Mennan Kara,

17
Il nuovo MVC 5.1 EnumDropDownListFor non utilizza [Description ("")] ma utilizza [Display (Name = "")]! Buon divertimento :)
Supergibbs,

195

In ASP.NET MVC 5.1 , hanno aggiunto ilEnumDropDownListFor() helper, quindi non sono necessarie estensioni personalizzate:

Modello :

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

Visualizza :

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

Utilizzando Tag Helper (ASP.NET MVC 6) :

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

21
Questo in qualche modo deve essere portato al primo posto

3
Dovresti creare una nuova domanda specifica per MVC 5.1 e inserirla come risposta, quindi inviarmi un link al post in modo che io possa votare un preferito.
Kevin Heidt,

2
Quello che non mi piace di EnumDropDownListFor () è che salva nel DB il valore int dell'enum, non il testo, quindi se scegli di aggiungere un nuovo elemento enum, deve necessariamente andare alla fine dell'elenco , in modo da non perdere la relazione tra i valori int del database salvati e le posizioni originali degli elementi enum. Questa è una restrizione non necessaria se il testo viene salvato. Inoltre, preferisco essere in grado di guardare il db e vedere un testo, piuttosto che ints dove poi devo cercare i valori del testo altrove. Altrimenti questo helper HTML è molto comodo da usare.
Giovanni,

2
@Giovanni: puoi specificare i tuoi valori numerici.
Tommy,

1
@Giovanni Il design rigoroso dovrebbe assegnare un valore per ciascuna voce enum (se è importante), altrimenti il ​​valore non dovrebbe avere importanza (e quindi posizionare quelli nuovi alla fine non dovrebbe essere un problema). Salvare i valori int è meglio quando si tratta di salvare memoria e aumentare le prestazioni (quando si esegue una ricerca).
King King

130

Mi sono imbattuto nello stesso problema, ho trovato questa domanda e ho pensato che la soluzione fornita da Ash non fosse quella che cercavo; Dover creare l'HTML da solo significa meno flessibilità rispetto al built-inHtml.DropDownList() funzione integrata.

Si scopre che C # 3 ecc. Lo rende abbastanza semplice. Ho enumchiamato 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);

Questo crea un buon vecchio SelectListche può essere usato come sei abituato nella vista:

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

Il tipo anonimo e LINQ rendono questo IMHO molto più elegante. Senza offesa, Ash. :)


buona risposta! Speravo che qualcuno avrebbe usato Linq e la SelectList :) Sono contento di aver controllato prima qui!
Pure.Krome il

1
ID = s mi dai il DataTextField non il valore? Quale potrebbe essere la ragione? Grazie
Barbaros Alp il

1
Rune, ho usato lo stesso metodo e il DropDownList FA ancora quando viene pubblicato sul server, non salva il valore che avevo selezionato.
senso orario,

5
@BarbarosAlp Affinché ID sia un numero, devi lanciare l'enum su un int:select new { ID = (int)s, Name = s.ToString() };
Keith,

Questa è la risposta che mi piace di più per la sua semplicità. Peccato che non hai ricevuto abbastanza credito da quando la risposta selezionata ha utilizzato la tua soluzione.
anar khalilov,

63

Ecco una soluzione incapsulata migliore:

https://www.spicelogic.com/Blog/enum-dropdownlistfor-asp-net-mvc-5

Di 'qui il tuo modello:

inserisci qui la descrizione dell'immagine

Esempio di utilizzo:

inserisci qui la descrizione dell'immagine

UI generata: inserisci qui la descrizione dell'immagine

E HTML generato

inserisci qui la descrizione dell'immagine

L'istantanea del codice sorgente dell'estensione dell'helper:

inserisci qui la descrizione dell'immagine

Puoi scaricare il progetto di esempio dal link che ho fornito.

EDIT: ecco il codice:

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

2
Solo la mia opinione, ma penso che questa risposta sia molto più pulita della risposta accettata. Mi piace in particolare l'opzione di utilizzare l'attributo Descrizione. Ho aggiunto il codice in modo che le persone possano copiarlo / incollarlo senza scaricarlo.
Ben Mills,

Chiama il metodo di estensione come EnumDropDownListFor anziché DropDownListFor Uso: -> @ Html.EnumDropDownListFor (x => x.Gender)
sandeep talabathula

Per qualcuno che cerca di aggiungere un altro elemento "Please Select" return htmlHelper.DropDownListFor (expression, createSelectList (expression.ReturnType, selected, firstElement), "Please Select");
Sandeep,

1
Funziona bene! Tuttavia, nella pagina Dettagli, DisplayFor () mostra il valore selezionato dell'enum invece della descrizione corrispondente. Suppongo che ciò richieda un sovraccarico per DisplayFor () per il tipo enum. Qualcuno ha una soluzione per questo?
corix010,

48

Html.DropDownListFor richiede solo un IEnumerable, quindi un'alternativa alla soluzione di Prise è la seguente. Questo ti permetterà di scrivere semplicemente:

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

[Dove SelectedItemType è un campo nel modello di tipo ItemTypes e il modello è non nullo]

Inoltre, non è necessario generare il metodo di estensione in quanto è possibile utilizzare enumValue.GetType () anziché typeof (T).

EDIT: qui è stata integrata anche la soluzione di Simon e incluso il metodo di estensione 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();
    }
}

Non funziona per me ('System.NullReferenceException: riferimento oggetto non impostato su un'istanza di un oggetto.') ... Il mio 'Modello' è nullo ... probabilmente ha qualcosa a che fare con 'GetNonNullableModelType' che Simon ha incluso
Studente

@Cristi, hai ragione questa soluzione non è destinata ad essere utilizzata in una condizione in cui il tuo Modello è nullo. Cerco di evitare un simile progetto in generale e di inizializzarlo su un modello "Vuoto" quando è così.
Zaid Masud,

Bene, sono nuovo di ASP mvc, ma ho abbastanza esperienza in .Net. Grazie, esaminerò ciò che stavi suggerendo. A proposito, l'estensione ToDescription è molto al di fuori dell'ambito "Enum". Immagino che vada bene per l '"oggetto" stesso. Questo è quello che ho usato quando ho preso il codice di Simon e l'ho ripulito un po 'di più.
Principiante

@Cristi è difficile capire cosa intendi per "molto al di fuori dell'ambito di" Enum "ma sembra che tu stia dicendo che il metodo di estensione ToDescription non è fortemente tipizzato all'enum ItemTypes? Questo è intenzionale e rende il metodo di estensione genericamente utilizzabile da tutti gli enum. Se lo stai confrontando con un metodo di estensione generico, ci sono pro e contro di ogni approccio. In particolare, se si genera, non è possibile vincolarlo solo agli enum.
Zaid Masud,

1
Ottimo, grazie. Ho cambiato valore. ToString per usare un'estensione FromCamelCase nel caso in cui non ci fosse una descrizione. È così che lancio :)
Valamas il

33

Quindi, senza funzioni di estensione, se stai cercando semplice e facile .. Questo è quello che ho fatto

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

dove XXXXX.Sites.YYYY.Models.State è un enum

Probabilmente è meglio fare la funzione di aiuto, ma quando il tempo è breve questo farà il lavoro.


Bene, ha funzionato popolando il menu a discesa, ma come si imposta il valore predefinito selezionato nella sintassi Razor per Html.DropDownListFor? Voglio mostrare una tabella con caselle combinate di enum e ho bisogno di impostare anche il valore selezionato in base a quello che era prima.
Johncl,

2
Dovrebbe essere in grado di passare un secondo parametro con il valore selezionato alla nuova funzione SelectList (IEnumerable, object). MSDN Dococumentation: msdn.microsoft.com/en-us/library/dd460123.aspx
Marty Trenouth

23

Espandendo le risposte di Prize e Rune, se si desidera che l'attributo value degli elementi dell'elenco di selezione sia mappato al valore intero del tipo di enumerazione, anziché al valore di stringa, utilizzare il codice seguente:

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

Invece di trattare ogni valore di enumerazione come un oggetto TEnum, possiamo trattarlo come un oggetto e quindi trasmetterlo a un numero intero per ottenere il valore senza casella.

Nota: ho anche aggiunto un vincolo di tipo generico per limitare i tipi per i quali questa estensione è disponibile solo per le strutture (tipo base di Enum) e una convalida del tipo di runtime che assicura che la struttura passata sia effettivamente un Enum.

Aggiornamento del 23/10/12: aggiunto un parametro di tipo generico per il tipo sottostante e risolto il problema di non compilazione relativo a .NET 4+.


Grazie! Questa era la risposta di cui avevo bisogno. Sto memorizzando un valore intero di Enum come colonna nel database e questa soluzione sembra funzionare perfettamente.
grimus,

ma cosa succede se si memorizza un carattere e non un int? che è il mio caso ovviamente potrei cambiare (int) in (char) ma che ne dici di fare anche questo generico. come farlo?
Stefanvds,

@Stefandvds Questa è una grande domanda riguardo al casting nel tipo rappresentato correttamente. Sulla base dei test che ho appena eseguito sembrerebbe che l'unico modo per raggiungere questo obiettivo sia specificare il tipo effettivo come un altro parametro di tipo. ToSelectList<TEnum, TEnumValue>(this TEnum enumObj) { ... }
Nathan Taylor,


Se i valori della tua enum sono int, puoi semplicemente usare Value = Convert.ToInt32(e). (int)enon compilare. :(
Andrew

11

Per risolvere il problema di ottenere il numero anziché il testo usando il metodo di estensione di Prise.

public static SelectList ToSelectList<TEnum>(this TEnum enumObj)
{
  var values = from TEnum e in Enum.GetValues(typeof(TEnum))
               select new { ID = (int)Enum.Parse(typeof(TEnum),e.ToString())
                         , Name = e.ToString() };

  return new SelectList(values, "Id", "Name", enumObj);
}

Questo è quello che stavo cercando, anche se è più brutto di quanto pensassi dovesse essere. Mi chiedo perché Visual Studio non vi lascerà direttamente lanci ea int.
Andrew,

O potresti semplicemente usare ID = Convert.ToInt32(e).
Andrew,

11

Un modo super semplice per farlo - senza tutte le estensioni che sembrano eccessive è questo:

Il tuo enum:

    public enum SelectedLevel
    {
       Level1,
       Level2,
       Level3,
       Level4
    }

All'interno del controller associa l'Enum a un elenco:

    List<SelectedLevel> myLevels = Enum.GetValues(typeof(SelectedLevel)).Cast<SelectedLevel>().ToList();

Dopodiché gettalo in un ViewBag:

    ViewBag.RequiredLevel = new SelectList(myLevels);

Infine, basta collegarlo alla vista:

    @Html.DropDownList("selectedLevel", (SelectList)ViewBag.RequiredLevel, new { @class = "form-control" })

Questo è di gran lunga il modo più semplice che ho trovato e non richiede alcuna estensione o qualcosa di così folle.

AGGIORNAMENTO : vedi il commento di Andrews di seguito.


3
Funziona solo se non hai assegnato alcun valore alla tua enum. Se lo avessi Level1 = 1, il valore del menu a discesa sarebbe "Level1"invece di 1.
Andrew,

11

La migliore soluzione che ho trovato per questo è stata la combinazione di questo blog con la risposta di Simon Goldstone .

Ciò consente l'uso dell'enum nel modello. Fondamentalmente l'idea è di usare una proprietà intera così come l'enum ed emulare la proprietà intera.

Quindi utilizzare l'attributo [System.ComponentModel.Description] per annotare il modello con il testo visualizzato e utilizzare l'estensione "EnumDropDownListFor" nella vista.

Ciò rende sia la vista che il modello molto leggibili e gestibili.

Modello:

public enum YesPartialNoEnum
{
    [Description("Yes")]
    Yes,
    [Description("Still undecided")]
    Partial,
    [Description("No")]
    No
}

//........

[Display(Name = "The label for my dropdown list")]
public virtual Nullable<YesPartialNoEnum> CuriousQuestion{ get; set; }
public virtual Nullable<int> CuriousQuestionId
{
    get { return (Nullable<int>)CuriousQuestion; }
    set { CuriousQuestion = (Nullable<YesPartialNoEnum>)value; }
}

Visualizza:

@using MyProject.Extensions
{
//...
    @Html.EnumDropDownListFor(model => model.CuriousQuestion)
//...
}

Estensione (direttamente dalla risposta di Simon Goldstone , inclusa qui per completezza):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.ComponentModel;
using System.Reflection;
using System.Linq.Expressions;
using System.Web.Mvc.Html;

namespace MyProject.Extensions
{
    //Extension methods must be defined in a static class
    public static class MvcExtensions
    {
        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);
        }
    }
}

Questo non funziona, MVC 4 Razor. Nella vista o runtime, errore = "La chiamata è ambigua tra i seguenti metodi o proprietà 'LDN.Extensions.MvcExtensions.EnumDropDownListFor <MyModel, LDN.Models.YesPartialNoEnum?> (System.Web.Mvc.HtmlHelper <MyModel>, System .Linq.Expressions.Expression <System.Func <MyModel, LDN.Models.YesPartialNoEnum? >>) 'e .... "e lo stesso identico metodo con gli stessi oggetti di scena ripetuti di nuovo (non sono ammessi caratteri sufficienti qui).
Marc,


8
@Html.DropDownListFor(model => model.Type, Enum.GetNames(typeof(Rewards.Models.PropertyType)).Select(e => new SelectListItem { Text = e }))

Buona! Come ottenere valore e testo da enum in questo modo? Voglio dire, ho SomeEnum {some1 = 1, some2 = 2} Ho bisogno di ottenere numeri (1, 2) per valore e testo (some1, some2) per il testo dell'elenco di selezione
Dmitresky

7

Queste sono le risposte di Rune e Premi modificate per usare l'Enum int valore come ID.

Enum di esempio:

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

Metodo di estensione:

    public static SelectList ToSelectList<TEnum>(this TEnum enumObj)
    {
        var values = from TEnum e in Enum.GetValues(typeof(TEnum))
                     select new { Id = (int)Enum.Parse(typeof(TEnum), e.ToString()), Name = e.ToString() };

        return new SelectList(values, "Id", "Name", (int)Enum.Parse(typeof(TEnum), enumObj.ToString()));
    }

Esempio di utilizzo:

 <%=  Html.DropDownList("MyEnumList", ItemTypes.Game.ToSelectList()) %>

Ricorda di importare lo spazio dei nomi contenente il metodo Extension

<%@ Import Namespace="MyNamespace.LocationOfExtensionMethod" %>

Esempio di HTML generato:

<select id="MyEnumList" name="MyEnumList">
    <option value="1">Movie</option>
    <option selected="selected" value="2">Game</option>
    <option value="3">Book </option>
</select>

Si noti che l'elemento che si utilizza per chiamare il ToSelectListon è l'elemento selezionato.


O potresti semplicemente usare Id = Convert.ToInt32(e) .
Andrew,

6

Questa è la versione per Razor:

@{
    var itemTypesList = new List<SelectListItem>();
    itemTypesList.AddRange(Enum.GetValues(typeof(ItemTypes)).Cast<ItemTypes>().Select(
                (item, index) => new SelectListItem
                {
                    Text = item.ToString(),
                    Value = (index).ToString(),
                    Selected = Model.ItemTypeId == index
                }).ToList());
 }


@Html.DropDownList("ItemTypeId", itemTypesList)

Funzionerà solo se il tuo enum è costituito da valori contigui che iniziano con 0. Un enum Flags non funzionerebbe con questo. Uso creativo della selezione indicizzata, però.
Suncat2000,

6

In .NET Core puoi semplicemente usare questo:

@Html.DropDownListFor(x => x.Foo, Html.GetEnumSelectList<MyEnum>())

1
O con l'helper tag <select asp-for="Model.Foo" class="form-control" asp-items="@Html.GetEnumSelectList<MyEnum>()"></select>.
Pascal R.

sì, direi che gli helper di tag sono ancora migliori in quanto il formato è più vicino al puro HTML;)
GoldenAge

Inoltre puoi farlo @ Html.DropDownListFor (x => x.Foo, Html.GetEnumSelectList (typeof (FooEnum)))
Fereydoon Barikzehy


5

Sulla base della risposta di Simon, un approccio simile è quello di ottenere i valori di Enum da visualizzare da un file di risorse, anziché in un attributo di descrizione all'interno dell'Enum stesso. Questo è utile se il tuo sito deve essere reso in più di una lingua e se dovessi avere un file di risorse specifico per Enums, potresti fare un passo ulteriore e avere solo valori Enum, nel tuo Enum e fare riferimento a loro dall'estensione tramite una convenzione come [EnumName] _ [EnumValue] - in definitiva meno battitura!

L'estensione è quindi simile a:

public static IHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> html, Expression<Func<TModel, TEnum>> expression)
{            
    var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);

    var enumType = Nullable.GetUnderlyingType(metadata.ModelType) ?? metadata.ModelType;

    var enumValues = Enum.GetValues(enumType).Cast<object>();

    var items = from enumValue in enumValues                        
                select new SelectListItem
                {
                    Text = GetResourceValueForEnumValue(enumValue),
                    Value = ((int)enumValue).ToString(),
                    Selected = enumValue.Equals(metadata.Model)
                };


    return html.DropDownListFor(expression, items, string.Empty, null);
}

private static string GetResourceValueForEnumValue<TEnum>(TEnum enumValue)
{
    var key = string.Format("{0}_{1}", enumValue.GetType().Name, enumValue);

    return Enums.ResourceManager.GetString(key) ?? enumValue.ToString();
}

Risorse nel file Enums.Resx che assomigliano a ItemTypes_Movie: Film

Un'altra cosa che mi piace fare è, invece di chiamare direttamente il metodo di estensione, preferirei chiamarlo con un @ Html.EditorFor (x => x.MyProperty), o idealmente avere l'intera forma, in un unico @ Html.EditorForModel (). Per fare ciò, modifico il modello di stringa in questo modo

@using MVCProject.Extensions

@{
    var type = Nullable.GetUnderlyingType(ViewData.ModelMetadata.ModelType) ?? ViewData.ModelMetadata.ModelType;

    @(typeof (Enum).IsAssignableFrom(type) ? Html.EnumDropDownListFor(x => x) : Html.TextBoxFor(x => x))
}

Se questo ti interessa, ho inserito una risposta molto più dettagliata qui sul mio blog:

http://paulthecyclist.com/2013/05/24/enum-dropdown/


5

Beh, sono davvero in ritardo alla festa, ma per quello che vale, ho scritto un blog su questo argomento in base al quale creo un EnumHelper classe che consente una trasformazione molto semplice.

http://jnye.co/Posts/4/creating-a-dropdown-list-from-an-enum-in-mvc-and-c%23

Nel tuo controller:

//If you don't have an enum value use the type
ViewBag.DropDownList = EnumHelper.SelectListFor<MyEnum>();

//If you do have an enum value use the value (the value will be marked as selected)    
ViewBag.DropDownList = EnumHelper.SelectListFor(MyEnum.MyEnumValue);

Nella tua vista:

@Html.DropDownList("DropDownList")
@* OR *@
@Html.DropDownListFor(m => m.Property, ViewBag.DropDownList as SelectList, null)

La classe di aiuto:

public static class EnumHelper
{
    // Get the value of the description attribute if the   
    // enum has one, otherwise use the value.  
    public static string GetDescription<TEnum>(this TEnum value)
    {
        var fi = value.GetType().GetField(value.ToString());

        if (fi != null)
        {
            var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attributes.Length > 0)
            {
                return attributes[0].Description;
            }
        }

        return value.ToString();
    }

    /// <summary>
    /// Build a select list for an enum
    /// </summary>
    public static SelectList SelectListFor<T>() where T : struct
    {
        Type t = typeof(T);
        return !t.IsEnum ? null
                         : new SelectList(BuildSelectListItems(t), "Value", "Text");
    }

    /// <summary>
    /// Build a select list for an enum with a particular value selected 
    /// </summary>
    public static SelectList SelectListFor<T>(T selected) where T : struct
    {
        Type t = typeof(T);
        return !t.IsEnum ? null
                         : new SelectList(BuildSelectListItems(t), "Text", "Value", selected.ToString());
    }

    private static IEnumerable<SelectListItem> BuildSelectListItems(Type t)
    {
        return Enum.GetValues(t)
                   .Cast<Enum>()
                   .Select(e => new SelectListItem { Value = e.ToString(), Text = e.GetDescription() });
    }
}

4

Sono molto in ritardo su questo, ma ho appena trovato un modo davvero fantastico per farlo con una riga di codice, se sei felice di aggiungere la melodia non vincolata pacchetto Unconstrained NuGet (una bella e piccola libreria di Jon Skeet).

Questa soluzione è migliore perché:

  1. Assicura (con vincoli di tipo generico) che il valore sia davvero un valore enum (dovuto alla melodia non vincolata)
  2. Evita il pugilato non necessario (a causa di Unconstrained Melody)
  3. Memorizza tutte le descrizioni nella cache per evitare di usare la riflessione su ogni chiamata (a causa di Melodia non vincolata)
  4. È meno codice rispetto alle altre soluzioni!

Quindi, ecco i passaggi per farlo funzionare:

  1. Nella console di Package Manager, "Install-Package UnconstrainedMelody"
  2. Aggiungi una proprietà al tuo modello in questo modo:

    //Replace "YourEnum" with the type of your enum
    public IEnumerable<SelectListItem> AllItems
    {
        get
        {
            return Enums.GetValues<YourEnum>().Select(enumValue => new SelectListItem { Value = enumValue.ToString(), Text = enumValue.GetDescription() });
        }
    }

Ora che hai l'Elenco di SelectListItem esposto sul tuo modello, puoi usare @ Html.DropDownList o @ Html.DropDownListFor usando questa proprietà come sorgente.


+1 per l'utilizzo del codice di Jon Skeet :),
sto

3

Un'altra correzione a questo metodo di estensione: la versione corrente non ha selezionato il valore corrente dell'enum. Ho corretto l'ultima riga:

public static SelectList ToSelectList<TEnum>(this TEnum enumObj) where TEnum : struct
    {
        if (!typeof(TEnum).IsEnum) throw new ArgumentException("An Enumeration type is required.", "enumObj");

        var values = from TEnum e in Enum.GetValues(typeof(TEnum))
                       select new
                       {
                           ID = (int)Enum.Parse(typeof(TEnum), e.ToString()),
                           Name = e.ToString()
                       };


        return new SelectList(values, "ID", "Name", ((int)Enum.Parse(typeof(TEnum), enumObj.ToString())).ToString());
    }

3

Se vuoi aggiungere il supporto per la localizzazione, modifica semplicemente il metodo s.toString () in qualcosa del genere:

ResourceManager rManager = new ResourceManager(typeof(Resources));
var dayTypes = from OperatorCalendarDay.OperatorDayType s in Enum.GetValues(typeof(OperatorCalendarDay.OperatorDayType))
               select new { ID = s, Name = rManager.GetString(s.ToString()) };

Qui il tipo di (Risorse) è la risorsa che vuoi caricare, e quindi ottieni la stringa localizzata, utile anche se il tuo enumeratore ha valori con più parole.


3

Questa è la mia versione del metodo helper. Io lo uso questo:

var values = from int e in Enum.GetValues(typeof(TEnum))
             select new { ID = e, Name = Enum.GetName(typeof(TEnum), e) };

Invece di quello:

var values = from TEnum e in Enum.GetValues(typeof(TEnum))
           select new { ID = (int)Enum.Parse(typeof(TEnum),e.ToString())
                     , Name = e.ToString() };

Ecco qui:

public static SelectList ToSelectList<TEnum>(this TEnum self) where TEnum : struct
    {
        if (!typeof(TEnum).IsEnum)
        {
            throw new ArgumentException("self must be enum", "self");
        }

        Type t = typeof(TEnum);

        var values = from int e in Enum.GetValues(typeof(TEnum))
                     select new { ID = e, Name = Enum.GetName(typeof(TEnum), e) };

        return new SelectList(values, "ID", "Name", self);
    }

3

Puoi anche utilizzare i miei HtmlHelpers personalizzati in Griffin.MvcContrib. Il seguente codice:

@Html2.CheckBoxesFor(model => model.InputType) <br />
@Html2.RadioButtonsFor(model => model.InputType) <br />
@Html2.DropdownFor(model => model.InputType) <br />

genera:

inserisci qui la descrizione dell'immagine

https://github.com/jgauffin/griffin.mvccontrib


3

Vorrei rispondere a questa domanda in un modo diverso in cui l'utente non deve fare nulla controller oLinq esprimere. Per di qua...

Ho un ENUM

public enum AccessLevelEnum
    {
        /// <summary>
        /// The user cannot access
        /// </summary>
        [EnumMember, Description("No Access")]
        NoAccess = 0x0,

        /// <summary>
        /// The user can read the entire record in question
        /// </summary>
        [EnumMember, Description("Read Only")]
        ReadOnly = 0x01,

        /// <summary>
        /// The user can read or write
        /// </summary>
        [EnumMember, Description("Read / Modify")]
        ReadModify = 0x02,

        /// <summary>
        /// User can create new records, modify and read existing ones
        /// </summary>
        [EnumMember, Description("Create / Read / Modify")]
        CreateReadModify = 0x04,

        /// <summary>
        /// User can read, write, or delete
        /// </summary>
        [EnumMember, Description("Create / Read / Modify / Delete")]
        CreateReadModifyDelete = 0x08,

        /*/// <summary>
        /// User can read, write, or delete
        /// </summary>
        [EnumMember, Description("Create / Read / Modify / Delete / Verify / Edit Capture Value")]
        CreateReadModifyDeleteVerify = 0x16*/
    }

Ora canto semplicemente creando un dropdownusando questo enum.

@Html.DropDownList("accessLevel",new SelectList(AccessLevelEnum.GetValues(typeof(AccessLevelEnum))),new { @class = "form-control" })

O

@Html.DropDownListFor(m=>m.accessLevel,new SelectList(AccessLevelEnum.GetValues(typeof(AccessLevelEnum))),new { @class = "form-control" })

Se si desidera selezionare un indice, provare questo

@Html.DropDownListFor(m=>m.accessLevel,new SelectList(AccessLevelEnum.GetValues(typeof(AccessLevelEnum)) , AccessLevelEnum.NoAccess ),new { @class = "form-control" })

Qui ho usato AccessLevelEnum.NoAccesscome parametro aggiuntivo per impostazione predefinita selezionando il menu a discesa.


3

Ho trovato una risposta qui . Tuttavia, alcuni dei miei enum hanno un [Description(...)]attributo, quindi ho modificato il codice per fornire supporto per questo:

    enum Abc
    {
        [Description("Cba")]
        Abc,

        Def
    }


    public static MvcHtmlString EnumDropDownList<TEnum>(this HtmlHelper htmlHelper, string name, TEnum selectedValue)
    {
        IEnumerable<TEnum> values = Enum.GetValues(typeof(TEnum))
            .Cast<TEnum>();

        List<SelectListItem> items = new List<SelectListItem>();
        foreach (var value in values)
        {
            string text = value.ToString();

            var member = typeof(TEnum).GetMember(value.ToString());
            if (member.Count() > 0)
            {
                var customAttributes = member[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
                if (customAttributes.Count() > 0)
                {
                    text = ((DescriptionAttribute)customAttributes[0]).Description;
                }
            }

            items.Add(new SelectListItem
            {
                Text = text,
                Value = value.ToString(),
                Selected = (value.Equals(selectedValue))
            });
        }

        return htmlHelper.DropDownList(
            name,
            items
            );
    }

Spero che aiuti.


Voglio restituire un membro di tipo = elenco a discesa. Sono bravo con Text = DescriptionAttribute, ma trovo difficile ottenere il valore int da Value
NanaFadanvis,

2

@Simon Goldstone: grazie per la tua soluzione, può essere perfettamente applicato nel mio caso. L'unico problema è che ho dovuto tradurlo in VB. Ma ora è fatto e per risparmiare tempo di altre persone (nel caso ne avessero bisogno) lo metto qui:

Imports System.Runtime.CompilerServices
Imports System.ComponentModel
Imports System.Linq.Expressions

Public Module HtmlHelpers
    Private Function GetNonNullableModelType(modelMetadata As ModelMetadata) As Type
        Dim realModelType = modelMetadata.ModelType

        Dim underlyingType = Nullable.GetUnderlyingType(realModelType)

        If Not underlyingType Is Nothing Then
            realModelType = underlyingType
        End If

        Return realModelType
    End Function

    Private ReadOnly SingleEmptyItem() As SelectListItem = {New SelectListItem() With {.Text = "", .Value = ""}}

    Private Function GetEnumDescription(Of TEnum)(value As TEnum) As String
        Dim fi = value.GetType().GetField(value.ToString())

        Dim attributes = DirectCast(fi.GetCustomAttributes(GetType(DescriptionAttribute), False), DescriptionAttribute())

        If Not attributes Is Nothing AndAlso attributes.Length > 0 Then
            Return attributes(0).Description
        Else
            Return value.ToString()
        End If
    End Function

    <Extension()>
    Public Function EnumDropDownListFor(Of TModel, TEnum)(ByVal htmlHelper As HtmlHelper(Of TModel), expression As Expression(Of Func(Of TModel, TEnum))) As MvcHtmlString
        Return EnumDropDownListFor(htmlHelper, expression, Nothing)
    End Function

    <Extension()>
    Public Function EnumDropDownListFor(Of TModel, TEnum)(ByVal htmlHelper As HtmlHelper(Of TModel), expression As Expression(Of Func(Of TModel, TEnum)), htmlAttributes As Object) As MvcHtmlString
        Dim metaData As ModelMetadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData)
        Dim enumType As Type = GetNonNullableModelType(metaData)
        Dim values As IEnumerable(Of TEnum) = [Enum].GetValues(enumType).Cast(Of TEnum)()

        Dim items As IEnumerable(Of SelectListItem) = From value In values
            Select New SelectListItem With
            {
                .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 Then
            items = SingleEmptyItem.Concat(items)
        End If

        Return htmlHelper.DropDownListFor(expression, items, htmlAttributes)
    End Function
End Module

Fine Lo usi in questo modo:

@Html.EnumDropDownListFor(Function(model) (model.EnumField))


2
@Html.DropdownListFor(model=model->Gender,new List<SelectListItem>
{
 new ListItem{Text="Male",Value="Male"},
 new ListItem{Text="Female",Value="Female"},
 new ListItem{Text="--- Select -----",Value="-----Select ----"}
}
)

2
@Html.DropDownListFor(model => model.MaritalStatus, new List<SelectListItem> 
{  

new SelectListItem { Text = "----Select----", Value = "-1" },


new SelectListItem { Text = "Marrid", Value = "M" },


 new SelectListItem { Text = "Single", Value = "S" }

})

Penso che questa non sia una risposta valida, non sta affatto usando l'enum per popolare il menu a discesa.
Andrew,
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.