Wie erstellen Sie eine Dropdown-Liste aus einer Aufzählung in ASP.NET MVC?

667

Ich versuche, die Html.DropDownListErweiterungsmethode zu verwenden, kann aber nicht herausfinden, wie sie mit einer Aufzählung verwendet wird.

Angenommen, ich habe eine Aufzählung wie diese:

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

Wie erstelle ich ein Dropdown-Menü mit diesen Werten mithilfe der Html.DropDownListErweiterungsmethode?

Oder ist es meine beste Wahl, einfach eine for-Schleife zu erstellen und die HTML-Elemente manuell zu erstellen?

Kevin Pang
quelle

Antworten:

840

Verwenden Sie für MVC v5.1 Html.EnumDropDownListFor

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

Verwenden Sie für MVC v5 EnumHelper

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

Für MVC 5 und niedriger

Ich habe Runes Antwort in eine Erweiterungsmethode umgewandelt:

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

So können Sie schreiben:

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

durch using MyApp.Common

Martin Faartoft
quelle
13
Ich konnte es nicht zum Laufen bringen, könnten Sie bitte helfen. Wenn ich Post.PostType.ToSelectList () mache; erkennt es die Erweiterung nicht?
Barbaros Alp
3
Ich konnte das auch nicht zum Laufen bringen. Ist Status Ihre Enum-Eigenschaft in der Task-Klasse? Ist das nicht einer der aufgezählten Werte?
Daryl
9
Sie können es ein wenig einschränken mit: where T: struct, IConvertible Siehe: stackoverflow.com/questions/79126/…
Richard Garside
8
Das ist cool. Wenn jemand Probleme mit der Implementierung hat, habe ich es so gemacht. Dem Ordner HtmlHelpers wurde eine EnumHelpers-Klasse hinzugefügt. Verwendete den obigen Code. Der Namespace wurde gemäß der Empfehlung von @TodK hinzugefügt: <add namespace = "xxx.HtmlHelpers" />. Dann habe ich es auf einer Rasierseite wie der folgenden verwendet: @ Html.DropDownListFor (model => model.Status, @ Model.Status.ToSelectList ()) HTH
Jeff Borden
6
Beachten Sie, dass es in neueren VersionenASP.NET MVC einen nativen Weg gibt: stackoverflow.com/a/22295360/1361084
Ofiris
359

Ich weiß, dass ich zu spät zur Party komme, dachte aber, dass Sie diese Variante nützlich finden könnten, da Sie mit dieser auch beschreibende Zeichenfolgen anstelle von Aufzählungskonstanten in der Dropdown-Liste verwenden können. Dekorieren Sie dazu jeden Aufzählungseintrag mit einem Attribut [System.ComponentModel.Description].

Zum Beispiel:

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

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

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

Hier ist mein Code:

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

Sie können dies dann aus Ihrer Sicht tun:

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

Hoffe das hilft dir!

** EDIT 2014-JAN-23: Microsoft hat gerade MVC 5.1 veröffentlicht, das jetzt über eine EnumDropDownListFor-Funktion verfügt. Leider scheint das Attribut [Beschreibung] nicht berücksichtigt zu werden, sodass der obige Code weiterhin gültig ist. Siehe Abschnitt " Aufzählung " in den Versionshinweisen von Microsoft für MVC 5.1.

Update: Es unterstützt jedoch das Display- Attribut [Display(Name = "Sample")], sodass man es verwenden kann.

[Update - habe dies gerade bemerkt und der Code sieht hier wie eine erweiterte Version des Codes aus: https://blogs.msdn.microsoft.com/stuartleeks/2010/05/21/asp-net-mvc-creating-a- dropdownlist-helper-for-enums / , mit ein paar ergänzungen . Wenn ja, würde die Zuschreibung fair erscheinen ;-)]

SimonGoldstone
quelle
28
+1 Ich fand dies die nützlichste aller Antworten hier. Ich konnte daraus einen höchst wiederverwendbaren Code machen. Vielen Dank!
Ed Charbeneau
43
Visual Studio hat einen seltsamen Fehler, bei dem, wenn Sie nicht darauf verweisen System.Web.Mvc.Html, dieser DropDownListFornicht gefunden werden kann, aber auch nicht behoben werden kann. Sie müssen manuell tun using System.Web.Mvc.Html;. Nur damit du es weißt.
Kezzer
1
Ich habe eine Variante davon in einem Kern, den wir in all unseren Projekten verwenden: gist.github.com/1287511
kamranicus
1
Tolle Lösung, danke, wäre noch besser, wenn Sie die Ergebnisse von GetEnumDescription
M. Mennan Kara
17
Die neue MVC 5.1 EnumDropDownListFor verwendet nicht [Description ("")], sondern [Display (Name = "")]! Viel Spaß :)
Supergibbs
195

In ASP.NET MVC 5.1 wurde der EnumDropDownListFor()Helfer hinzugefügt , sodass keine benutzerdefinierten Erweiterungen erforderlich sind:

Modell :

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

Ansicht :

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

Verwenden des Tag-Hilfsprogramms (ASP.NET MVC 6) :

<select asp-for="@Model.SelectedValue" asp-items="Html.GetEnumSelectList<MyEnum>()">
Ofiris
quelle
21
Dies muss irgendwie auf den ersten Platz gebracht werden
3
Sie sollten eine neue Frage erstellen, die für MVC 5.1 spezifisch ist, und diese als Antwort angeben. Senden Sie mir dann einen Link zum Beitrag, damit ich einen Favoriten positiv bewerten kann.
Kevin Heidt
2
Was mir an EnumDropDownListFor () nicht gefällt, ist, dass der int-Wert der Aufzählung und nicht der Text in der Datenbank gespeichert wird. Wenn Sie also jemals ein neues Aufzählungselement hinzufügen möchten, muss es unbedingt am Ende der Liste stehen , um die Beziehung der gespeicherten Datenbank-Int-Werte zu den ursprünglichen Positionen der Aufzählungselemente nicht zu verlieren. Dies ist eine unnötige Einschränkung, wenn der Text gespeichert wird. Außerdem kann ich lieber in die Datenbank schauen und einen Text sehen, als ints, wo ich dann die Textwerte an anderer Stelle nachschlagen muss. Ansonsten ist dieser HTML-Helfer sehr bequem zu bedienen.
Giovanni
2
@Giovanni - Sie können Ihre eigenen Zahlenwerte angeben.
Tommy
1
@Giovanni Strenges Design sollte jedem Aufzählungseintrag einen Wert zuweisen (falls dies wichtig ist), andernfalls sollte der Wert keine Rolle spielen (und daher sollte es kein Problem sein, die neuen am Ende zu platzieren). Das Speichern von int-Werten ist besser, wenn es darum geht, Speicherplatz zu sparen und die Leistung zu steigern (wenn Sie eine Suche durchführen).
König König
130

Ich stieß auf dasselbe Problem, fand diese Frage und dachte, dass die von Ash bereitgestellte Lösung nicht das war, wonach ich suchte. Das HTML selbst erstellen zu müssen bedeutet weniger Flexibilität im Vergleich zur integrierten Html.DropDownList()Funktion.

Es stellt sich heraus, dass C # 3 usw. dies ziemlich einfach macht. Ich habe einen enumAnruf 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);

Dadurch wird eine gute alte Version erstellt SelectList, die so verwendet werden kann, wie Sie es in der Ansicht gewohnt sind:

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

Der anonyme Typ und LINQ machen dies meiner Meinung nach viel eleganter. Nichts für ungut, Ash. :) :)

Rune Jacobsen
quelle
gute Antwort! Ich hatte gehofft, jemand würde Linq und die SelectList verwenden :) Ich bin froh, dass ich hier zuerst nachgesehen habe!
Pure.Krome
1
ID = s gib mir das DataTextField nicht den Wert? Was könnte der Grund sein? Vielen Dank
Barbaros Alp
1
Rune, ich habe dieselbe Methode verwendet und die DropDownList wird NICHT gerendert, wenn sie auf dem Server veröffentlicht wird. Der von mir ausgewählte Wert wird jedoch nicht gespeichert.
im Uhrzeigersinn
5
@BarbarosAlp Damit ID eine Zahl ist, müssen Sie die Aufzählung in ein int umwandeln:select new { ID = (int)s, Name = s.ToString() };
Keith
Dies ist die Antwort, die mir wegen ihrer Einfachheit am besten gefällt. Schade, dass Sie nicht genug Guthaben erhalten haben, da die ausgewählte Antwort Ihre Lösung verwendet hat.
Anar Khalilov
63

Hier ist eine besser gekapselte Lösung:

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

Sagen Sie hier ist Ihr Modell:

Geben Sie hier die Bildbeschreibung ein

Beispielnutzung:

Geben Sie hier die Bildbeschreibung ein

Generierte Benutzeroberfläche: Geben Sie hier die Bildbeschreibung ein

Und generiertes HTML

Geben Sie hier die Bildbeschreibung ein

Der Schnappschuss des Quellcodes der Helfererweiterung:

Geben Sie hier die Bildbeschreibung ein

Sie können das Beispielprojekt über den von mir angegebenen Link herunterladen.

EDIT: Hier ist der Code:

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();
    }
}
Emran Hussain
quelle
2
Nur meine Meinung, aber ich denke, diese Antwort ist viel sauberer als die akzeptierte Antwort. Mir gefällt besonders die Option, das Description-Attribut zu verwenden. Ich habe den Code hinzugefügt, damit die Leute ihn kopieren / einfügen können, ohne ihn herunterladen zu müssen.
Ben Mills
Rufen Sie die Erweiterungsmethode als EnumDropDownListFor und nicht als DropDownListFor auf. Verwendung: -> @ Html.EnumDropDownListFor (x => x.Gender)
Sandeep Talabathula
Für jemanden, der ein weiteres Element hinzufügen möchte "Please Select" return htmlHelper.DropDownListFor (Ausdruck, createSelectList (expression.ReturnType, selected, firstElement), "Please Select");
Sandeep
1
Funktioniert gut! Auf der Seite Details zeigt DisplayFor () jedoch den ausgewählten Wert der Aufzählung anstelle der entsprechenden Beschreibung an. Ich nehme an, dies erfordert eine Überladung für DisplayFor () für den Aufzählungstyp. Hat jemand eine Lösung dafür?
Corix010
48

Für Html.DropDownListFor ist nur eine IEnumerable erforderlich. Eine Alternative zur Prise-Lösung lautet daher wie folgt. So können Sie einfach schreiben:

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

[Wobei SelectedItemType ein Feld in Ihrem Modell vom Typ ItemTypes ist und Ihr Modell nicht null ist]

Außerdem müssen Sie die Erweiterungsmethode nicht wirklich generieren, da Sie enumValue.GetType () anstelle von typeof (T) verwenden können.

BEARBEITEN: Integrierte auch hier Simons Lösung und enthielt die ToDescription-Erweiterungsmethode.

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();
    }
}
Zaid Masud
quelle
Funktioniert bei mir nicht ('System.NullReferenceException: Objektreferenz nicht auf eine Instanz eines Objekts festgelegt.') ... Mein 'Modell' ist null ... hat wahrscheinlich etwas mit 'GetNonNullableModelType' zu tun, das Simon hat enthalten
Lerner
@Cristi, Sie haben Recht, diese Lösung ist nicht für den Fall vorgesehen, dass Ihr Modell null ist. Ich versuche, ein solches Design im Allgemeinen zu vermeiden und auf ein "leeres" Modell zu initialisieren, wenn dies der Fall ist.
Zaid Masud
Nun, ich bin neu bei asp mvc, aber ich habe ziemlich viel Erfahrung in .Net. Vielen Dank, ich werde untersuchen, was Sie vorgeschlagen haben. Übrigens liegt Ihre ToDescription-Erweiterung weit außerhalb des Bereichs "Enum". Ich denke, das passt gut zum 'Objekt'. Dies ist, was ich verwendet habe, als ich Simons Code genommen und ihn ein bisschen mehr aufgeräumt habe.
Lerner
@Cristi Es ist schwer zu verstehen, was Sie unter "weit außerhalb des Enum-Bereichs" verstehen, aber es scheint, als würden Sie sagen, dass die ToDescription-Erweiterungsmethode nicht stark in die ItemTypes-Enumeration typisiert ist. Dies ist beabsichtigt und macht die Erweiterungsmethode generisch für alle Aufzählungen verwendbar. Wenn Sie es mit einer generischen Erweiterungsmethode vergleichen, gibt es Vor- und Nachteile für jeden Ansatz. Insbesondere wenn Sie generecisieren, können Sie es nicht nur auf Enums beschränken.
Zaid Masud
1
Großartig, danke. Ich habe value.ToString geändert, um eine Erweiterung FromCamelCase zu verwenden, falls keine Beschreibung vorhanden ist. So rolle ich :)
Valamas
33

Also ohne Erweiterungsfunktionen, wenn Sie einfach und unkompliziert suchen. Das habe ich getan

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

Dabei ist XXXXX.Sites.YYYY.Models.State eine Aufzählung

Wahrscheinlich ist es besser, die Hilfsfunktion auszuführen, aber wenn die Zeit knapp ist, wird dies die Arbeit erledigen.

Marty Trenouth
quelle
Es hat gut funktioniert, das Dropdown-Menü auszufüllen, aber wie können Sie den ausgewählten Standardwert in der Razor-Syntax für Html.DropDownListFor festlegen? Ich möchte eine Tabelle mit Kombinationsfeldern mit Aufzählungen anzeigen und den ausgewählten Wert entsprechend dem vorherigen Wert festlegen.
Johncl
2
Sollte in der Lage sein, einen zweiten Parameter mit dem ausgewählten Wert an die neue SelectList-Funktion (IEnumerable, object) zu übergeben. MSDN-Dokumentation: msdn.microsoft.com/en-us/library/dd460123.aspx
Marty Trenouth
23

Wenn Sie die Antworten von Prize und Rune erweitern möchten, möchten Sie den folgenden Code verwenden, wenn das value-Attribut Ihrer ausgewählten Listenelemente dem ganzzahligen Wert des Aufzählungstyps und nicht dem Zeichenfolgenwert zugeordnet werden soll:

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

Anstatt jeden Aufzählungswert als TEnum-Objekt zu behandeln, können wir ihn als Objekt behandeln und ihn dann in eine Ganzzahl umwandeln, um den Wert ohne Box zu erhalten.

Hinweis: Ich habe außerdem eine generische Typeinschränkung hinzugefügt, um die Typen, für die diese Erweiterung verfügbar ist, nur für Strukturen (Enums Basistyp) zu beschränken, und eine Laufzeit-Typüberprüfung, die sicherstellt, dass die übergebene Struktur tatsächlich eine Enum ist.

Update 23.10.12: Generischer Typparameter für zugrunde liegenden Typ hinzugefügt und Nichtkompilierungsproblem behoben, das .NET 4+ betrifft.

Nathan Taylor
quelle
Vielen Dank! Dies war die Antwort, die ich brauchte. Ich speichere den ganzzahligen Wert einer Enum als Spalte in der Datenbank und diese Lösung scheint perfekt zu funktionieren.
Grimus
aber was ist, wenn Sie ein Zeichen und kein int speichern? Welches ist mein Fall. Natürlich könnte ich (int) in (char) ändern, aber wie wäre es auch damit, dieses Generikum zu machen? wie geht das?
Stefanvds
@Stefandvds Dies ist eine großartige Frage in Bezug auf das Casting auf den korrekt dargestellten Typ. Basierend auf den Tests, die ich gerade durchgeführt habe, scheint es die einzige Möglichkeit zu sein, dies zu erreichen, indem Sie den tatsächlichen Typ als einen anderen Typparameter angeben. ToSelectList<TEnum, TEnumValue>(this TEnum enumObj) { ... }
Nathan Taylor
@Stefandvds Siehe diese Frage .
Nathan Taylor
Wenn die Werte Ihrer Aufzählung int sind, können Sie sie einfach verwenden Value = Convert.ToInt32(e). (int)ekompiliert nicht. :(
Andrew
11

Um das Problem zu lösen, dass die Nummer anstelle von Text mithilfe der Erweiterungsmethode von Prise abgerufen wird.

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);
}
ceedee
quelle
Das habe ich gesucht, obwohl es etwas hässlicher ist, als ich gedacht hatte. Ich frage mich , warum Visual Studio lassen Sie nicht direkt geworfen ezu int.
Andrew
Oder Sie könnten einfach verwenden ID = Convert.ToInt32(e).
Andrew
11

Ein super einfacher Weg, dies zu erledigen - ohne all das Erweiterungsmaterial, das übertrieben erscheint, ist dies:

Ihre Aufzählung:

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

Binden Sie die Aufzählung innerhalb Ihres Controllers an eine Liste:

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

Danach werfen Sie es in eine ViewBag:

    ViewBag.RequiredLevel = new SelectList(myLevels);

Zum Schluss binden Sie es einfach an die Ansicht:

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

Dies ist bei weitem der einfachste Weg, den ich gefunden habe, und erfordert keine Erweiterungen oder etwas so Verrücktes.

UPDATE : Siehe Andrews Kommentar unten.

Louie Bacaj
quelle
3
Dies funktioniert nur, wenn Sie Ihrer Aufzählung keinen Wert zugewiesen haben. Wenn Sie dies Level1 = 1getan hätten , wäre der Wert der Dropdown-Liste "Level1"anstelle von 1.
Andrew
11

Die beste Lösung, die ich dafür gefunden habe, war die Kombination dieses Blogs mit der Antwort von Simon Goldstone .

Dies ermöglicht die Verwendung der Aufzählung im Modell. Im Wesentlichen besteht die Idee darin, eine Ganzzahl-Eigenschaft sowie die Aufzählung zu verwenden und die Ganzzahl-Eigenschaft zu emulieren.

Verwenden Sie dann das Attribut [System.ComponentModel.Description], um das Modell mit Ihrem Anzeigetext zu versehen, und verwenden Sie in Ihrer Ansicht die Erweiterung "EnumDropDownListFor".

Dies macht sowohl die Ansicht als auch das Modell sehr lesbar und wartbar.

Modell:

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

Aussicht:

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

Erweiterung (der Vollständigkeit halber direkt aus der Antwort von Simon Goldstone ):

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);
        }
    }
}
Nick Evans
quelle
Das funktioniert nicht, MVC 4 Razor. In der Ansicht oder Laufzeit ist error = "Der Aufruf ist zwischen den folgenden Methoden oder Eigenschaften nicht eindeutig. 'LDN.Extensions.MvcExtensions.EnumDropDownListFor <MyModel, LDN.Models.YesPartialNoEnum?> (System.Web.Mvc.HtmlHelper <MyModel>, System .Linq.Expressions.Expression <System.Func <MyModel, LDN.Models.YesPartialNoEnum? >>) 'und .... "und genau dieselbe Methode mit denselben Requisiten, die erneut wiederholt wurden (hier sind nicht genügend Zeichen zulässig).
Marc
9

Sie möchten etwas wie verwenden Enum.GetValues

Garry Shutler
quelle
8
@Html.DropDownListFor(model => model.Type, Enum.GetNames(typeof(Rewards.Models.PropertyType)).Select(e => new SelectListItem { Text = e }))
Mr. Pumpkin
quelle
Gut! Wie kann man auf diese Weise Wert und Text von enum erhalten? Ich meine, ich habe SomeEnum {some1 = 1, some2 = 2}. Ich muss Zahlen (1, 2) für den Wert und Text (some1, some2) für den Text der Auswahlliste erhalten
Dmitresky
7

Dies sind Rune & Prize-Antworten, die geändert wurden, um den Enum- intWert als ID zu verwenden.

Beispielaufzählung:

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

Verlängerungsmethode:

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

Verwendungsbeispiel:

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

Denken Sie daran, den Namespace zu importieren, der die Erweiterungsmethode enthält

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

Beispiel für generiertes HTML:

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

Beachten Sie, dass das Element, mit dem Sie das Ein aufrufen, ToSelectListdas ausgewählte Element ist.

Mr. Flibble
quelle
Oder Sie könnten einfach verwenden Id = Convert.ToInt32(e).
Andrew
6

Dies ist die Version für 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)
user550950
quelle
Dies funktioniert nur, wenn Ihre Aufzählung aus zusammenhängenden Werten besteht, die mit 0 beginnen. Eine Flags-Aufzählung würde damit nicht funktionieren. Kreative Verwendung der indizierten Auswahl.
Suncat2000
6

In .NET Core können Sie Folgendes verwenden:

@Html.DropDownListFor(x => x.Foo, Html.GetEnumSelectList<MyEnum>())
Goldenes Zeitalter
quelle
1
Oder mit dem Tag-Helfer <select asp-for="Model.Foo" class="form-control" asp-items="@Html.GetEnumSelectList<MyEnum>()"></select>.
Pascal R.
Ja, ich würde sagen, Tag-Helfer sind noch besser, da das Format näher am reinen HTML liegt;)
GoldenAge
Sie können dies auch tun @ Html.DropDownListFor (x => x.Foo, Html.GetEnumSelectList (typeof (FooEnum)))
Fereydoon Barikzehy
5

Aufbauend auf Simons Antwort besteht ein ähnlicher Ansatz darin, die Enum-Werte aus einer Ressourcendatei anzuzeigen, anstatt in einem Beschreibungsattribut innerhalb der Enum selbst. Dies ist hilfreich, wenn Ihre Site in mehr als einer Sprache gerendert werden muss und wenn Sie eine bestimmte Ressourcendatei für Enums haben möchten, können Sie einen Schritt weiter gehen und nur Enum-Werte in Ihrer Enum haben und auf diese aus der Erweiterung von verweisen eine Konvention wie [EnumName] _ [EnumValue] - letztendlich weniger Tippen!

Die Erweiterung sieht dann so aus:

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

Ressourcen in der Enums.Resx-Datei, die wie ItemTypes_Movie: Film aussehen

Eine andere Sache, die ich gerne mache, ist, anstatt die Erweiterungsmethode direkt aufzurufen, sie lieber mit einem @ Html.EditorFor (x => x.MyProperty) aufzurufen oder im Idealfall nur die gesamte Form in einem ordentlichen @ zu haben Html.EditorForModel (). Dazu ändere ich die Zeichenfolgenvorlage so, dass sie so aussieht

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

Wenn Sie das interessiert, habe ich hier in meinem Blog eine viel detailliertere Antwort veröffentlicht:

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

PaulTheCyclist
quelle
5

Nun, ich bin sehr spät zur Party, aber für das, was es wert ist, habe ich über genau dieses Thema gebloggt, wobei ich eine EnumHelperKlasse erstelle , die eine sehr einfache Transformation ermöglicht.

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

In Ihrem 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);

Aus Ihrer Sicht:

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

Die Helferklasse:

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

Ich bin sehr spät dran, aber ich habe gerade einen wirklich coolen Weg gefunden, dies mit einer Codezeile zu tun, wenn Sie gerne das Unconstrained Melody NuGet-Paket hinzufügen (eine schöne, kleine Bibliothek von Jon Skeet).

Diese Lösung ist besser, weil:

  1. Es stellt sicher (mit generischen Typbeschränkungen), dass der Wert wirklich ein Aufzählungswert ist (aufgrund der nicht eingeschränkten Melodie).
  2. Es vermeidet unnötiges Boxen (aufgrund der uneingeschränkten Melodie)
  3. Es werden alle Beschreibungen zwischengespeichert, um zu vermeiden, dass bei jedem Anruf eine Reflexion verwendet wird (aufgrund der uneingeschränkten Melodie).
  4. Es ist weniger Code als die anderen Lösungen!

Hier sind die Schritte, um dies zum Laufen zu bringen:

  1. In der Package Manager-Konsole "Install-Package UnconstrainedMelody"
  2. Fügen Sie Ihrem Modell eine Eigenschaft wie folgt hinzu:

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

Nachdem Sie die Liste von SelectListItem in Ihrem Modell verfügbar gemacht haben, können Sie @ Html.DropDownList oder @ Html.DropDownListF verwenden, um diese Eigenschaft als Quelle zu verwenden.

nootn
quelle
+1 für die Verwendung von Jon Skeets Code :), nur ein Scherz gut
Vamsi
3

Ein weiterer Fix für diese Erweiterungsmethode: Die aktuelle Version hat den aktuellen Wert der Aufzählung nicht ausgewählt. Ich habe die letzte Zeile repariert:

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

Wenn Sie Lokalisierungsunterstützung hinzufügen möchten, ändern Sie einfach die Methode s.toString () wie folgt:

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

Hier ist der Typ (Ressourcen) die Ressource, die Sie laden möchten, und dann erhalten Sie den lokalisierten String, der auch nützlich ist, wenn Ihr Enumerator Werte mit mehreren Wörtern enthält.

Brafales
quelle
3

Dies ist meine Version der Hilfsmethode. Ich benutze das:

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

Stattdessen:

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

Hier ist es:

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);
    }
Vadim Sentiaev
quelle
3

Sie können auch meine benutzerdefinierten HTML-Helfer in Griffin.MvcContrib verwenden. Der folgende Code:

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

Erzeugt:

Geben Sie hier die Bildbeschreibung ein

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

jgauffin
quelle
3

Ich möchte diese Frage auf eine andere Art und Weise beantworten, bei der der Benutzer nichts in controlleroder LinqAusdruck tun muss . Diesen Weg...

Ich habe ein 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*/
    }

Jetzt kann ich einfach ein erstellen dropdown indem dies benutze enum.

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

ODER

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

Wenn Sie einen Index auswählen möchten, versuchen Sie dies

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

Hier habe ich AccessLevelEnum.NoAccessals zusätzlichen Parameter für die Standardauswahl der Dropdown-Liste verwendet.

gdmanandamohon
quelle
3

Ich habe hier eine Antwort gefunden . Einige meiner Aufzählungen haben jedoch [Description(...)]Attribute, daher habe ich den Code geändert, um dies zu unterstützen:

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

Ich hoffe, das hilft.

Alkasai
quelle
Ich möchte ein Mitglied vom Typ = DropdownList zurückgeben. Ich bin gut mit dem Text = DescriptionAttribute, aber es
fällt mir
2

@ Simon Goldstone: Danke für Ihre Lösung, sie kann in meinem Fall perfekt angewendet werden. Das einzige Problem ist, dass ich es in VB übersetzen musste. Aber jetzt ist es geschafft und um die Zeit anderer Leute zu sparen (falls sie es brauchen), habe ich es hier eingefügt:

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

Ende Du benutzt es so:

@Html.EnumDropDownListFor(Function(model) (model.EnumField))
Michal B.
quelle
2

Am Ende habe ich Erweiterungsmethoden entwickelt, um das zu tun, was hier im Wesentlichen die akzeptierte Antwort ist. Die letzte Hälfte des Gist befasst sich speziell mit Enum.

https://gist.github.com/3813767

Nick Albrecht
quelle
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 ----"}
}
)
Shahnawaz
quelle
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" }

})
vicky
quelle
Ich denke, dies ist keine gültige Antwort, es wird überhaupt nicht die Aufzählung verwendet, um die Dropdown-Liste zu füllen.
Andrew