Bagaimana Anda membuat dropdownlist dari enum di ASP.NET MVC?

671

Saya mencoba menggunakan Html.DropDownListmetode ekstensi tetapi tidak tahu cara menggunakannya dengan enumerasi.

Katakanlah saya memiliki penghitungan seperti ini:

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

Bagaimana cara saya membuat dropdown dengan nilai-nilai ini menggunakan Html.DropDownListmetode ekstensi?

Atau apakah taruhan terbaik saya adalah dengan membuat for for dan membuat elemen Html secara manual?

Kevin Pang
sumber

Jawaban:

842

Untuk MVC v5.1 gunakan Html.EnumDropDownListFor

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

Untuk MVC v5 gunakan EnumHelper

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

Untuk MVC 5 dan lebih rendah

Saya menggulung jawaban Rune ke dalam metode ekstensi:

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

Ini memungkinkan Anda untuk menulis:

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

oleh using MyApp.Common

Martin Faartoft
sumber
13
Saya tidak bisa menyelesaikannya, bisakah Anda membantu. Ketika saya melakukan Post.PostType.ToSelectList (); itu tidak mengenali ekstensi?
Barbaros Alp
3
Saya juga tidak bisa membuatnya bekerja. Apakah Status Properti Enum Anda ada di kelas tugas? Bukankah ini salah satu dari nilai yang disebutkan?
Daryl
9
Anda dapat membatasi sedikit dengan: di mana T: struct, IConvertible See: stackoverflow.com/questions/79126/…
Richard Garside
8
Ini keren. Jika ada yang berjuang dengan implementasi, inilah cara saya melakukannya. Menambahkan kelas EnumHelpers ke folder HtmlHelpers. Digunakan kode di atas. Menambahkan namespace per rekomendasi @TodK: <add namespace = "xxx.HtmlHelpers" />. Lalu saya menggunakannya di halaman silet seperti: @ Html.DropDownListFor (model => model.Status, @ Model.Status.ToSelectList ()) HTH
Jeff Borden
6
Perhatikan bahwa di yang lebih baru ASP.NET MVCada cara asli: stackoverflow.com/a/22295360/1361084
Ofiris
359

Saya tahu saya terlambat ke pesta tentang hal ini, tetapi saya pikir Anda mungkin menemukan varian ini berguna, karena yang ini juga memungkinkan Anda untuk menggunakan string deskriptif daripada konstanta enumerasi dalam drop down. Untuk melakukan ini, hiasi setiap entri enumerasi dengan atribut [System.ComponentModel.Description].

Sebagai contoh:

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

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

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

Ini kode saya:

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

Anda kemudian dapat melakukan ini dalam pandangan Anda:

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

Semoga ini bisa membantu Anda!

** EDIT 2014-JAN-23: Microsoft baru saja merilis MVC 5.1, yang sekarang memiliki fitur EnumDropDownListFor. Sayangnya itu tampaknya tidak menghormati atribut [Deskripsi] sehingga kode di atas masih berdiri. Lihat bagian Enum di catatan rilis Microsoft untuk MVC 5.1.

Pembaruan: Itu memang mendukung atribut Display[Display(Name = "Sample")] , jadi orang dapat menggunakannya.

[Perbarui - perhatikan saja ini, dan kode ini terlihat seperti versi kode yang diperluas di sini: https://blogs.msdn.microsoft.com/stuartleeks/2010/05/21/asp-net-mvc-creating-a- dropdownlist-helper-for-enums / , dengan beberapa tambahan. Jika demikian, atribusi akan tampak adil ;-)]

SimonGoldstone
sumber
28
+1 Saya menemukan ini yang paling berguna dari semua jawaban di sini. Saya bisa mengubahnya menjadi sepotong kode yang sangat dapat digunakan kembali. Terima kasih!
Ed Charbeneau
43
Visual Studio memiliki bug aneh di mana jika Anda tidak referensi System.Web.Mvc.Htmlmaka dikatakan itu DropDownListFortidak dapat ditemukan, tetapi juga tidak bisa menyelesaikannya. Anda harus melakukannya secara manual using System.Web.Mvc.Html;. Asal tahu saja.
Kezzer
1
Saya memiliki varian ini di intisari yang kami gunakan di semua proyek kami: gist.github.com/1287511
kamranicus
1
Solusi hebat, terima kasih, akan lebih baik lagi jika Anda dapat men-cache hasil GetEnumDescription
M. Mennan Kara
17
MVC 5.1 EnumDropDownListFor baru tidak menggunakan [Deskripsi ("")] tetapi ia menggunakan [Tampilan (Nama = "")]! Selamat menikmati :)
Supergibbs
195

Di ASP.NET MVC 5.1 , mereka menambahkan EnumDropDownListFor()helper, jadi tidak perlu untuk ekstensi khusus:

Model :

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

Lihat :

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

Menggunakan Tag Helper (ASP.NET MVC 6) :

<select asp-for="@Model.SelectedValue" asp-items="Html.GetEnumSelectList<MyEnum>()">
Ofiris
sumber
21
Ini perlu ditabrak sampai tempat pertama entah bagaimana
3
Anda harus membuat pertanyaan baru yang khusus untuk MVC 5.1 dan menempatkan ini sebagai jawabannya, lalu mengirimkan saya tautan ke posting sehingga saya dapat meningkatkan suara favorit.
Kevin Heidt
2
Apa yang saya tidak suka tentang EnumDropDownListFor () adalah bahwa ia menyimpan ke dalam DB nilai int enum, bukan teks, jadi jika Anda pernah memilih untuk menambahkan item enum baru, itu harus pergi pada akhir daftar , agar tidak kehilangan hubungan nilai int basis data yang disimpan dengan posisi asli item enum. Itu adalah batasan yang tidak perlu jika teks disimpan. Plus, saya lebih suka melihat db dan melihat teks, daripada ints di mana saya kemudian harus mencari nilai teks di tempat lain. Kalau tidak, html helper ini sangat nyaman digunakan.
Giovanni
2
@Giovanni - Anda dapat menentukan nilai numerik Anda sendiri.
Tommy
1
@Giovanni Desain yang ketat harus memberikan nilai untuk setiap entri enum (jika penting), jika tidak nilai tidak menjadi masalah (dan menempatkan yang baru di akhir seharusnya tidak menjadi masalah). Menyimpan nilai int lebih baik dalam hal menghemat penyimpanan dan meningkatkan kinerja (saat melakukan beberapa pencarian).
King King
130

Saya bertemu dengan masalah yang sama, menemukan pertanyaan ini, dan berpikir bahwa solusi yang diberikan oleh Ash bukan yang saya cari; Harus membuat HTML sendiri berarti kurang fleksibel dibandingkan dengan Html.DropDownList()fungsi bawaan.

Ternyata C # 3 dll membuat ini sangat mudah. Saya telah sebuah enumdisebut 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);

Ini menghasilkan ol yang bagus SelectListyang bisa digunakan seperti yang biasa Anda lihat:

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

Tipe anonim dan LINQ membuat IMHO ini jauh lebih elegan. Tidak ada pelanggaran yang dimaksudkan, Ash. :)

Rune Jacobsen
sumber
jawaban yang bagus! Saya berharap seseorang akan menggunakan LINQ dan SelectList :) Senang saya memeriksa di sini dulu!
Pure.Krome
1
ID = s beri saya DataTextField bukan nilainya? Apa yang mungkin menjadi alasannya? Terima kasih
Barbaros Alp
1
Rune, saya menggunakan metode yang sama dan DropDownList TIDAK me-render ketika memposting ke server, itu tidak menyimpan nilai yang saya pilih.
clockwiseq
5
@BarbarosAlp Agar ID menjadi nomor, Anda harus memasukkan enum ke int:select new { ID = (int)s, Name = s.ToString() };
Keith
Ini adalah jawaban yang paling saya sukai karena kesederhanaannya. Malu Anda tidak menerima kredit yang cukup karena jawaban yang dipilih menggunakan solusi Anda.
anar khalilov
63

Berikut ini adalah solusi enkapsulasi yang lebih baik:

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

Katakan di sini adalah model Anda:

masukkan deskripsi gambar di sini

Penggunaan sampel:

masukkan deskripsi gambar di sini

UI yang dihasilkan: masukkan deskripsi gambar di sini

Dan menghasilkan HTML

masukkan deskripsi gambar di sini

Snapshot Kode Sumber Ekstensi Pembantu:

masukkan deskripsi gambar di sini

Anda dapat mengunduh proyek sampel dari tautan yang saya berikan.

EDIT: Inilah kodenya:

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
sumber
2
Hanya pendapat saya, tapi saya pikir jawaban ini jauh lebih bersih daripada jawaban yang diterima. Saya sangat suka opsi menggunakan atribut Deskripsi. Saya menambahkan kode sehingga orang dapat menyalin / menempelnya tanpa mengunduh.
Ben Mills
Panggil metode ekstensi sebagai EnumDropDownListFor daripada DropDownListFor Usage: -> @ Html.EnumDropDownListFor (x => x.Gender)
sandeep talabathula
Untuk Seseorang Mencari Menambahkan satu elemen lagi "Silakan Pilih" return htmlHelper.DropDownListFor (ekspresi, buatSelectList (ekspresi.ReturnType, dipilih, firstElement), "Silakan Pilih");
Sandeep
1
Bekerja dengan baik! Namun, pada halaman Detail, DisplayFor () menunjukkan nilai enum yang dipilih, bukan deskripsi yang sesuai. Saya kira ini panggilan untuk kelebihan untuk DisplayFor () untuk tipe enum. Adakah yang punya solusi untuk ini?
corix010
48

Html.DropDownListFor hanya membutuhkan IEnumerable, jadi alternatif untuk solusi Prise adalah sebagai berikut. Ini memungkinkan Anda untuk menulis:

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

[Di mana SelectedItemType adalah bidang pada model ItemTypes jenis Anda, dan model Anda bukan nol]

Selain itu, Anda tidak perlu membuat generik metode ekstensi karena Anda dapat menggunakan enumValue.GetType () daripada typeof (T).

EDIT: Solusi Simon terintegrasi di sini juga, dan termasuk metode ekstensi 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();
    }
}
Zaid Masud
sumber
Tidak berfungsi untuk saya ('System.NullReferenceException: Referensi objek tidak disetel ke instance objek.') ... 'Model' saya adalah null ... mungkin ada hubungannya dengan 'GetNonNullableModelType' yang Simon miliki termasuk
Pelajar
@ Cristi, Anda benar solusi ini tidak dimaksudkan untuk digunakan dalam kondisi di mana Model Anda adalah nol. Saya mencoba untuk menghindari desain seperti itu secara umum dan menginisialisasi model "Kosong" ketika itu terjadi.
Zaid Masud
Yah, saya baru mengenal asp MVC, tetapi saya memiliki cukup pengalaman dalam. Net. Terima kasih, saya akan melihat apa yang Anda sarankan. Btw ekstensi ToDescription Anda jauh di luar cakupan 'Enum'. Saya kira cocok untuk 'Objek' itu sendiri. Ini yang saya gunakan ketika saya mengambil kode Simon dan membersihkannya sedikit lebih banyak.
Learner
@Cristi sulit untuk memahami apa yang Anda maksud dengan "jauh di luar lingkup 'Enum'" tetapi sepertinya Anda mengatakan bahwa metode ekstensi ToDescription tidak diketik dengan kuat ke ItemTypes enum? Ini disengaja dan membuat metode ekstensi secara umum dapat digunakan oleh semua enum. Jika Anda membandingkannya dengan metode ekstensi generik, ada pro dan kontra dari setiap pendekatan. Secara khusus, jika Anda melakukan generalisasi, Anda tidak dapat membuatnya dibatasi hanya pada enum.
Zaid Masud
1
Hebat, terima kasih. Saya mengubah value.ToString untuk menggunakan ekstensi FromCamelCase seandainya tidak ada deskripsi. Begitulah cara saya roll :)
Valamas
33

Jadi tanpa fungsi Extension jika Anda mencari yang sederhana dan mudah .. Inilah yang saya lakukan

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

di mana XXXXX.Sites.YYYY.Models.State adalah enum

Mungkin lebih baik melakukan fungsi helper, tetapi ketika waktu singkat ini akan menyelesaikan pekerjaan.

Marty Trenouth
sumber
Bagus ini berhasil mengisi dropdown tetapi bagaimana Anda menetapkan nilai yang dipilih default dalam sintaks Razor untuk Html.DropDownListFor? Saya ingin menunjukkan meja dengan kotak kombo enum dan saya perlu mengatur nilai yang dipilih juga sesuai dengan apa yang sebelumnya.
Johncl
2
Seharusnya dapat melewatkan parameter kedua dengan nilai yang dipilih ke fungsi SelectList (IEnumerable, objek) yang baru. Dokumentasi MSDN: msdn.microsoft.com/en-us/library/dd460123.aspx
Marty Trenouth
23

Memperluas jawaban Hadiah dan Rune, jika Anda ingin memiliki atribut nilai peta item daftar pilih Anda ke nilai integer dari tipe Enumerasi, daripada nilai string, gunakan kode berikut:

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

Alih-alih memperlakukan setiap nilai Enumerasi sebagai objek TEnum, kita bisa memperlakukannya sebagai objek lalu melemparkannya ke integer untuk mendapatkan nilai yang tidak dikotak.

Catatan: Saya juga menambahkan batasan tipe generik untuk membatasi tipe yang ekstensi ini hanya tersedia untuk struct (tipe dasar Enum), dan validasi tipe run-time yang memastikan bahwa struct yang dilewati memang merupakan Enum.

Pembaruan 10/23/12: Menambahkan parameter tipe umum untuk tipe dasar dan memperbaiki masalah non-kompilasi yang mempengaruhi .NET 4+.

Nathan Taylor
sumber
Terima kasih! Inilah jawaban yang saya butuhkan. Saya menyimpan nilai integer Enum sebagai kolom dalam database dan solusi ini tampaknya berfungsi dengan baik.
grimus
tetapi bagaimana jika Anda menyimpan arang dan bukan int? yang merupakan kasus saya. jelas saya bisa mengubah (int) ke (char) tetapi bagaimana dengan membuat generik ini juga. bagaimana cara melakukannya?
Stefanvds
@Stefandvds Ini adalah pertanyaan yang bagus untuk casting ke tipe yang diwakili dengan benar. Berdasarkan tes yang baru saja saya lakukan, tampaknya satu-satunya cara Anda dapat mencapai ini adalah dengan menentukan tipe aktual sebagai parameter tipe lain. ToSelectList<TEnum, TEnumValue>(this TEnum enumObj) { ... }
Nathan Taylor
@Stefandvds Lihat pertanyaan ini .
Nathan Taylor
Jika nilai enum Anda int, Anda bisa menggunakannya Value = Convert.ToInt32(e). (int)etidak dikompilasi. :(
Andrew
11

Untuk mengatasi masalah mendapatkan nomor alih-alih teks menggunakan metode ekstensi 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);
}
kopi kecil
sumber
Itulah yang saya cari, meskipun itu lebih jelek dari yang saya kira perlu. Saya bertanya-tanya mengapa Visual Studio tidak akan membiarkan Anda langsung dilemparkan eke int.
Andrew
Atau Anda bisa menggunakannya ID = Convert.ToInt32(e).
Andrew
11

Cara super mudah untuk menyelesaikan ini - tanpa semua ekstensi yang tampaknya berlebihan adalah ini:

Enum Anda:

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

Di dalam pengontrol Anda mengikat Enum ke Daftar:

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

Setelah itu buang ke dalam ViewBag:

    ViewBag.RequiredLevel = new SelectList(myLevels);

Akhirnya cukup ikat ke View:

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

Sejauh ini, ini adalah cara termudah yang saya temukan dan tidak memerlukan ekstensi atau apapun yang gila.

UPDATE : Lihat komentar Andrews di bawah ini.

Louie Bacaj
sumber
3
Ini hanya berfungsi jika Anda belum menetapkan nilai apa pun pada enum Anda. Jika sudah Level1 = 1, maka nilai dropdown akan menjadi "Level1"bukan 1.
Andrew
11

Solusi terbaik yang saya temukan untuk ini adalah menggabungkan blog ini dengan jawaban Simon Goldstone .

Ini memungkinkan penggunaan enum dalam model. Intinya idenya adalah menggunakan properti integer serta enum, dan meniru properti integer.

Kemudian gunakan atribut [System.ComponentModel.Description] untuk membubuhi keterangan model dengan teks tampilan Anda, dan gunakan ekstensi "EnumDropDownListFor" di tampilan Anda.

Ini membuat tampilan dan model sangat mudah dibaca dan dipelihara.

Model:

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

Melihat:

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

Ekstensi (langsung dari jawaban Simon Goldstone , termasuk di sini untuk kelengkapan):

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
sumber
Ini tidak bekerja, MVC 4 Razor. Dalam tampilan atau runtime, error = "Panggilan ini tidak jelas antara metode atau properti berikut ini 'LDN.Extensions.MvcExtensions.EnumDropDownListFor <MyModel, LDN.Models.YesPartialNoEnum?> (System.Web.Mvc.HtmlHelper <MyModel>, System .Linq.Expressions.Expression <System.Func <MyModel, LDN.Models.YesPartialNoEnum? >>) 'dan .... "dan metode yang sama persis dengan alat peraga yang sama diulangi lagi (tidak cukup karakter diizinkan di sini).
Marc
9

Anda ingin melihat menggunakan sesuatu seperti Enum.GetValues

Garry Shutler
sumber
8
@Html.DropDownListFor(model => model.Type, Enum.GetNames(typeof(Rewards.Models.PropertyType)).Select(e => new SelectListItem { Text = e }))
Tuan Pumpkin
sumber
Baik! Bagaimana cara mendapatkan nilai dan teks dari enum dengan cara ini? Maksud saya, saya memiliki SomeEnum {some1 = 1, some2 = 2} Saya perlu mendapatkan angka (1, 2) untuk nilai dan teks (some1, some2) untuk teks daftar pilih
Dmitresky
7

Ini adalah jawaban Rune & Hadiah yang diubah untuk menggunakan nilai Enum intsebagai ID.

Sampel Enum:

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

Metode ekstensi:

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

Contoh penggunaan:

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

Ingatlah untuk Mengimpor namespace yang berisi metode Extension

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

Contoh HTML yang dihasilkan:

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

Perhatikan bahwa item yang Anda gunakan untuk memanggil ToSelectListadalah item yang dipilih.

Tuan Flibble
sumber
Atau Anda bisa menggunakannya Id = Convert.ToInt32(e).
Andrew
6

Ini adalah versi untuk 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)
pengguna550950
sumber
Itu akan bekerja hanya jika enum Anda terdiri dari nilai yang berdekatan dimulai dengan 0. Sebuah flag enum tidak akan bekerja dengan ini. Penggunaan kreatif dari Select yang diindeks.
Suncat2000
6

Di .NET Core Anda bisa menggunakan ini:

@Html.DropDownListFor(x => x.Foo, Html.GetEnumSelectList<MyEnum>())
Zaman keemasan
sumber
1
Atau dengan penolong tag <select asp-for="Model.Foo" class="form-control" asp-items="@Html.GetEnumSelectList<MyEnum>()"></select>.
Pascal R.
ya, Id mengatakan bantuan tag bahkan lebih baik karena formatnya lebih dekat ke HTML murni;)
GoldenAge
Anda juga dapat melakukan ini @ Html.DropDownListFor (x => x.Foo, Html.GetEnumSelectList (typeof (FooEnum)))
Fereydoon Barikzehy
5

Membangun pada jawaban Simon, pendekatan yang sama adalah untuk mendapatkan nilai-nilai Enum untuk ditampilkan dari file Sumber Daya, bukan dalam atribut deskripsi dalam Enum itu sendiri. Ini bermanfaat jika situs Anda perlu diterjemahkan dalam lebih dari satu bahasa dan jika Anda memiliki file sumber daya spesifik untuk Enums, Anda dapat melangkah lebih jauh dan hanya memiliki nilai Enum, di Enum Anda dan merujuknya dari ekstensi dengan sebuah konvensi seperti [EnumName] _ [EnumValue] - akhirnya kurang mengetik!

Ekstensi tersebut kemudian terlihat seperti:

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

Sumber daya dalam file Enums.Resx tampak seperti ItemTypes_Movie: Film

Satu hal lain yang ingin saya lakukan adalah, daripada memanggil metode ekstensi secara langsung, saya lebih suka menyebutnya dengan @ Html.EditorFor (x => x.MyProperty), atau idealnya hanya memiliki seluruh bentuk, dalam satu @ rapi Html.EditorForModel (). Untuk melakukan ini, saya mengubah templat string menjadi seperti ini

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

Jika ini menarik minat Anda, saya telah memberikan jawaban yang jauh lebih rinci di blog saya:

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

PaulTheCyclist
sumber
5

Yah saya benar-benar terlambat ke pesta, tetapi untuk apa nilainya, saya telah membuat blog tentang subjek ini di mana saya membuat EnumHelperkelas yang memungkinkan transformasi yang sangat mudah.

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

Di controller Anda:

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

Menurut Anda:

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

Kelas pembantu:

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
sumber
4

Saya sangat terlambat dalam hal ini, tetapi saya baru saja menemukan cara yang sangat keren untuk melakukan ini dengan satu baris kode, jika Anda senang menambahkan paket Melody NuGet yang tidak dibatasi (perpustakaan kecil yang bagus dari Jon Skeet).

Solusi ini lebih baik karena:

  1. Ini memastikan (dengan batasan tipe generik) bahwa nilainya benar-benar merupakan nilai enum (karena Melodi Tidak Dibatasi)
  2. Ini menghindari tinju yang tidak perlu (karena Melody yang tidak dibatasi)
  3. Itu cache semua deskripsi untuk menghindari menggunakan refleksi pada setiap panggilan (karena Melody yang tidak dibatasi)
  4. Itu kode kurang dari solusi lain!

Jadi, inilah langkah-langkah untuk membuatnya bekerja:

  1. Di Package Manager Console, "Instal-Paket Tidak Terbatas"
  2. Tambahkan properti pada model Anda seperti ini:

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

Sekarang Anda memiliki Daftar SelectListItem terbuka pada model Anda, Anda dapat menggunakan @ Html.DropDownList atau @ Html.DropDownListFor menggunakan properti ini sebagai sumber.

nootn
sumber
+1 untuk menggunakan kode Jon Skeet :), hanya bercanda bagus
Vamsi
3

Perbaikan lain untuk metode ekstensi ini - versi saat ini tidak memilih nilai enum saat ini. Saya memperbaiki baris terakhir:

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
sumber
3

Jika Anda ingin menambahkan dukungan lokalisasi, ubah saja metode s.toString () menjadi seperti ini:

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

Di sini typeof (Sumber) adalah sumber yang ingin Anda muat, dan kemudian Anda mendapatkan String yang dilokalkan, juga berguna jika enumerator Anda memiliki nilai dengan banyak kata.

brafales
sumber
3

Ini adalah versi metode penolong saya. Saya menggunakan ini:

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

Alih-alih itu:

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

Ini dia:

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
sumber
3

Anda juga dapat menggunakan HtmlHelpers khusus saya di Griffin.MvcContrib. Kode berikut:

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

Menghasilkan:

masukkan deskripsi gambar di sini

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

jgauffin
sumber
3

Saya ingin menjawab pertanyaan ini dengan cara yang berbeda di mana, pengguna tidak perlu melakukan apa pun di dalam controlleratau Linqekspresi. Cara ini...

Saya punya 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*/
    }

Sekarang saya hanya bisa membuat dropdowndengan menggunakan ini enum.

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

ATAU

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

Jika Anda ingin membuat indeks dipilih maka coba ini

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

Di sini saya telah menggunakan AccessLevelEnum.NoAccesssebagai parameter tambahan untuk memilih dropdown secara default.

gdmanandamohon
sumber
3

Saya menemukan jawaban di sini . Namun, beberapa enum saya memiliki [Description(...)]atribut, jadi saya telah memodifikasi kode untuk memberikan dukungan untuk itu:

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

Semoga itu bisa membantu.

Alkasai
sumber
Saya ingin mengembalikan anggota type = DropdownList. Saya pandai dengan Text = DescriptionAttribute tapi sulit untuk mendapatkan nilai int dari Value
NanaFadanvis
2

@Simon Goldstone: Terima kasih atas solusi Anda, ini dapat diterapkan dengan sempurna dalam kasus saya. Satu-satunya masalah adalah saya harus menerjemahkannya ke VB. Tapi sekarang sudah selesai dan untuk menghemat waktu orang lain (jika mereka membutuhkannya) saya taruh di sini:

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

End Anda menggunakannya seperti ini:

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

Saya akhirnya menciptakan metode ekstensi untuk melakukan apa yang pada dasarnya adalah jawaban terima di sini. Setengah terakhir dari Gist berkaitan dengan Enum secara khusus.

https://gist.github.com/3813767

Nick Albrecht
sumber
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
sumber
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
sumber
Saya pikir ini bukan jawaban yang valid, itu tidak menggunakan enum sama sekali untuk mengisi dropdown.
Andrew