Как создать выпадающий список из перечисления в ASP.NET MVC? - PullRequest
632 голосов
/ 23 декабря 2008

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

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

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

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

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

Ответы [ 36 ]

796 голосов
/ 29 марта 2009

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

349 голосов
/ 10 марта 2011

Я знаю, что опоздал на вечеринку по этому вопросу, но подумал, что этот вариант может оказаться полезным, так как этот также позволяет вам использовать описательные строки, а не константы перечисления в раскрывающемся списке. Для этого украсьте каждую запись перечисления атрибутом [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], поэтому приведенный выше код остается в силе. См. Раздел Enum в Замечания к выпуску Microsoft для MVC 5.1.

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

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

185 голосов
/ 10 марта 2014

В ASP.NET MVC 5.1 они добавили помощника EnumDropDownListFor(), поэтому нет необходимости в пользовательских расширениях:

Модель

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

View

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

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

<select asp-for="@Model.SelectedValue" asp-items="Html.GetEnumSelectList<MyEnum>()">
126 голосов
/ 11 января 2009

Я столкнулся с той же проблемой, нашел этот вопрос и подумал, что решение, предложенное Эшем, было не тем, что я искал; Самостоятельное создание 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 делают это намного более элегантным ИМХО. Не обижайся, Эш. :)

59 голосов
/ 18 апреля 2013

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

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

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

enter image description here

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

enter image description here

Созданный пользовательский интерфейс: enter image description here

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

enter image description here

Снимок исходного кода вспомогательного расширения:

enter image description here

Вы можете скачать пример проекта по предоставленной мной ссылке.

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

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();
    }
}
47 голосов
/ 03 ноября 2010

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();
    }
}
32 голосов
/ 26 октября 2010

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

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

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

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

22 голосов
/ 12 марта 2010

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

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 +.

10 голосов
/ 04 декабря 2011

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

Это позволяет использовать 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);
        }
    }
}
10 голосов
/ 03 июня 2010

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

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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...