Як створити спадні списки з перерахунку в ASP.NET MVC?


669

Я намагаюся використовувати Html.DropDownListметод розширення, але не можу зрозуміти, як його використовувати при перерахунку.

Скажімо, у мене таке перерахування:

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

Як мені створити спадне меню з цими значеннями за допомогою Html.DropDownListметоду розширення?

Або моя найкраща ставка - просто створити цикл for і створити елементи Html вручну?

Відповіді:


841

Для MVC v5.1 використовуйте Html.EnumDropDownListFor

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

Для MVC v5 використовуйте EnumHelper

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

Для MVC 5 і нижче

Відповідь Руни я перевів у метод розширення:

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

Це дозволяє писати:

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

від using MyApp.Common


13
Я не міг змусити його працювати, чи можете ви, будь ласка, допомогти. Коли я роблю Post.PostType.ToSelectList (); він не визнає розширення?
Barbaros Alp

3
Я також не міг змусити цього працювати. Чи статус "Ваша властивість Enum" у класі завдань? Хіба це не одне із перелічених значень?
Дарил

9
Ви можете обмежити його трохи з: де T: структура, IConvertible Див: stackoverflow.com/questions/79126 / ...
Річард Гарсайд

8
Це круто. Якщо хтось бореться з впровадженням, ось як я це зробив. До папки HtmlHelpers додано клас EnumHelpers. Використовувався вищевказаний код. Додано простір імен за рекомендацією @TodK: <додати простір імен = "xxx.HtmlHelpers" />. Тоді я використав її на сторінці бритви на зразок такої: @ Html.DropDownListFor (model => model.Status, @ Model.Status.ToSelectList ()) HTH
Jeff Borden

6
Зверніть увагу , що в новіших ASP.NET MVCє нативний спосіб: stackoverflow.com/a/22295360/1361084
Ofiris

359

Я знаю, що я запізнився на вечірку з цього приводу, але подумав, що цей варіант вам може бути корисним, оскільки цей також дозволяє використовувати описові рядки, а не константи перерахування у спадному меню. Для цього прикрасьте кожен запис перерахування атрибутом [System.ComponentModel.Description].

Наприклад:

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

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

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

Ось мій код:

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

Потім ви можете зробити це на ваш погляд:

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

Сподіваюся, це допоможе вам!

** EDIT 2014-JAN-23: Microsoft щойно випустила MVC 5.1, який тепер має функцію EnumDropDownListFor. На жаль, схоже, він не поважає атрибут [Опис], тому код вище все ще стоїть. Дивіться розділ Enum у примітках про випуск Microsoft для MVC 5.1.

Оновлення: Хоча він підтримує атрибут Display[Display(Name = "Sample")] , тому можна використовувати його.

[Оновлення - щойно це помітили, і код виглядає як розширена версія коду тут: https://blogs.msdn.microsoft.com/stuartleeks/2010/05/21/asp-net-mvc-creating-a- спадний список-помічник-для перерахунків / , з парою доповнень. Якщо так, атрибуція здасться справедливою ;-)]


28
+1 Я знайшов це найкориснішим з усіх відповідей тут. Я зміг перетворити це на шматок багаторазового використання. Дякую!
Ед Шарбено

43
У Visual Studio є дивна помилка, де, якщо ви не посилаєтесь, System.Web.Mvc.Htmlто вона каже, що DropDownListForїї неможливо знайти, але і її неможливо вирішити. Ви повинні це зробити вручну using System.Web.Mvc.Html;. Просто так ви знаєте.
Kezzer

1
У мене є варіант цього в суті, яку ми використовуємо у всіх наших проектах: gist.github.com/1287511
kamranicus

1
Чудове рішення, спасибі, буде ще краще, якщо ви зможете кешувати результати GetEnumDescription
М. Меннан Кара

17
Новий MVC 5.1 EnumDropDownListFor не використовує [Опис ("")], але він використовує [Display (Name = "")]! Насолоджуйтесь :)
Supergibbs

195

У ASP.NET MVC 5.1 вони додали EnumDropDownListFor()помічник, тому не потрібно використовувати спеціальні розширення:

Модель :

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

Вид :

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

Використання помічника тегів (ASP.NET MVC 6) :

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

21
Це потрібно якось

3
Вам слід створити нове запитання, характерне для MVC 5.1, і поставити це як відповідь, а потім надіслати мені посилання на пост, щоб я міг схвалити вибране.
Кевін Хайдт

2
Що мені не подобається в EnumDropDownListFor (), це те, що він зберігає в БД значення int значення enum, а не текст, тому якщо ви коли-небудь вирішите додати новий елемент enum, він обов'язково повинен пройти в кінці списку , щоб не втратити відносини збережених баз даних int значень до вихідних позицій елементів перерахунку. Це зайве обмеження, якщо текст зберігається. Плюс до того, я швидше зможу подивитися на db і побачити текст, а не в ints, де мені потім потрібно шукати текстові значення в іншому місці. В іншому випадку цей html-помічник дуже зручний у використанні.
Джованні

2
@Giovanni - ви можете вказати власні числові значення.
Томмі

1
@Giovanni Строгий дизайн повинен призначити значення для кожного запису перерахунків (якщо це важливо), інакше значення не має значення (і тому розміщення нових у кінці не повинно бути проблемою). Збереження значень int краще, якщо мова йде про збереження пам’яті та підвищення продуктивності (під час певного пошуку).
Король-король

130

Я зіткнувся з тією ж проблемою, знайшов це питання і подумав, що рішення, яке надає Еш, не те, що я шукав; Необхідно створити HTML сам означає меншу гнучкість порівняно з вбудованою Html.DropDownList()функцією.

Виявляється, C # 3 тощо робить це досить просто. У мене є enumдзвінок 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);

Це створює хороший ol ', SelectListякий можна використовувати так, як ви звикли в представленні:

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

Анонімний тип та LINQ роблять це набагато більш елегантним IMHO. Жодного злочину не передбачалося, Еш. :)


хороша відповідь! Я сподівався, що хтось буде використовувати linq та SelectList :) Радий, що я перевірив тут спочатку!
Pure.Krome

1
ID = s дайте мені DataTextField не значення? Що може бути причиною? Дякую
Барбарос Альп

1
Руна, я використовував цей самий метод, і DropDownList НЕ видає, коли він публікує на сервері, він не зберігає вибране вами значення.
за годинниковою стрілкоюq

5
@BarbarosAlp Щоб ID був числом, вам потрібно буде передати перерахунок до int:select new { ID = (int)s, Name = s.ToString() };
Кіт

Ця відповідь мені найбільше подобається через її простоту. Ганьба, що ви не отримали достатньо кредиту, оскільки обрана відповідь використовувала ваше рішення.
анар халілов

63

Ось краще інкапсульоване рішення:

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

Скажіть ось ваша модель:

введіть тут опис зображення

Використання зразка:

введіть тут опис зображення

Створений інтерфейс користувача: введіть тут опис зображення

І генерований HTML

введіть тут опис зображення

Знімок знімка джерела розширення Helper:

введіть тут опис зображення

Ви можете завантажити зразок проекту за посиланням, яке я надав.

EDIT: Ось код:

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
Просто моя думка, але я вважаю, що ця відповідь набагато чіткіше, ніж прийнята відповідь. Особливо мені подобається можливість використання атрибута Description. Я додав код, щоб люди могли його скопіювати / вставити без завантаження.
Бен Міллс

Викликайте метод розширення як EnumDropDownListДля замість DropDownListДля використання: -> @ Html.EnumDropDownListFor (x => x.Gender)
sandeep talabathula

Для когось, хто шукає додавання ще одного елемента "Виберіть, будь ласка" поверніть htmlHelper.DropDownListFor (вираз, createSelectList (express.ReturnType, selected, firstElement), "Please select");
Сандієп

1
Добре працює! Однак на сторінці "Деталі" DisplayFor () показує вибране значення перерахунку замість відповідного опису. Я думаю, це вимагає перевантаження для DisplayFor () для типу enum. Хтось має для цього рішення?
corix010

48

Html.DropDownListFor вимагає лише IEnumerable, тому альтернатива рішення Prise полягає в наступному. Це дозволить вам просто написати:

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

[Де SelectedItemType - поле для вашої моделі типу ItemTypes, а ваша модель не має значення]

Крім того, вам не потрібно генерувати метод розширення, оскільки ви можете використовувати enumValue.GetType (), а не typeof (T).

EDIT: Тут також інтегровано рішення Саймона та включено метод розширення 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();
    }
}

Не працює для мене ('System.NullReferenceException: Посилання на об'єкт не встановлено для екземпляра об'єкта.') ... Моя 'Модель' є нульовою ... напевно, має щось спільне з 'GetNonNullableModelType', який має Саймон включено
Учень

@ Cristi, ви праві, це рішення не призначене для використання в умовах, коли ваша модель недійсна. Я намагаюся взагалі уникати такого дизайну і ініціалізую модель «Порожня», коли це так.
Заїд Масуд

Ну, я новачок у asp mvc, але у мене є досить досвід роботи в .Net. Дякую, я перегляну те, що ви пропонували. Btw ваше розширення ToDescription далеко не за межами "Enum". Я здогадуюсь і для самого «Об’єкта». Це те, що я використав, коли взяв код Саймона і трохи більше його очистив.
Учень

@ Крісті важко зрозуміти, що ти маєш на увазі під "далеко за межами" Enum "сфери", але це здається, що ти кажеш, що метод розширення ToDescription не сильно набраний до перерахунку ItemTypes? Це навмисно і робить метод розширення корисним для всіх переліків. Якщо ви порівнюєте його із загальним методом розширення, у кожного підходу є плюси і мінуси. Зокрема, якщо ви узагальнюєте, ви не можете обмежуватися лише перерахунками.
Заїд Масуд

1
Чудово, з подякою. Я змінив value.ToString використовувати розширення FromCamelCase у випадку, якщо опису не було. Ось так я
котиться

33

Так що без функцій розширення, якщо ви шукаєте просте і легке .. Це те, що я зробив

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

де XXXXX.Sites.YYYY.Models.State є перерахунком

Напевно, краще виконати функцію помічника, але коли буде мало часу, це зроблять роботу.


Приємно, що це спрацювало, заповнюючи спадне меню, але як встановити вибране за замовчуванням значення синтаксису Razor для Html.DropDownListFor? Я хочу показати таблицю з комбінованими полями перерахунків, і мені потрібно встановити вибране значення відповідно до того, що було раніше.
Джонкл

2
Потрібно мати можливість передавати другий параметр із вибраним значенням новій функції SelectList (IEnumerable, object). Документація MSDN: msdn.microsoft.com/en-us/library/dd460123.aspx
Marty Trenouth

23

Розширюючи відповіді премії та руни, якщо ви хочете, щоб атрибут значення вибраного списку елементів списку відображався на ціле значення типу перерахування, а не значення рядка, використовуйте наступний код:

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

Замість того, щоб розцінювати кожне значення перерахування як об’єкт TEnum, ми можемо розглянути його як об'єкт, а потім передати його цілому числу, щоб отримати незмінене значення.

Примітка. Я також додав загальне обмеження типу, щоб обмежити типи, для яких це розширення доступне лише структурам (базовий тип Enum), і перевірку типу виконання під час виконання, яка гарантує, що передана структура справді є Enum.

Оновлення 23.10.12: Додано загальний параметр типу для базового типу та виправлену проблему некомпіляції, що стосується .NET 4+.


Дякую! Це була потрібна мені відповідь. Я зберігаю ціле значення Enum як стовпець у базі даних, і це рішення, здається, працює чудово.
grimus

але що робити, якщо ви зберігаєте char, а не int? яка моя справа. Очевидно, я міг би змінити (int) на (char), але як щодо того, щоб зробити це загальним. як це зробити?
Стефанівд

@Stefandvds Це велике запитання щодо кастингу для правильного представленого типу. На основі тестів, які я щойно виконував, здавалося б, єдиним способом ви могли б цього досягти, вказавши фактичний тип як інший параметр типу. ToSelectList<TEnum, TEnumValue>(this TEnum enumObj) { ... }
Натан Тейлор


Якщо значення вашого перерахунку є int, ви можете просто використовувати Value = Convert.ToInt32(e). (int)eне компілюється. :(
Ендрю

11

Щоб вирішити проблему отримання числа замість тексту, використовуйте метод розширення 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);
}

Це те, що я шукав, хоч це і не потворніше, ніж я вважав, що це потрібно. Цікаво , чому Visual Studio не дозволить вам безпосередньо кинути eв int.
Андрій

Або ви могли просто використовувати ID = Convert.ToInt32(e).
Андрій

11

Надзвичайно простий спосіб зробити це - без усіх розширень, які здаються зайвими, це:

Ваш перелік:

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

Всередині контролера прив’яжіть Enum до списку:

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

Після цього киньте його у ViewBag:

    ViewBag.RequiredLevel = new SelectList(myLevels);

Нарешті просто прив’яжіть його до виду:

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

Це, безумовно, найпростіший спосіб, який я знайшов, і не вимагає жодних розширень або нічого такого, що божевільний.

ОНОВЛЕННЯ : Дивіться коментар Ендрюса нижче.


3
Це працює лише в тому випадку, якщо ви не присвоїли жодному значенню своєму перерахунку. Якби це було Level1 = 1, то значення, що випадає, було б "Level1"замість 1.
Андрій

11

Найкращим рішенням, яке я знайшов для цього, було поєднання цього блогу з відповіддю Саймона Голдстоуна .

Це дозволяє використовувати перерахунок у моделі. По суті, ідея полягає у використанні цілого властивості, а також enum та емуляції цілої властивості.

Потім використовуйте атрибут [System.ComponentModel.Description] для анотування моделі з відображуваним текстом та використовуйте розширення "EnumDropDownListFor" у вашому поданні.

Це робить і вигляд, і модель дуже читабельною та легкодоступною.

Модель:

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

Вид:

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

Розширення (безпосередньо з відповіді Саймона Голдстоуна , включена тут для повноти):

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

Це не працює, MVC 4 Бритва. У вікні перегляду або в режимі виконання, помилка = "Виклик неоднозначний між такими методами чи властивостями" LDN.Extensions.MvcExtensions.EnumDropDownListFor <MyModel, LDN.Models.YesPartialNoEnum?> (System.Web.Mvc.HtmlHelper <MyModMod .Linq.Expressions.Expression <System.Func <MyModel, LDN.Models.YesPartialNoEnum? >>) 'і .... ", і той самий той самий метод повторюється повторно (тут не дозволено достатньо символів).
Марк


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

Добре! Як таким чином отримати значення та текст із enum? Я маю на увазі, що у мене є SomeEnum {some1 = 1, some2 = 2} Мені потрібно отримати значення (1, 2) за значенням і text (some1, some2) для тексту вибраного списку
Дмитреський

7

Це відповіді Rune & Prize, змінені для використання intзначення Enum як ідентифікатора.

Зразок Enum:

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

Спосіб розширення:

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

Зразок використання:

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

Не забудьте імпортувати простір імен, що містить метод розширення

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

Зразок створеного HTML:

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

Зауважте, що елемент, який ви використовуєте для дзвінка, ToSelectListє обраним.


Або ви могли просто використовувати Id = Convert.ToInt32(e).
Андрій

6

Це версія для 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)

Це буде працювати лише в тому випадку, якщо ваш перерахунок складається з суміжних значень, починаючи з 0. А перелік прапорів не буде працювати з цим. Творче використання індексованого Select, хоча.
Suncat2000

6

У .NET Core ви можете просто скористатися цим:

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

1
Або з помічником тегів <select asp-for="Model.Foo" class="form-control" asp-items="@Html.GetEnumSelectList<MyEnum>()"></select>.
Паскаль Р.

так, я кажу, що помічники тегів ще кращі, оскільки формат наближений до чистого HTML;)
GoldenAge

Також ви можете це зробити @ Html.DropDownListFor (x => x.Foo, Html.GetEnumSelectList (typeof (FooEnum)))
Fereydoon Barikzehy

6

Тепер ця функція підтримується поза вікном у MVC 5.1 до @Html.EnumDropDownListFor()

Перевірте наступне посилання:

https://docs.microsoft.com/en-us/aspnet/mvc/overview/releases/mvc51-release-notes#Enum

Дуже прикро, що для впровадження такої функції, яка так затребувана відповідно до голосування вище, знадобилося Microsoft 5 років!


5

Спираючись на відповідь Саймона, подібний підхід полягає в тому, щоб значення Enum відображалися з файла ресурсу, а не з атрибута опису в самому Enum. Це корисно, якщо ваш сайт потрібно рендерувати більш ніж однією мовою, і якщо ви мали б мати певний файл ресурсів для Enums, ви можете піти ще на крок і мати лише значення Enum у вашому Enum та посилатись на них із розширення на такий звіт, як [EnumName] _ [EnumValue] - в кінцевому рахунку менше друкувати!

Потім розширення виглядає так:

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

Ресурси у файлі Enums.Resx виглядають як ItemTypes_Movie: Film

Ще одне, що мені подобається робити, це замість того, щоб викликати метод розширення безпосередньо, я б швидше називав його за допомогою @ Html.EditorFor (x => x.MyProperty), або в ідеалі просто мати всю форму, в один акуратний @ Html.EditorForModel (). Для цього я змінюю шаблон рядка, щоб виглядати так

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

Якщо це вас цікавить, я розмістив набагато докладнішу відповідь тут у своєму блозі:

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


5

Ну, я дуже спізнююся на вечірку, але для того, що воно того варте, я обговорив цю тему, завдяки чому я створюю EnumHelperклас, який дозволяє дуже легко перетворитись.

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

У вашому контролері:

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

У вашому перегляді:

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

Клас помічників:

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

Я дуже запізнююсь на цьому, але я просто знайшов дуже класний спосіб зробити це за допомогою одного рядка коду, якщо ви раді додати пакет Unconstrained Melody NuGet (хороша маленька бібліотека від Jon Skeet).

Це рішення краще, оскільки:

  1. Це гарантує (із загальними обмеженнями типу), що значення дійсно є перерахунковим значенням (завдяки необмеженій мелодії)
  2. Це дозволяє уникнути зайвого боксу (через необмежену мелодію)
  3. Він зберігає всі описи, щоб уникнути використання роздумів про кожен дзвінок (через необмежену мелодію)
  4. Це менше коду, ніж інші рішення!

Отже, ось такі кроки, щоб зробити цю роботу:

  1. У консолі диспетчера пакунків "Встановити-пакувати необмежену мелодію"
  2. Додайте властивість у свою модель так:

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

Тепер, коли у вашій моделі представлений Список SelectListItem, ви можете використовувати @ Html.DropDownList або @ Html.DropDownListДля використання цього властивості як джерела.


+1 за використання коду Джона Скіта :), але просто жартую хорошого
Вамсі

3

Ще одне виправлення цього методу розширення - поточна версія не вибрала поточне значення перерахунку. Я зафіксував останній рядок:

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

Якщо ви хочете додати підтримку локалізації, просто змініть метод s.toString () на щось подібне:

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

Тут typeof (Resources) - це ресурс, який ви хочете завантажити, і тоді ви отримаєте локалізовану String, також корисну, якщо ваш перерахувач має значення з кількома словами.


3

Це моя версія допоміжного методу. Я використовую це:

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

Замість цього:

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

Ось:

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

Ви також можете використовувати мої користувацькі HtmlHelpers у Griffin.MvcContrib. Наступний код:

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

Створює:

введіть тут опис зображення

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


3

Я хотів би відповісти на це питання по-іншому, де користувачеві не потрібно нічого робити в controllerабо Linqвисловлювати. Сюди...

Я маю 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*/
    }

Тепер я просто не можу створити це dropdownза допомогою цього enum.

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

АБО

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

Якщо ви хочете зробити індекс обраним, спробуйте це

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

Тут я використовував AccessLevelEnum.NoAccessяк додатковий параметр для вибору за замовчуванням випадаючого меню.


3

Я знайшов відповідь тут . Однак деякі з моїх перерахунків мають [Description(...)]атрибут, тому я змінив код, щоб забезпечити підтримку для цього:

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

Сподіваюся, що це допомагає.


Я хочу повернути члена типу = DropdownList. Я добре з Text = DescriptionAttribute, але важко отримати значення int зі значення
NanaFadanvis

2

@Simon Goldstone: Дякую за ваше рішення, його можна ідеально застосувати в моєму випадку. Єдина проблема - мені довелося перекласти це на VB. Але зараз це зроблено, і щоб заощадити час інших людей (у випадку, якщо вони знадобляться), я ставлю його сюди:

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

Кінець Ви використовуєте це так:

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

2

Я в кінцевому підсумку створив методи розширення, щоб зробити те, що по суті є прийнятою відповіддю. Остання половина «Гісті» стосується конкретно Енума.

https://gist.github.com/3813767


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

})

Я думаю, що це неправдива відповідь, це зовсім не використання перерахунку для заповнення спадного меню.
Андрій
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.