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

563

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

Скажем, у меня есть перечисление вроде этого:

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

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

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

Теги:
asp.net-mvc

35 ответов

666
Лучший ответ

Для 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 и ниже

Я перевернул ответ Rune в метод расширения:

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

  • 12
    Я не мог заставить это работать, не могли бы вы помочь. Когда я делаю Post.PostType.ToSelectList (); это не признает расширение?
  • 3
    Я не мог заставить это работать тоже. Является ли Status вашим свойством Enum для класса задач? Разве это не одно из перечисленных значений?
Показать ещё 16 комментариев
322

Я знаю, что я опаздываю на вечеринку по этому поводу, но подумал, что вы можете найти этот вариант полезным, так как он также позволяет вам использовать дескриптивные строки, а не константы перечисления в раскрывающемся списке. Чтобы сделать это, украсьте каждую запись перечисления атрибутом [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. К сожалению, он, похоже, не уважает атрибут [Description], поэтому код выше все еще стоит. (См. http://www.asp.net/mvc/overview/releases/mvc51-release-notes#Enum для заметок о выпуске Microsoft.)

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

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

  • 26
    +1 Я нашел этот самый полезный из всех ответов здесь. Я смог превратить это в многократно используемый код. Спасибо!
  • 40
    В Visual Studio есть странная ошибка, из-за которой, если вы не ссылаетесь на System.Web.Mvc.Html он говорит, что DropDownListFor не может быть найден, но также не может ее устранить. Вы должны сделать это вручную, using System.Web.Mvc.Html; , Просто так ты знаешь.
Показать ещё 11 комментариев
137

В 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>()">
  • 18
    Это нужно как-то поднять на первое место
  • 3
    Вы должны создать новый вопрос, специфичный для MVC 5.1, и указать его в качестве ответа, а затем отправить мне ссылку на сообщение, чтобы я мог добавить в избранное.
Показать ещё 5 комментариев
115

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

Выключает С# 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. Никакого преступления не было, Эш.:)

  • 0
    хороший ответ! Я надеялся, что кто-то будет использовать linq и SelectList :) Рад, что я проверил здесь первым!
  • 1
    ID = s дают мне DataTextField, а не значение? В чем может быть причина? Спасибо
Показать ещё 4 комментария
50

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

http://www.spicelogic.com/Journal/ASP-NET-MVC-DropDownListFor-Html-Helper-Enum-5

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

Изображение 5898

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

Изображение 5899

Сгенерированный пользовательский интерфейс: Изображение 5900

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

Изображение 5901

Съемка исходного кода расширенного расширения помощника:

Изображение 5902

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

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();
    }
}
  • 10
    зачем публиковать скриншот? опубликовать код!
  • 2
    Просто мое мнение, но я думаю, что этот ответ намного чище, чем принятый ответ. Мне особенно нравится возможность использования атрибута Description. Я добавил код, чтобы люди могли копировать / вставлять его без загрузки.
Показать ещё 3 комментария
48

Html.DropDownList Для использования только IEnumerable, поэтому альтернатива решению Prize выглядит следующим образом. Это позволит вам просто написать:

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

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

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

EDIT: Интегрированное решение Simon здесь, а также включенный метод расширения 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();
    }
}
  • 0
    Не работает для меня ('System.NullReferenceException: ссылка на объект не установлена для экземпляра объекта.') ... Моя 'Модель' равна нулю ... вероятно, имеет отношение к 'GetNonNullableModelType', который есть у Саймона включены
  • 0
    @ Кристи, вы правы, это решение не предназначено для использования в условиях, когда ваша Модель имеет значение null. Я стараюсь избегать такого дизайна в целом и инициализирую для «пустой» модели, когда это так.
Показать ещё 4 комментария
29

Итак, без функций расширения, если вы ищете простые и легкие. Это то, что я сделал

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

где XXXXX.Sites.YYYY.Models.State - это перечисление

Вероятно, лучше выполнять вспомогательную функцию, но когда время коротко, это выполнит работу.

  • 0
    Хорошо, это работало, заполняя выпадающий список, но как установить выбранное по умолчанию значение в синтаксисе Razor для Html.DropDownListFor? Я хочу показать таблицу со списками перечислений, и мне нужно также установить выбранное значение в соответствии с тем, что было раньше.
  • 2
    Должен быть в состоянии передать второй параметр с выбранным значением в новую функцию SelectList (IEnumerable, object). Документация MSDN: msdn.microsoft.com/en-us/library/dd460123.aspx
Показать ещё 1 комментарий
22

Развернув ответы на 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, мы можем рассматривать его как объект, а затем приводить его к целому числу, чтобы получить значение unboxed.

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

Обновление 10/23/12: Добавлен общий тип типа для базового типа и проблема с фиксированной некомпиляцией, влияющая на .NET 4 +.

  • 0
    Спасибо! Это был ответ, который мне был нужен. Я храню целочисленное значение Enum в виде столбца в базе данных, и это решение, похоже, работает отлично.
  • 0
    но что, если вы храните char, а не int? это мой случай. очевидно, я мог бы изменить (int) на (char), но как насчет того, чтобы сделать этот универсальный тоже. как это сделать?
Показать ещё 4 комментария
9

Чтобы решить проблему получения номера вместо текста с помощью метода расширения Prize.

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);
}
  • 0
    Это то, что я искал, хотя это и ужаснее, чем я думал. Интересно, почему Visual Studio не позволяет напрямую приводить e к int .
  • 0
    Или вы можете просто использовать ID = Convert.ToInt32(e) .
8

Лучшее решение, которое я нашел для этого, состояло в объединении этого блога с ответом Саймон Голдстоун.

Это позволяет использовать перечисление в модели. По сути, идея состоит в том, чтобы использовать свойство integer, а также перечисление, и эмулировать свойство integer.

Затем используйте атрибут [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);
        }
    }
}
  • 0
    Это не работает, 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? >>) 'и .... "и тот же самый метод с теми же опорами повторяется снова (здесь не допускается достаточное количество символов).
7

Вы хотите посмотреть на что-то вроде Enum.GetValues

6

Супер простой способ сделать это - без всяких расширений, которые кажутся излишними:

Ваше перечисление:

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

Это самый простой способ найти и не требует каких-либо расширений или чего-то, что сходит с ума.

ОБНОВЛЕНИЕ. См. комментарий Andrews ниже.

  • 2
    Это работает только в том случае, если вы не присвоили никакое значение своему перечислению. Если бы у вас был Level1 = 1 , то значение выпадающего списка было бы "Level1" вместо 1 .
5
@Html.DropDownListFor(model => model.Type, Enum.GetNames(typeof(Rewards.Models.PropertyType)).Select(e => new SelectListItem { Text = e }))
  • 0
    Хорошо! Как получить значение и текст из enum таким образом? Я имею в виду, у меня есть SomeEnum {some1 = 1, some2 = 2}, мне нужно получить числа (1, 2) для значения и текст (some1, some2) для текста списка выбора
5

Это ответы Rune и Prize изменены, чтобы использовать значение Enum int в качестве идентификатора.

Пример перечисления:

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 on, - это выбранный элемент.

  • 0
    Или вы можете просто использовать Id = Convert.ToInt32(e) .
4

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

Теперь эта функция поддерживается из-за-в-коробке в MVC 5.1 через @Html.EnumDropDownListFor()

Проверьте следующую ссылку:

http://www.asp.net/mvc/overview/releases/mvc51-release-notes#Enum

Поистине стыдно, что Microsoft потребовала от Microsoft 5 лет реализовать такую ​​функцию, которая так востребована в соответствии с голосованием выше!

3

На основе ответа Саймона аналогичный подход заключается в том, чтобы получить значения 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: Фильм

Еще одна вещь, которую мне нравится делать, - вместо того, чтобы напрямую обращаться к методу расширения, я бы скорее назвал ее с помощью @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/

3

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

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

  • Он гарантирует (с ограничениями общего типа), что значение действительно является значением перечисления (из-за Unconstrained Melody).
  • Это позволяет избежать ненужного бокса (из-за Unconstrained Melody).
  • Он кэширует все описания, чтобы избежать использования отражения при каждом вызове (из-за Unconstrained Melody)
  • Это меньше кода, чем другие решения!

Итак, вот шаги для этого:

  • В консоли диспетчера пакетов "Install-Package UnconstrainedMelody"
  • Добавьте свойство на вашей модели следующим образом:

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

Теперь, когда у вас есть List of SelectListItem, выставленный на вашей модели, вы можете использовать @Html.DropDownList или @Html.DropDownListДля использования этого свойства в качестве источника.

  • 0
    +1 за использование кода Джона Скита :), шучу, хотя хороший
2

Ну, я очень опаздываю на вечеринку, но для чего это стоит, я написал блог об этом самом предмете, в котором я создаю класс 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() });
    }
}
2

Вы также можете использовать мои пользовательские HtmlHelpers в Griffin.MvcContrib. Следующий код:

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

Формирует:

Изображение 5903

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

2

Это моя версия вспомогательного метода. Я использую это:

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

Если вы хотите добавить поддержку локализации, просто измените метод 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, также полезную, если ваш счетчик имеет значения с несколькими словами.

2

Другое исправление этого метода расширения - текущая версия не выбрала текущее значение перечисления. Я исправил последнюю строку:

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

Я нашел ответ здесь http://blogs.msdn.com/b/stuartleeks/archive/2010/05/21/asp-net-mvc-creating-a-dropdownlist-helper-for-enums.aspx; однако некоторые из моих перечислений имеют атрибут [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
            );
    }

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

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

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

https://gist.github.com/3813767

1

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

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

0
        ////  ViewModel

        public class RegisterViewModel
          {

        public RegisterViewModel()
          {
              ActionsList = new List<SelectListItem>();
          }

        public IEnumerable<SelectListItem> ActionsList { get; set; }

        public string StudentGrade { get; set; }

           }

       //// Enum Class

        public enum GradeTypes
             {
               A,
               B,
               C,
               D,
               E,
               F,
               G,
               H
            }

         ////Controller action 

           public ActionResult Student()
               {
    RegisterViewModel vm = new RegisterViewModel();
    IEnumerable<GradeTypes> actionTypes = Enum.GetValues(typeof(GradeTypes))
                                         .Cast<GradeTypes>();                  
    vm.ActionsList = from action in actionTypes
                     select new SelectListItem
                     {
                         Text = action.ToString(),
                         Value = action.ToString()
                     };
              return View(vm);
               }

         ////// View Action

   <div class="form-group">
                            <label class="col-lg-2 control-label" for="hobies">Student Grade:</label>
                            <div class="col-lg-10">
                               @Html.DropDownListFor(model => model.StudentGrade, Model.ActionsList, new { @class = "form-control" })
                            </div>
0

В MVC4 я бы сделал это

@Html.DropDownList("RefType", new SelectList(Enum.GetValues(typeof(WebAPIApp.Models.RefType))), " Select", new { @class = "form-control" })

public enum RefType
    {
        Web = 3,
        API = 4,
        Security = 5,
        FE = 6
    }

    public class Reference
    {
        public int Id { get; set; }
        public RefType RefType { get; set; }
    }
0

Я сделал следующее и успешно работает:

  • В view.cshtml:

@model MyModel.cs

@Html.EnumDropDownListFor(m=>m.MyItemType )
  • В модели: MyModel.cs

public ItemTypes MyItemType { get; set; }

  • 0
    Вы просто продублировали ответ, данный @Ofiris, и это работает только для ASP.NET MVC 5.1 и выше.
0

Здесь вариация Martin Faartoft, где вы можете поместить пользовательские ярлыки, которые хороши для локализации.

public static class EnumHtmlHelper
{
    public static SelectList ToSelectList<TEnum>(this TEnum enumObj, Dictionary<int, string> customLabels)
        where TEnum : struct, IComparable, IFormattable, IConvertible
    {
        var values = from TEnum e in Enum.GetValues(typeof(TEnum))
                     select new { Id = e, Name = customLabels.First(x => x.Key == Convert.ToInt32(e)).Value.ToString() };

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

Использовать в поле зрения:

@Html.DropDownListFor(m => m.Category, Model.Category.ToSelectList(new Dictionary<int, string>() { 
          { 1, ContactResStrings.FeedbackCategory }, 
          { 2, ContactResStrings.ComplainCategory }, 
          { 3, ContactResStrings.CommentCategory },
          { 4, ContactResStrings.OtherCategory }
      }), new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Category)
  • 0
    Вы перечисляете каждый элемент enum по вашему мнению? Что произойдет, если вы добавите новый элемент в ваше перечисление и если у вас будет много перечислений для многих выпадающих списков?
  • 0
    Это, безусловно, поможет вам ....
0

1- Создайте свой ENUM

public enum LicenseType
{
    xxx = 1,
    yyy = 2
}

2- Создайте свой класс обслуживания

public class LicenseTypeEnumService
    {

        public static Dictionary<int, string> GetAll()
        {

            var licenseTypes = new Dictionary<int, string>();

            licenseTypes.Add((int)LicenseType.xxx, "xxx");
            licenseTypes.Add((int)LicenseType.yyy, "yyy");

            return licenseTypes;

        }

        public static string GetById(int id)
        {

            var q = (from p in this.GetAll() where p.Key == id select p).Single();
            return q.Value;

        }

    }

3- Установите ViewBag в контроллере

var licenseTypes = LicenseTypeEnumService.GetAll();
ViewBag.LicenseTypes = new SelectList(licenseTypes, "Key", "Value");

4- Свяжите свой DropDownList

@Html.DropDownList("LicenseType", (SelectList)ViewBag.LicenseTypes)
  • 1
    Вы вручную добавляете элементы перечисления ... Если ваше перечисление меняется, вам придется дважды изменить код. А что если у вас много перечислений для многих выпадающих списков?
0
@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" }

})
  • 3
    Где перечисление?
  • 0
    Я думаю, что это неправильный ответ, он вообще не использует enum для заполнения выпадающего списка.
-5

ОБНОВЛЕНО. Я предлагаю использовать предложение Rune ниже, а не эту опцию.


Я предполагаю, что вы хотите что-то вроде следующего выплюнуть:

<select name="blah">
    <option value="1">Movie</option>
    <option value="2">Game</option>
    <option value="3">Book</option>
</select>

который вы можете сделать с помощью метода расширения, например:

public static string DropdownEnum(this System.Web.Mvc.HtmlHelper helper,
                                  Enum values)
{
    System.Text.StringBuilder sb = new System.Text.StringBuilder();
    sb.Append("<select name=\"blah\">");
    string[] names = Enum.GetNames(values.GetType());
    foreach(string name in names)
    {
        sb.Append("<option value=\"");
        sb.Append(((int)Enum.Parse(values.GetType(), name)).ToString());
        sb.Append("\">");
        sb.Append(name);
        sb.Append("</option>");
    }
    sb.Append("</select>");
    return sb.ToString();
}

НО такие вещи не являются локализуемыми (т.е. трудно перевести на другой язык).

Примечание: вам нужно вызвать статический метод с экземпляром Enumeration, т.е. Html.DropdownEnum(ItemTypes.Movie);

Там может быть более элегантный способ сделать это, но выше это работает.

  • 0
    Благодарю. Это на самом деле реализация, с которой я закончил, но я надеялся, что она уже встроена в фреймворк. Я думаю, нет. :-(
  • 12
    Вот это да. это плохо
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню