Как создать выпадающий список из перечисления в ASP.NET MVC?

671

Я пытаюсь использовать Html.DropDownListметод расширения, но не могу понять, как использовать его с перечислением.

Допустим, у меня есть перечисление как это:

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

Как мне создать выпадающий список с этими значениями, используя Html.DropDownListметод расширения?

Или лучше всего просто создать цикл for и создать элементы Html вручную?

Kevin Pang
источник

Ответы:

842

Для 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

Martin Faartoft
источник
13
Я не мог заставить это работать, не могли бы вы помочь. Когда я делаю Post.PostType.ToSelectList (); это не признает расширение?
Barbaros Alp
3
Я не мог заставить это работать тоже. Статус - это ваше свойство Enum для класса задач? Разве это не одно из перечисленных значений?
Дэрил
9
Вы можете немного ограничить это с помощью: где T: struct, IConvertible См .: stackoverflow.com/questions/79126/…
Ричард Гарсайд
8
Это здорово. Если кто-то борется с реализацией, вот как я это сделал. Добавлен класс EnumHelpers в папку HtmlHelpers. Использовал приведенный выше код. Добавлено пространство имен в соответствии с рекомендацией @TodK: <add namespace = "xxx.HtmlHelpers" />. Затем я использовал его на странице бритвы, например: @ Html.DropDownListFor (model => model.Status, @ Model.Status.ToSelectList ()) HTH
Джефф Борден,
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)

Надеюсь, это поможет вам!

** РЕДАКТИРОВАТЬ 2014-ЯНВ-23: Microsoft только что выпустила MVC 5.1, которая теперь имеет функцию EnumDropDownListFor. К сожалению, он не соответствует атрибуту [Description], поэтому приведенный выше код остается в силе. См. Раздел Enum в примечаниях к выпуску Microsoft для MVC 5.1.

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

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

SimonGoldstone
источник
28
+1 Я нашел этот самый полезный из всех ответов здесь. Я был в состоянии превратить это в очень многократно используемый кусок кода. Спасибо!
Эд Чарбено
43
В Visual Studio есть странная ошибка, из-за которой, если вы не ссылаетесь на System.Web.Mvc.Htmlнее, она говорит, что DropDownListForне может быть найдена, но и не может ее устранить. Вы должны сделать это вручную using System.Web.Mvc.Html;. Просто так ты знаешь.
Кеззер
1
У меня есть вариант этого в сущности, который мы используем во всех наших проектах: gist.github.com/1287511
kamranicus
1
Отличное решение, спасибо, было бы еще лучше, если бы вы могли кэшировать результаты GetEnumDescription
М.
17
Новый MVC 5.1 EnumDropDownListFor не использует [Description ("")], но использует [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)

Использование Tag Helper (ASP.NET MVC 6) :

<select asp-for="@Model.SelectedValue" asp-items="Html.GetEnumSelectList<MyEnum>()">
Ofiris
источник
21
Это нужно как-то поднять на первое место
3
Вы должны создать новый вопрос, специфичный для MVC 5.1, и указать его в качестве ответа, а затем отправить мне ссылку на сообщение, чтобы я мог добавить в избранное.
Кевин Хайдт
2
Что мне не нравится в EnumDropDownListFor (), так это то, что он сохраняет в БД значение int перечисления, а не текст, поэтому, если вы когда-нибудь решите добавить новый элемент перечисления, он обязательно должен идти в конце списка , чтобы не потерять связь сохраненных значений базы данных с исходными позициями элементов перечисления. Это ненужное ограничение, если текст сохраняется. Кроме того, я предпочитаю смотреть на БД и видеть текст, а не целые, где я должен искать текстовые значения в другом месте. В противном случае этот помощник HTML очень удобно использовать.
Джованни
2
@ Джованни - вы можете указать свои собственные числовые значения.
Томми
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);

Это создает хороший старый код, SelectListкоторый можно использовать так же, как вы привыкли в представлении:

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

Анонимный тип и LINQ делают это намного более элегантным ИМХО. Не обижайся, Эш. :)

Rune Jacobsen
источник
хороший ответ! Я надеялся, что кто-то будет использовать linq и SelectList :) Рад, что я проверил здесь первым!
Pure.Krome
1
ID = s дают мне DataTextField, а не значение? В чем может быть причина? Спасибо
Barbaros Alp
1
Руна, я использовал этот же метод, и DropDownList ОТСУТСТВУЕТ при рендеринге на сервер, но не сохраняет выбранное мной значение.
часовой стрелке
5
@BarbarosAlp Чтобы ID был числом, вам нужно привести перечисление к целому числу:select new { ID = (int)s, Name = s.ToString() };
Кит
Это ответ, который мне нравится больше всего из-за его простоты. Жаль, что вы не получили достаточно кредитов, так как выбранный ответ использовал ваше решение.
Анар Халилов
63

Вот лучшее инкапсулированное решение:

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

Скажи, вот твоя модель:

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

Пример использования:

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

Сгенерированный интерфейс: введите описание изображения здесь

И сгенерированный HTML

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

Снимок исходного кода Helper Extension:

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

Вы можете скачать образец проекта по ссылке, которую я предоставил.

РЕДАКТИРОВАТЬ: Вот код:

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
источник
2
Просто мое мнение, но я думаю, что этот ответ намного чище, чем принятый ответ. Мне особенно нравится возможность использования атрибута Description. Я добавил код, чтобы люди могли копировать / вставлять его без загрузки.
Бен Миллс
Вызовите метод расширения как EnumDropDownListFor, а не DropDownListFor. Использование: -> @ Html.EnumDropDownListFor (x => x.Gender)
sandeep talabathula
Для тех, кто ищет Добавление еще одного элемента «Пожалуйста, выберите» return htmlHelper.DropDownListFor (expression, createSelectList (expression.ReturnType, selected, firstElement), «Please Select»);
Сандип
1
Работает отлично! Однако на странице Details DisplayFor () показывает выбранное значение перечисления вместо соответствующего описания. Я предполагаю, что это вызывает перегрузку для DisplayFor () для типа enum. У кого-нибудь есть решение для этого?
corix010
48

Html.DropDownListFor требует только IEnumerable, поэтому альтернатива решению Prise заключается в следующем. Это позволит вам просто написать:

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

[Где SelectedItemType - это поле в вашей модели типа ItemTypes, а ваша модель не равна нулю]

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

РЕДАКТИРОВАТЬ: интегрированное решение Саймона здесь, а также включил метод расширения 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: ссылка на объект не установлена ​​на экземпляр объекта.') ... Моя 'Model' равна нулю ... вероятно, имеет отношение к 'GetNonNullableModelType', который есть у Саймона в комплекте
ученик
@ Кристи, вы правы, это решение не предназначено для использования в условиях, когда ваша Модель имеет значение null. Я стараюсь избегать такого дизайна в целом и инициализирую для «пустой» модели, когда это так.
Заид Масуд
Ну, я новичок в asp mvc, но у меня есть достаточно опыта в .Net. Спасибо, я посмотрю, что вы предлагаете. Кстати, ваше расширение ToDescription находится далеко за пределами области действия Enum. Я думаю, хорошо подходит для самого объекта. Это то, что я использовал, когда взял код Саймона и немного его очистил.
Учащийся
@ Кристи, трудно понять, что вы подразумеваете под «далеко за пределами области действия Enum», но похоже, что вы говорите, что метод расширения ToDescription не является строго типизированным для перечисления ItemTypes? Это сделано намеренно и делает метод расширения универсальным для всех перечислений. Если вы сравниваете его с общим методом расширения, у каждого подхода есть свои плюсы и минусы. В частности, если вы генерируете rerecize, вы не можете ограничить только перечисления.
Заид Масуд
1
Отлично, с благодарностью. Я изменил value.ToString, чтобы использовать расширение FromCamelCase, если не было описания. Вот так я катаюсь :)
Valamas
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? Я хочу показать таблицу со списками перечислений, и мне нужно также установить выбранное значение в соответствии с тем, что было раньше.
Johncl
2
Должен быть в состоянии передать второй параметр с выбранным значением в новую функцию SelectList (IEnumerable, object). Документация MSDN: msdn.microsoft.com/en-us/library/dd460123.aspx
Марти Тренут,
23

Расширяя ответы Prize и Rune, если вы хотите, чтобы атрибут value ваших элементов списка выбора соответствовал целочисленному значению типа Enumeration, а не строковому значению, используйте следующий код:

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

Вместо того, чтобы рассматривать каждое значение Enumeration как объект TEnum, мы можем рассматривать его как объект и затем приводить его к целому числу, чтобы получить распакованное значение.

Замечания: я также добавил ограничение общего типа, чтобы ограничить типы, для которых это расширение доступно только для структур (базовый тип Enum), и проверку типа во время выполнения, которая гарантирует, что переданная структура действительно является Enum.

Обновление 23.10.12: добавлен параметр общего типа для базового типа и исправлена ​​проблема некомпиляции, затрагивающая .NET 4+.

Натан Тейлор
источник
Спасибо! Это был ответ, который мне был нужен. Я храню целочисленное значение Enum в виде столбца в базе данных, и это решение, похоже, работает отлично.
Grimus
но что если вы храните char, а не int? это мой случай. очевидно, я мог бы изменить (int) на (char), но как насчет того, чтобы сделать этот универсальный тоже. как это сделать?
Stefanvds
@Stefandvds Это отличный вопрос относительно приведения к правильному представленному типу. Исходя из только что выполненных тестов, кажется, единственный способ достичь этого - это указать фактический тип в качестве другого параметра типа. ToSelectList<TEnum, TEnumValue>(this TEnum enumObj) { ... }
Натан Тейлор
@Stefandvds Смотрите этот вопрос .
Натан Тейлор
Если значения вашего перечисления 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 Razor. В представлении или во время выполнения error = "Вызов неоднозначен между следующими методами или свойствами 'LDN.Extensions.MvcExtensions.EnumDropDownListFor <MyModel, LDN.Models.YesPartialNoEnum?> (System.Web.Mvc.HtmlHelper <MyModel>, система .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) для значения и текст (some1, some2) для текста списка выбора
Dmitresky
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()) %>

Не забудьте импортировать пространство имен, содержащее метод Extension

<%@ 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)
user550950
источник
Это будет работать, только если ваше перечисление состоит из смежных значений, начинающихся с 0. Перечисление Flags не будет работать с этим. Творческое использование индексированного выбора, хотя.
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 лет, чтобы реализовать такую ​​функцию, которая так востребована в соответствии с голосованием выше!

Lafi
источник
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/

PaulTheCyclist
источник
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() });
    }
}
NinjaNye
источник
4

Я опаздываю на этот раз, но я нашел действительно классный способ сделать это с помощью одной строки кода, если вы счастливы добавить пакет Unconstrained Melody NuGet (симпатичная небольшая библиотека от Jon Skeet).

Это решение лучше, потому что:

  1. Это гарантирует (с ограничениями общего типа), что значение действительно является значением перечисления (из-за Неограниченной Мелодии)
  2. Избегает ненужного бокса (из-за неограниченной мелодии)
  3. Он кэширует все описания, чтобы избежать использования отражения при каждом вызове (из-за неограниченной мелодии).
  4. Это меньше кода, чем другие решения!

Итак, вот шаги, чтобы заставить это работать:

  1. В консоли диспетчера пакетов "Install-Package UnconstrainedMelody"
  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.DropDownListFor, используя это свойство в качестве источника.

ноотн
источник
+1 за использование кода Джона Скита :), просто шучу хороший, хотя
Vamsi
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());
    }
justabuzz
источник
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) - это ресурс, который вы хотите загрузить, и затем вы получите локализованную строку, также полезную, если ваш перечислитель имеет значения с несколькими словами.

brafales
источник
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

jgauffin
источник
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дополнительный параметр для выбора по умолчанию.

gdmanandamohon
источник
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
            );
    }

Надеюсь, это поможет.

Alkasai
источник
Я хочу вернуть член типа = DropdownList. У меня все хорошо с
атрибутом
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

Я закончил тем, что создал методы расширения, чтобы сделать то, что по сути является ответом здесь. Последняя половина Gist посвящена Enum конкретно.

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 ----"}
}
)
Shahnawaz
источник
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
источник
Я думаю, что это неправильный ответ, он вообще не использует enum для заполнения выпадающего списка.
Андрей