Я пытаюсь использовать метод расширения Html.DropDownList, но не могу понять, как использовать его с перечислением.
Скажем, у меня есть такое перечисление:
public enum ItemTypes
{
Movie = 1,
Game = 2,
Book = 3
}
Как мне создать раскрывающийся список с этими значениями с помощью метода расширения Html.DropDownList?
Или лучше всего просто создать цикл for и вручную создавать элементы Html?





ОБНОВЛЕНО - Я бы посоветовал использовать предложение 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);
Может быть более элегантный способ сделать это, но все вышеперечисленное работает.
Если я правильно помню, для этого было что-то в MvcContrib.
Я согласен с вашим обновлением - решения, опубликованные Rune & Prize, намного аккуратнее, оставляя фактическую разметку для отображения в представлении.
Я столкнулся с той же проблемой, нашел этот вопрос и подумал, что решение, предоставленное Эшем, не то, что я искал; Самостоятельное создание 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 делают это намного более элегантным ИМХО. Без обид, Эш. :)
хороший ответ! Я надеялся, что кто-то будет использовать linq и SelectList :) Рад, что я сначала проверил здесь!
ID = s дает мне DataTextField, а не значение? В чем может быть причина? Спасибо
Rune, я использовал тот же метод, и DropDownList ДЕЙСТВИТЕЛЬНО отображает, но когда он отправляет на сервер, он не сохраняет значение, которое я выбрал.
@BarbarosAlp Чтобы ID был числом, вам нужно преобразовать перечисление в int: select new { ID = (int)s, Name = s.ToString() };
Этот ответ мне нравится больше всего из-за его простоты. Жаль, что вы не получили достаточного количества баллов, поскольку в выбранном ответе использовалось ваше решение.
@Keith, если вы используете дженерики вместо фиксированного enum, Visual Studio не позволит вам выполнить это приведение. Он сообщает вам Cannot convert type 'TEnum' to 'int'. :( (Я использую принятый код ответа, кстати)
@Html.EnumDropDownListFor(
x => x.YourEnumField,
"Select My Type",
new { @class = "form-control" })
@Html.DropDownList("MyType",
EnumHelper.GetSelectList(typeof(MyType)) ,
"Select My Type",
new { @class = "form-control" })
Я свернул ответ 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
У меня не получилось, не могли бы вы помочь. Когда я делаю Post.Post Type.To SelectList (); он не распознает расширение?
Я тоже не мог заставить это работать. Является ли статус вашим свойством Enum в классе задачи? Разве это не одно из перечисленных значений?
Большой! На вопросы выше, да, это одно из значений перечисления, которое становится «выбранным значением». Вы также можете: ((TypesEnum) typeId) .ToSelectList ();
убедитесь, что если вы еще не добавили пространство имен для своих помощников, вы добавляете его через 'using <namespace>;' директива в твоей бритве
Одно предостережение: поскольку метод расширения не имеет ограничений, он будет расширять каждый тип. Это неизбежно, потому что вы не знаете, что такое перечислимые типы, и вы все равно не могли ограничить их (я считаю). Итак, вы обнаружите, что, например, int теперь имеет метод ToSelectList. Я совершил ту же ошибку и считаю, что по этой причине оно лучше в качестве расширения Html.
Вы можете немного ограничить его с помощью: where T: struct, IConvertible См.: stackoverflow.com/questions/79126/…
Это здорово. Если кто-то борется с реализацией, вот как я это сделал. Добавлен класс EnumHelpers в папку HtmlHelpers. Использовал приведенный выше код. Добавлено пространство имен в соответствии с рекомендацией @TodK: <add namespace = "xxx.HtmlHelpers" />. Затем я использовал его на такой странице с бритвой: @ Html.DropDownListFor (model => model.Status, @ Model.Status.ToSelectList ()) HTH
Используется @ Html.DropDownListFor (model => model.SelectedCategory, Model.SelectedCategory.ToSelectList ()}, где SelectedCategory является типом Enum.
Добавлены ограничения универсального типа из комментария Рикардо Нольде и Лизы к stackoverflow.com/a/79903/221708.
Обратите внимание, что в более новой версии ASP.NET MVC есть собственный способ: stackoverflow.com/a/22295360/1361084
«Тип возврата 'System.Int64' не поддерживается. Имя параметра: выражение»
Расширяя ответы 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);
}
Вместо того, чтобы рассматривать каждое значение перечисления как объект TEnum, мы можем рассматривать его как объект, а затем преобразовать его в целое число, чтобы получить распакованное значение.
Примечание: Я также добавил ограничение общего типа, чтобы ограничить типы, для которых это расширение доступно только структурами (базовый тип Enum), и проверку типа во время выполнения, которая гарантирует, что переданная структура действительно является Enum.
Обновление 23.10.12: Добавлен параметр универсального типа для базового типа и исправлена проблема без компиляции, влияющая на .NET 4+.
Спасибо! Это был ответ, который мне был нужен. Я сохраняю целочисленное значение Enum в виде столбца в базе данных, и это решение, похоже, работает отлично.
но что, если вы храните char, а не int? это мой случай. очевидно, я мог бы изменить (int) на (char), но как насчет того, чтобы сделать этот общий. как это сделать?
@Stefandvds Это отличный вопрос относительно приведения к правильному представленному типу. Судя по только что проведенным мною тестам, кажется, что единственный способ добиться этого - указать фактический тип в качестве другого параметра типа. ToSelectList<TEnum, TEnumValue>(this TEnum enumObj) { ... }
@Stefandvds Посмотреть этот вопрос.
Если значения вашего перечисления - int, вы можете просто использовать Value = Convert.ToInt32(e). (int)e не компилируется. :(
Решить проблему получения числа вместо текста с помощью метода расширения 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).
Еще одно исправление этого метода расширения - текущая версия не выбирала текущее значение перечисления. Я исправил последнюю строчку:
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());
}
Так что без функций расширения, если вы ищете простое и легкое ... Это то, что я сделал
<%= Html.DropDownListFor(x => x.CurrentAddress.State, new SelectList(Enum.GetValues(typeof(XXXXX.Sites.YYYY.Models.State))))%>
где XXXXX.Sites.YYYY.Models.State - это перечисление
Возможно, лучше выполнить вспомогательную функцию, но когда времени мало, работа будет выполнена.
Хорошо, что это сработало при заполнении раскрывающегося списка, но как установить выбранное значение по умолчанию в синтаксисе Razor для Html.DropDownListFor? Я хочу показать таблицу с полями со списком перечислений, и мне нужно также установить выбранное значение в соответствии с тем, что было раньше.
Должна иметься возможность передать второй параметр с выбранным значением новой функции SelectList (IEnumerable, object). Документация MSDN: msdn.microsoft.com/en-us/library/dd460123.aspx
Html.DropDownListFor требует только IEnumerable, поэтому альтернативой решению Prise является следующее. Это позволит вам просто написать:
@Html.DropDownListFor(m => m.SelectedItemType, Model.SelectedItemType.ToSelectList())
[Где SelectedItemType - это поле в вашей модели типа ItemTypes, а ваша модель не имеет значения NULL]
Кроме того, вам действительно не нужно обобщать метод расширения, поскольку вы можете использовать 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: ссылка на объект не установлена на экземпляр объекта.') ... Моя 'Модель' имеет значение null ... вероятно, имеет какое-то отношение к 'GetNonNullableModelType', который у Саймона включены
@Cristi, вы правы, это решение не предназначено для использования в условиях, когда ваша модель равна нулю. Я вообще стараюсь избегать такой конструкции и инициализирую "пустую" модель, когда это так.
Что ж, я новичок в asp mvc, но у меня есть опыт работы с .Net. Спасибо, я изучу то, что вы предлагали. Кстати, ваше расширение ToDescription находится далеко за пределами Enum. Думаю, подходит для самого «Объекта». Это то, что я использовал, когда взял код Саймона и немного его очистил.
@Cristi трудно понять, что вы имеете в виду под «далеко за пределами области Enum», но похоже, что вы говорите, что метод расширения ToDescription не строго типизирован для перечисления ItemTypes? Это сделано намеренно и делает метод расширения универсальным для всех перечислений. Если вы сравниваете его с универсальным методом расширения, у каждого подхода есть свои плюсы и минусы. В частности, если вы генерируете, вы не можете ограничить его только перечислениями.
Во-первых, ваш код в порядке, вы не ошиблись. Просто я предпочитаю более общие и логичные вещи. Мое личное мнение состоит в том, что нет необходимости создавать это расширение для Enum, поскольку существует более общий объект (Enum: Object), к которому это расширение применяется лучше. Вы все равно можете использовать остальную часть кода, это расширение будет иметь более высокую адгезию, если будет «ToDescription (это значение объекта)». Теперь надежда прояснилась.
Отлично, с благодарностью. Я изменил value.ToString, чтобы использовать расширение FromCamelCase в случае отсутствия описания. Вот как я катаюсь :)
Если вы хотите добавить поддержку локализации, просто измените метод 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) - это ресурс, который вы хотите загрузить, а затем вы получаете локализованную строку, также полезную, если ваш перечислитель имеет значения с несколькими словами.
Это моя версия вспомогательного метода. Я использую это:
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);
}
Я знаю, что опаздываю на вечеринку по этому поводу, но подумал, что вы можете найти этот вариант полезным, поскольку он также позволяет вам использовать описательные строки, а не константы перечисления в раскрывающемся списке. Для этого украсьте каждую запись перечисления атрибутом [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. К сожалению, он, похоже, не соблюдает атрибут [Описание], поэтому приведенный выше код все еще остается в силе. См. Раздел 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/, с парой дополнений. Если так, то атрибуция будет казаться справедливой ;-)]
+1 Я нашел этот самый полезный из всех ответов здесь. Я смог превратить это в фрагмент кода, который можно использовать повторно. Спасибо!
В Visual Studio есть странная ошибка, когда, если вы не ссылаетесь на System.Web.Mvc.Html, он говорит, что DropDownListFor не может быть найден, но не может решить эту проблему. Приходится вручную делать using System.Web.Mvc.Html;. Просто так ты знаешь.
У меня есть вариант этого по сути, который мы используем во всех наших проектах: gist.github.com/1287511
Красиво, но как добавить в раскрывающийся список первый элемент со значением «- Выбрать -»? Я не хочу включать этот элемент в свое перечисление.
Отличное решение, спасибо, было бы еще лучше, если бы вы могли кэшировать результаты GetEnumDescription
Предоставленный вами код не работает для MVC 4, ваш код просмотра выдает сообщение «Вызов неоднозначен для следующих методов или свойств», а затем перечисляет два длинных имени метода EnumDropDownListFor и свойства.
Новый MVC 5.1 EnumDropDownListFor не использует [Description ("")], но использует [Display (Name = "")]! Наслаждаться :)
Красивый. Пришлось немного погуглить, чтобы найти, как все это собрать. stackoverflow.com/questions/10998827/…
Я думаю, что код Selected = value.Equals(metadata.Model) на самом деле не повлияет, потому что MVC будет отображать выбранное значение раскрывающегося списка в соответствии со значением свойства модели внутри переменной выражения.
Взгляните на это: codeproject.com/Articles/776908/Dealing-with-Enum-in-MVC
@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))
Вы также можете использовать мои пользовательские HtmlHelpers в Griffin.MvcContrib. Следующий код:
@Html2.CheckBoxesFor(model => model.InputType) <br />
@Html2.RadioButtonsFor(model => model.InputType) <br />
@Html2.DropdownFor(model => model.InputType) <br />
Создает:

Это ответы 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, является выбранным элементом.
Или вы можете просто использовать Id = Convert.ToInt32(e).
Лучшим решением, которое я нашел для этого, было объединение этот блог с Ответ Саймона Голдстоуна.
Это позволяет использовать перечисление в модели. По сути, идея состоит в том, чтобы использовать целочисленное свойство, а также перечисление, и имитировать целочисленное свойство.
Затем используйте атрибут [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, LD N.Models.YesPartialN oEnum?> (System.Web.M vc. HtmlHelper <MyMode l>, System.Linq.Expressions.Expression <System.Func <MyModel, LDN.M odels.YesPartialNoEn um? >>) 'и .... "и тот же самый метод с теми же реквизитами повторяется снова (здесь недостаточно символов).
Это версия для 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. Перечисление Flags не будет работать с этим. Тем не менее, творческое использование индексированного Select.
Я закончил тем, что создал методы расширения, чтобы сделать то, что, по сути, является здесь ответом accept. Последняя половина Gist посвящена конкретно Enum.
Я очень опаздываю с этим, но я только что нашел действительно классный способ сделать это с помощью одной строки кода, если вы счастливы добавить пакет NuGet Безудержная мелодия (красивая небольшая библиотека от Джона Скита).
Это решение лучше, потому что:
Итак, вот шаги, чтобы заставить это работать:
Добавьте свойство в свою модель следующим образом:
//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 за использование кода Джона Скита :), шучу, хороший
Вот лучшее инкапсулированное решение:
https://www.spicelogic.com/Blog/enum-dropdownlistfor-asp-net-mvc-5
Скажем, вот ваша модель:

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

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

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

Вы можете скачать образец проекта по предоставленной мной ссылке.
Обновлено: вот код:
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();
}
}
Просто мое мнение, но я думаю, что этот ответ намного чище, чем принятый ответ. Мне особенно нравится возможность использования атрибута Description. Я добавил код, чтобы люди могли копировать / вставлять его, не скачивая.
Вызовите метод расширения как EnumDropDownListFor, а не DropDownListFor. Использование: -> @ Html.EnumDropDownListFor (x => x.Gender)
Для тех, кто хочет добавить еще один элемент «Пожалуйста, выберите» return htmlHelper.DropDownListFor (выражение, createSelectList (выражение.ReturnType, selected, firstElement), «Пожалуйста, выберите»);
Работает отлично! Однако на странице Details DisplayFor () показывает выбранное значение перечисления вместо соответствующего описания. Я полагаю, это требует перегрузки DisplayFor () для типа перечисления. У кого-нибудь есть решение для этого?
Основываясь на ответе Саймона, аналогичный подход состоит в том, чтобы получить значения 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))
}
Если вас это интересует, я поместил гораздо более подробный ответ здесь, в своем блоге:
@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 ----"}
}
)
@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" }
})
Я думаю, что это неверный ответ, он вообще не использует перечисление для заполнения раскрывающегося списка.
Что ж, я действительно опаздываю на вечеринку, но, как бы то ни было, я написал в блоге об этой самой теме, в результате чего я создаю класс 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() });
}
}
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)
Вы вручную добавляете элементы перечисления ... Если ваше перечисление изменяется, вам необходимо дважды изменить свой код. А что, если у вас много перечислений для многих выпадающих списков?
@Html.DropDownListFor(model => model.Type, Enum.GetNames(typeof(Rewards.Models.PropertyType)).Select(e => new SelectListItem { Text = e }))
Хорошо! Как таким образом получить значение и текст из перечисления? Я имею в виду, что у меня есть SomeEnum {some1 = 1, some2 = 2} Мне нужно получить числа (1, 2) для значения и текст (some1, some2) для текста списка выбора
Теперь эта функция прямо из коробки поддерживается в версиях от MVC 5.1 до @Html.EnumDropDownListFor().
Проверьте следующую ссылку:
https://docs.microsoft.com/en-us/aspnet/mvc/overview/releases/mvc51-release-notes#Enum
Очень жаль, что Microsoft потребовалось 5 лет, чтобы реализовать такую функцию, которая так востребована согласно результатам голосования выше!
В 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>()">
Вы должны создать новый вопрос, относящийся к MVC 5.1, и поместить его в качестве ответа, а затем отправить мне ссылку на сообщение, чтобы я мог проголосовать за любимого.
Что мне не нравится в EnumDropDownListFor (), так это то, что он сохраняет в БД значение int перечисления, а не текст, поэтому, если вы когда-либо решите добавить новый элемент перечисления, он обязательно должен быть в конце списка , чтобы не потерять связь сохраненных значений int в базе данных с исходными позициями элементов перечисления. Это ненужное ограничение, если текст сохранен. Кроме того, я предпочитаю смотреть на базу данных и видеть текст, а не на целые числа, в которых затем мне нужно искать текстовые значения в другом месте. В остальном этот html-помощник очень удобен в использовании.
@Giovanni - вы можете указать свои числовые значения.
это еще не ужин enum [Flags] :(
@Giovanni Строгий дизайн должен назначать значение для каждой записи перечисления (если это важно), в противном случае значение не должно иметь значения (и поэтому размещение новых в конце не должно быть проблемой). Сохранение значений int лучше, когда дело доходит до экономии места и увеличения производительности (при выполнении некоторого поиска).
Как определить дефолт? Пришлось использовать @Html.DropDownListFor(m => m.MyProperty, @Html.GetEnumSelectList(typeof(MyTypes.MyModel)))
Нашел ответ здесь. Однако некоторые из моих перечислений имеют атрибут [Description(...)], поэтому я изменил код, чтобы обеспечить его поддержку:
enum Abc
{
[Description("Cba")]
Abc,
Def
}
public static MvcHtmlString EnumDropDownList<TEnum>(this HtmlHelper htmlHelper, string name, TEnum selectedValue)
{
IEnumerable<TEnum> values = Enum.GetValues(typeof(TEnum))
.Cast<TEnum>();
List<SelectListItem> items = new List<SelectListItem>();
foreach (var value in values)
{
string text = value.ToString();
var member = typeof(TEnum).GetMember(value.ToString());
if (member.Count() > 0)
{
var customAttributes = member[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (customAttributes.Count() > 0)
{
text = ((DescriptionAttribute)customAttributes[0]).Description;
}
}
items.Add(new SelectListItem
{
Text = text,
Value = value.ToString(),
Selected = (value.Equals(selectedValue))
});
}
return htmlHelper.DropDownList(
name,
items
);
}
Надеюсь, это поможет.
Я хочу вернуть член типа = DropdownList. Я хорошо разбираюсь в атрибуте Text = DescriptionAttribute, но мне трудно получить значение int из Value
Супер простой способ сделать это - без всяких расширений, которые кажутся излишними:
Ваше перечисление:
public enum SelectedLevel
{
Level1,
Level2,
Level3,
Level4
}
Внутри вашего контроллера привяжите Enum к списку:
List<SelectedLevel> myLevels = Enum.GetValues(typeof(SelectedLevel)).Cast<SelectedLevel>().ToList();
После этого бросьте его в ViewBag:
ViewBag.RequiredLevel = new SelectList(myLevels);
Наконец, просто привяжите его к View:
@Html.DropDownList("selectedLevel", (SelectList)ViewBag.RequiredLevel, new { @class = "form-control" })
Это, безусловно, самый простой способ, который я нашел, и не требует никаких расширений или чего-то такого безумного.
ОБНОВИТЬ: см. Комментарий Эндрюса ниже.
Это работает, только если вы не присвоили своему перечислению никакого значения. Если бы у вас был Level1 = 1, то значение в раскрывающемся списке было бы "Level1" вместо 1.
Здесь вариант Мартина Фаартофта, где вы можете разместить собственные метки, которые удобны для локализации.
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)
Вы перечисляете каждый элемент перечисления в своем представлении? Что произойдет, если вы добавите новый элемент в свое перечисление и у вас будет много перечислений для многих раскрывающихся списков?
Я сделал следующее и успешно работает:
@model MyModel.cs
@Html.EnumDropDownListFor(m=>m.MyItemType )
public ItemTypes MyItemType { get; set; }
Вы просто продублировали ответ @Ofiris, и это работает только для ASP.NET MVC 5.1 и выше.
В 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; }
}
//// 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>
Я хотел бы ответить на этот вопрос по-другому, когда пользователю не нужно ничего делать в выражении 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 в качестве дополнительного параметра для выбора раскрывающегося списка по умолчанию.
В .NET Core вы можете просто использовать это:
@Html.DropDownListFor(x => x.Foo, Html.GetEnumSelectList<MyEnum>())
Или с помощью тега-помощника <select asp-for = "Model.Foo" class = "form-control" asp-items = "@Html.GetEnumSelectList<MyEnum>()"></select>.
да, я бы сказал, помощники по тегам даже лучше, поскольку формат ближе к чистому HTML;)
Также вы можете сделать это @ Html.DropDownListFor (x => x.Foo, Html.GetEnumSelectList (typeof (FooEnum)))
Просто и мило. ТЫ!
отличный ответ. Искал, чтобы не нужно было создавать собственный метод для этого
Спасибо. На самом деле это реализация, к которой я пришел, но я надеялся, что она уже встроена в структуру. Думаю, нет. :-(