I'm trying to use the Html.DropDownList extension method but can't figure out how to use it with an enumeration.
Let's say I have an enumeration like this:
public enum ItemTypes
{
Movie = 1,
Game = 2,
Book = 3
}
How do I go about creating a dropdown with these values using the Html.DropDownList extension method?
Or is my best bet to simply create a for loop and create the Html elements manually?
For MVC v5.1 use Html.EnumDropDownListFor
#Html.EnumDropDownListFor(
x => x.YourEnumField,
"Select My Type",
new { #class = "form-control" })
For MVC v5 use EnumHelper
#Html.DropDownList("MyType",
EnumHelper.GetSelectList(typeof(MyType)) ,
"Select My Type",
new { #class = "form-control" })
For MVC 5 and lower
I rolled Rune's answer into an extension method:
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);
}
}
}
This allows you to write:
ViewData["taskStatus"] = task.Status.ToSelectList();
by using MyApp.Common
I know I'm late to the party on this, but thought you might find this variant useful, as this one also allows you to use descriptive strings rather than enumeration constants in the drop down. To do this, decorate each enumeration entry with a [System.ComponentModel.Description] attribute.
For example:
public enum TestEnum
{
[Description("Full test")]
FullTest,
[Description("Incomplete or partial test")]
PartialTest,
[Description("No test performed")]
None
}
Here is my code:
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);
}
You can then do this in your view:
#Html.EnumDropDownListFor(model => model.MyEnumProperty)
**EDIT 2014-JAN-23: Microsoft have just released MVC 5.1, which now has an EnumDropDownListFor feature. Sadly it does not appear to respect the [Description] attribute so the code above still stands.See Enum section in Microsoft's release notes for MVC 5.1.
Update: It does support the Display attribute [Display(Name = "Sample")] though, so one can use that.
[Update - just noticed this, and the code looks like an extended version of the code here: https://blogs.msdn.microsoft.com/stuartleeks/2010/05/21/asp-net-mvc-creating-a-dropdownlist-helper-for-enums/, with a couple of additions. If so, attribution would seem fair ;-)]
In ASP.NET MVC 5.1, they added the EnumDropDownListFor() helper, so no need for custom extensions:
Model:
public enum MyEnum
{
[Display(Name = "First Value - desc..")]
FirstValue,
[Display(Name = "Second Value - desc...")]
SecondValue
}
View:
#Html.EnumDropDownListFor(model => model.MyEnum)
Using Tag Helper (ASP.NET MVC 6):
<select asp-for="#Model.SelectedValue" asp-items="Html.GetEnumSelectList<MyEnum>()">
I bumped into the same problem, found this question, and thought that the solution provided by Ash wasn't what I was looking for; Having to create the HTML myself means less flexibility compared to the built-in Html.DropDownList() function.
Turns out C#3 etc. makes this pretty easy. I have an enum called 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);
This creates a good ol' SelectList that can be used like you're used to in the view:
<td><b>Status:</b></td><td><%=Html.DropDownList("taskStatus")%></td></tr>
The anonymous type and LINQ makes this so much more elegant IMHO. No offence intended, Ash. :)
Here is a better encapsulated solution:
https://www.spicelogic.com/Blog/enum-dropdownlistfor-asp-net-mvc-5
Say here is your model:
Sample Usage:
Generated UI:
And generated HTML
The Helper Extension Source Code snap shot:
You can download the sample project from the link I provided.
EDIT: Here's the code:
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();
}
}
Html.DropDownListFor only requires an IEnumerable, so an alternative to Prise's solution is as follows. This will allow you to simply write:
#Html.DropDownListFor(m => m.SelectedItemType, Model.SelectedItemType.ToSelectList())
[Where SelectedItemType is a field on your model of type ItemTypes, and your model is non-null]
Also, you don't really need to genericize the extension method as you can use enumValue.GetType() rather than typeof(T).
EDIT: Integrated Simon's solution here as well, and included ToDescription extension method.
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();
}
}
So without Extension functions if you are looking for simple and easy.. This is what I did
<%= Html.DropDownListFor(x => x.CurrentAddress.State, new SelectList(Enum.GetValues(typeof(XXXXX.Sites.YYYY.Models.State))))%>
where XXXXX.Sites.YYYY.Models.State is an enum
Probably better to do helper function, but when time is short this will get the job done.
Expanding on Prise and Rune's answers, if you'd like to have the value attribute of your select list items map to the integer value of the Enumeration type, rather than the string value, use the following code:
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);
}
Instead of treating each Enumeration value as a TEnum object, we can treat it as a object and then cast it to integer to get the unboxed value.
Note:
I also added a generic type constraint to restrict the types for which this extension is available to only structs (Enum's base type), and a run-time type validation which ensures that the struct passed in is indeed an Enum.
Update 10/23/12:
Added generic type parameter for underlying type and fixed non-compilation issue affecting .NET 4+.
In .NET Core you can just use this:
#Html.DropDownListFor(x => x.Foo, Html.GetEnumSelectList<MyEnum>())
To solve the problem of getting the number instead of text using Prise's extension method.
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);
}
A super easy way to get this done - without all the extension stuff that seems overkill is this:
Your enum:
public enum SelectedLevel
{
Level1,
Level2,
Level3,
Level4
}
Inside of your controller bind the Enum to a List:
List<SelectedLevel> myLevels = Enum.GetValues(typeof(SelectedLevel)).Cast<SelectedLevel>().ToList();
After that throw it into a ViewBag:
ViewBag.RequiredLevel = new SelectList(myLevels);
Finally simply bind it to the View:
#Html.DropDownList("selectedLevel", (SelectList)ViewBag.RequiredLevel, new { #class = "form-control" })
This is by far the easiest way I found and does not require any extensions or anything that crazy.
UPDATE: See Andrews comment below.
The best solution I found for this was combining this blog with Simon Goldstone's answer.
This allows use of the enum in the model. Essentially the idea is to use an integer property as well as the enum, and emulate the integer property.
Then use the [System.ComponentModel.Description] attribute for annotating the model with your display text, and use an "EnumDropDownListFor" extension in your view.
This makes both the view and model very readable and maintainable.
Model:
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; }
}
View:
#using MyProject.Extensions
{
//...
#Html.EnumDropDownListFor(model => model.CuriousQuestion)
//...
}
Extension (directly from Simon Goldstone's answer, included here for completeness):
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);
}
}
}
You want to look at using something like Enum.GetValues
#Html.DropDownListFor(model => model.Type, Enum.GetNames(typeof(Rewards.Models.PropertyType)).Select(e => new SelectListItem { Text = e }))
Now this feature is supported out-of-the-box in MVC 5.1 through #Html.EnumDropDownListFor()
Check the following link:
https://learn.microsoft.com/en-us/aspnet/mvc/overview/releases/mvc51-release-notes#Enum
It is really shame that it took Microsoft 5 years to implement such as feature which is so in demand according to the voting above!
This is Rune & Prise answers altered to use the Enum int value as the ID.
Sample Enum:
public enum ItemTypes
{
Movie = 1,
Game = 2,
Book = 3
}
Extension method:
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()));
}
Sample of usage:
<%= Html.DropDownList("MyEnumList", ItemTypes.Game.ToSelectList()) %>
Remember to Import the namespace containing the Extension method
<%# Import Namespace="MyNamespace.LocationOfExtensionMethod" %>
Sample of generated HTML:
<select id="MyEnumList" name="MyEnumList">
<option value="1">Movie</option>
<option selected="selected" value="2">Game</option>
<option value="3">Book </option>
</select>
Note that the item that you use to call the ToSelectList on is the selected item.
This is version for 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)
Building on Simon's answer, a similar approach is to get the Enum values to display from a Resource file, instead of in a description attribute within the Enum itself. This is helpful if your site needs to be rendered in more than one language and if you were to have a specific resource file for Enums, you could go one step further and have just Enum values, in your Enum and reference them from the extension by a convention such as [EnumName]_[EnumValue] - ultimately less typing!
The extension then looks like:
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();
}
Resources in the Enums.Resx file looking like
ItemTypes_Movie : Film
One other thing I like to do is, instead of calling the extension method directly, I'd rather call it with a #Html.EditorFor(x => x.MyProperty), or ideally just have the whole form, in one neat #Html.EditorForModel(). To do this I change the string template to look like this
#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))
}
If this interests you, I've put a much more detailed answer here on my blog:
http://paulthecyclist.com/2013/05/24/enum-dropdown/
Well I'm really late to the party, but for what it is worth, I have blogged about this very subject whereby I create a EnumHelper class that enables very easy transformation.
http://jnye.co/Posts/4/creating-a-dropdown-list-from-an-enum-in-mvc-and-c%23
In your controller:
//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);
In your View:
#Html.DropDownList("DropDownList")
#* OR *#
#Html.DropDownListFor(m => m.Property, ViewBag.DropDownList as SelectList, null)
The helper class:
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() });
}
}
I am very late on this one but I just found a really cool way to do this with one line of code, if you are happy to add the Unconstrained Melody NuGet package (a nice, small library from Jon Skeet).
This solution is better because:
It ensures (with generic type constraints) that the value really is an enum value (due to Unconstrained Melody)
It avoids unnecessary boxing (due to Unconstrained Melody)
It caches all the descriptions to avoid using reflection on every call (due to Unconstrained Melody)
It is less code than the other solutions!
So, here are the steps to get this working:
In Package Manager Console, "Install-Package UnconstrainedMelody"
Add a property on your model like so:
//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() });
}
}
Now that you have the List of SelectListItem exposed on your model, you can use the #Html.DropDownList or #Html.DropDownListFor using this property as the source.
I found an answer here. However, some of my enums have [Description(...)] attribute, so I've modified the code to provide support for that:
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
);
}
Hope that helps.
Another fix to this extension method - the current version didn't select the enum's current value. I fixed the last line:
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());
}
If you want to add localization support just change the s.toString() method to something like this:
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()) };
In here the typeof(Resources) is the resource you want to load, and then you get the localized String, also useful if your enumerator has values with multiple words.
This is my version of helper method.
I use this:
var values = from int e in Enum.GetValues(typeof(TEnum))
select new { ID = e, Name = Enum.GetName(typeof(TEnum), e) };
Instead of that:
var values = from TEnum e in Enum.GetValues(typeof(TEnum))
select new { ID = (int)Enum.Parse(typeof(TEnum),e.ToString())
, Name = e.ToString() };
Here it is:
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);
}
You can also use my custom HtmlHelpers in Griffin.MvcContrib. The following code:
#Html2.CheckBoxesFor(model => model.InputType) <br />
#Html2.RadioButtonsFor(model => model.InputType) <br />
#Html2.DropdownFor(model => model.InputType) <br />
Generates:
https://github.com/jgauffin/griffin.mvccontrib
#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 ----"}
}
)
I would like to answer this question in a different way where, user need not to do anything in controller or Linq expression. This way...
I have a 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*/
}
Now I canto simply create a dropdown by using this enum.
#Html.DropDownList("accessLevel",new SelectList(AccessLevelEnum.GetValues(typeof(AccessLevelEnum))),new { #class = "form-control" })
OR
#Html.DropDownListFor(m=>m.accessLevel,new SelectList(AccessLevelEnum.GetValues(typeof(AccessLevelEnum))),new { #class = "form-control" })
If you want to make a index selected then try this
#Html.DropDownListFor(m=>m.accessLevel,new SelectList(AccessLevelEnum.GetValues(typeof(AccessLevelEnum)) , AccessLevelEnum.NoAccess ),new { #class = "form-control" })
Here I have used AccessLevelEnum.NoAccess as an extra parameter for default selecting the dropdown.
#Simon Goldstone: Thanks for your solution, it can be perfectly applied in my case. The only problem is I had to translate it to VB. But now it is done and to save other people's time (in case they need it) I put it here:
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
End You use it like this:
#Html.EnumDropDownListFor(Function(model) (model.EnumField))
I ended up creating extention methods to do what is essentially the accept answer here. The last half of the Gist deals with Enum specifically.
https://gist.github.com/3813767
#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" }
})
Related
I've got a property in my model called Promotion that its type is a flag enum called UserPromotion. Members of my enum have display attributes set as follows:
[Flags]
public enum UserPromotion
{
None = 0x0,
[Display(Name = "Send Job Offers By Mail")]
SendJobOffersByMail = 0x1,
[Display(Name = "Send Job Offers By Sms")]
SendJobOffersBySms = 0x2,
[Display(Name = "Send Other Stuff By Sms")]
SendPromotionalBySms = 0x4,
[Display(Name = "Send Other Stuff By Mail")]
SendPromotionalByMail = 0x8
}
Now I want to be able to create say a ul in my view to show the selected values of my Promotion property. This is what I have done so far but the problem is that how can I get the display names here?
<ul>
#foreach (int aPromotion in #Enum.GetValues(typeof(UserPromotion)))
{
var currentPromotion = (int)Model.JobSeeker.Promotion;
if ((currentPromotion & aPromotion) == aPromotion)
{
<li>Here I don't know how to get the display attribute of "currentPromotion".</li>
}
}
</ul>
One liner - Fluent syntax
public static class Extensions
{
/// <summary>
/// A generic extension method that aids in reflecting
/// and retrieving any attribute that is applied to an `Enum`.
/// </summary>
public static TAttribute GetAttribute<TAttribute>(this Enum enumValue)
where TAttribute : Attribute
{
return enumValue.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<TAttribute>();
}
}
Example
public enum Season
{
[Display(Name = "It's autumn")]
Autumn,
[Display(Name = "It's winter")]
Winter,
[Display(Name = "It's spring")]
Spring,
[Display(Name = "It's summer")]
Summer
}
public class Foo
{
public Season Season = Season.Summer;
public void DisplayName()
{
var seasonDisplayName = Season.GetAttribute<DisplayAttribute>();
Console.WriteLine("Which season is it?");
Console.WriteLine (seasonDisplayName.Name);
}
}
Output
Which season is it?
It's summer
UPDATE
First solution was focused on getting display names from enum. Code below should be exact solution for your problem.
You can use this helper class for enums:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
public static class EnumHelper<T>
where T : struct, Enum // This constraint requires C# 7.3 or later.
{
public static IList<T> GetValues(Enum value)
{
var enumValues = new List<T>();
foreach (FieldInfo fi in value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public))
{
enumValues.Add((T)Enum.Parse(value.GetType(), fi.Name, false));
}
return enumValues;
}
public static T Parse(string value)
{
return (T)Enum.Parse(typeof(T), value, true);
}
public static IList<string> GetNames(Enum value)
{
return value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public).Select(fi => fi.Name).ToList();
}
public static IList<string> GetDisplayValues(Enum value)
{
return GetNames(value).Select(obj => GetDisplayValue(Parse(obj))).ToList();
}
private static string lookupResource(Type resourceManagerProvider, string resourceKey)
{
var resourceKeyProperty = resourceManagerProvider.GetProperty(resourceKey,
BindingFlags.Static | BindingFlags.Public, null, typeof(string),
new Type[0], null);
if (resourceKeyProperty != null)
{
return (string)resourceKeyProperty.GetMethod.Invoke(null, null);
}
return resourceKey; // Fallback with the key name
}
public static string GetDisplayValue(T value)
{
var fieldInfo = value.GetType().GetField(value.ToString());
var descriptionAttributes = fieldInfo.GetCustomAttributes(
typeof(DisplayAttribute), false) as DisplayAttribute[];
if (descriptionAttributes[0].ResourceType != null)
return lookupResource(descriptionAttributes[0].ResourceType, descriptionAttributes[0].Name);
if (descriptionAttributes == null) return string.Empty;
return (descriptionAttributes.Length > 0) ? descriptionAttributes[0].Name : value.ToString();
}
}
And then you can use it in your view as following:
<ul>
#foreach (var value in #EnumHelper<UserPromotion>.GetValues(UserPromotion.None))
{
if (value == Model.JobSeeker.Promotion)
{
var description = EnumHelper<UserPromotion>.GetDisplayValue(value);
<li>#Html.DisplayFor(e => description )</li>
}
}
</ul>
Building on Aydin's great answer, here's an extension method that doesn't require any type parameters.
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
public static class EnumExtensions
{
public static string GetDisplayName(this Enum enumValue)
{
return enumValue.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>()
.GetName();
}
}
NOTE: GetName() should be used instead of the Name property. This ensures that the localized string will be returned if using the ResourceType attribute property.
Example
To use it, just reference the enum value in your view.
#{
UserPromotion promo = UserPromotion.SendJobOffersByMail;
}
Promotion: #promo.GetDisplayName()
Output
Promotion: Send Job Offers By Mail
Based on Aydin's answer I would suggest a less "duplicatious" implementation (because we could easily get the Type from the Enum value itself, instead of providing it as a parameter 馃槈:
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
public static string GetDisplayName(this Enum enumValue)
{
return enumValue.GetType().GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>()
.Name;
}
EDIT (based upon #Vahagn Nahapetyan's comment)
public static string GetDisplayName(this Enum enumValue)
{
return enumValue.GetType()?
.GetMember(enumValue.ToString())?
.First()?
.GetCustomAttribute<DisplayAttribute>()?
.Name;
}
Now we can use it very clean in this way:
public enum Season
{
[Display(Name = "The Autumn")]
Autumn,
[Display(Name = "The Weather")]
Winter,
[Display(Name = "The Tease")]
Spring,
[Display(Name = "The Dream")]
Summer
}
Season.Summer.GetDisplayName();
Which results in
"The Dream"
If you are using MVC 5.1 or upper there is simplier and clearer way: just use data annotation (from System.ComponentModel.DataAnnotations namespace) like below:
public enum Color
{
[Display(Name = "Dark red")]
DarkRed,
[Display(Name = "Very dark red")]
VeryDarkRed,
[Display(Name = "Red or just black?")]
ReallyDarkRed
}
And in view, just put it into proper html helper:
#Html.EnumDropDownListFor(model => model.Color)
Building on Todd's great answer which built on Aydin's great answer, here's a generic extension method which doesn't require any type parameters.
/// <summary>
/// Gets human-readable version of enum.
/// </summary>
/// <returns>effective DisplayAttribute.Name of given enum.</returns>
public static string GetDisplayName<T>(this T enumValue) where T : IComparable, IFormattable, IConvertible // C# 7.3+: where T : struct, Enum
{
if (!typeof(T).IsEnum) // Not needed in C# 7.3+ with above updated constraint
throw new ArgumentException("Argument must be of type Enum");
DisplayAttribute displayAttribute = enumValue.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>();
string displayName = displayAttribute?.GetName();
return displayName ?? enumValue.ToString();
}
I needed this for my project because something like the below code, where not every member of the enum has a DisplayAttribute, throws an exception with Todd's solution:
public class MyClass
{
public enum MyEnum
{
[Display(Name="ONE")]
One,
// No DisplayAttribute
Two
}
public void UseMyEnum()
{
MyEnum foo = MyEnum.One;
MyEnum bar = MyEnum.Two;
Console.WriteLine(foo.GetDisplayName());
Console.WriteLine(bar.GetDisplayName());
}
}
// Output:
//
// ONE
// Two
If this is a complicated solution to a simple problem, please let me know, but this was the fix I used.
You could use Type.GetMember Method, then get the attribute info using reflection:
// display attribute of "currentPromotion"
var type = typeof(UserPromotion);
var memberInfo = type.GetMember(currentPromotion.ToString());
var attributes = memberInfo[0].GetCustomAttributes(typeof(DisplayAttribute), false);
var description = ((DisplayAttribute)attributes[0]).Name;
There were a few similar posts here:
Getting attributes of Enum's value
How to make MVC3 DisplayFor show the value of an Enum's Display-Attribute?
In .NET5, I used DisplayTextFor without needing helper or extension methods:
#Html.DisplayTextFor(m => m.SomeEnumProperty)
Where SomeEnumProperty has a value of:
public enum MyEnum
{
[Display(Name = "Not started")]
NotStarted = 0,
[Display(Name = "Weird display name instead of just 'Started'")]
Started = 1,
}
For ASP.Net Core 3.0, this worked for me (credit to previous answerers).
My Enum class:
using System;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
public class Enums
{
public enum Duration
{
[Display(Name = "1 Hour")]
OneHour,
[Display(Name = "1 Day")]
OneDay
}
// Helper method to display the name of the enum values.
public static string GetDisplayName(Enum value)
{
return value.GetType()?
.GetMember(value.ToString())?.First()?
.GetCustomAttribute<DisplayAttribute>()?
.Name;
}
}
My View Model Class:
public class MyViewModel
{
public Duration Duration { get; set; }
}
An example of a razor view displaying a label and a drop-down list. Notice the drop-down list does not require a helper method:
#model IEnumerable<MyViewModel>
#foreach (var item in Model)
{
<label asp-for="#item.Duration">#Enums.GetDisplayName(item.Duration)</label>
<div class="form-group">
<label asp-for="#item.Duration" class="control-label">Select Duration</label>
<select asp-for="#item.Duration" class="form-control"
asp-items="Html.GetEnumSelectList<Enums.Duration>()">
</select>
</div>
}
With Core 2.1,
public static string GetDisplayName(Enum enumValue)
{
return enumValue.GetType()?
.GetMember(enumValue.ToString())?[0]?
.GetCustomAttribute<DisplayAttribute>()?
.Name;
}
<ul>
#foreach (int aPromotion in #Enum.GetValues(typeof(UserPromotion)))
{
var currentPromotion = (int)Model.JobSeeker.Promotion;
if ((currentPromotion & aPromotion) == aPromotion)
{
<li>#Html.DisplayFor(e => currentPromotion)</li>
}
}
</ul>
combining all edge-cases together from above:
enum members with base object members' names (Equals, ToString)
optional Display attribute
here is my code:
public enum Enum
{
[Display(Name = "What a weird name!")]
ToString,
Equals
}
public static class EnumHelpers
{
public static string GetDisplayName(this Enum enumValue)
{
var enumType = enumValue.GetType();
return enumType
.GetMember(enumValue.ToString())
.Where(x => x.MemberType == MemberTypes.Field && ((FieldInfo)x).FieldType == enumType)
.First()
.GetCustomAttribute<DisplayAttribute>()?.Name ?? enumValue.ToString();
}
}
void Main()
{
Assert.Equals("What a weird name!", Enum.ToString.GetDisplayName());
Assert.Equals("Equals", Enum.Equals.GetDisplayName());
}
You need to use a bit of reflection in order to access that attribute:
var type = typeof(UserPromotion);
var member = type.GetMember(Model.JobSeeker.Promotion.ToString());
var attributes = member[0].GetCustomAttributes(typeof(DisplayAttribute), false);
var name = ((DisplayAttribute)attributes[0]).Name;
I recommend wrapping this method in a extension method or perform this in a view model.
I'm sorry to do this, but I couldn't use any of the other answers as-is and haven't time to duke it out in the comments.
Uses C# 6 syntax.
static class EnumExtensions
{
/// returns the localized Name, if a [Display(Name="Localised Name")] attribute is applied to the enum member
/// returns null if there isnt an attribute
public static string DisplayNameOrEnumName(this Enum value)
// => value.DisplayNameOrDefault() ?? value.ToString()
{
// More efficient form of ^ based on http://stackoverflow.com/a/17034624/11635
var enumType = value.GetType();
var enumMemberName = Enum.GetName(enumType, value);
return enumType
.GetEnumMemberAttribute<DisplayAttribute>(enumMemberName)
?.GetName() // Potentially localized
?? enumMemberName; // Or fall back to the enum name
}
/// returns the localized Name, if a [Display] attribute is applied to the enum member
/// returns null if there is no attribute
public static string DisplayNameOrDefault(this Enum value) =>
value.GetEnumMemberAttribute<DisplayAttribute>()?.GetName();
static TAttribute GetEnumMemberAttribute<TAttribute>(this Enum value) where TAttribute : Attribute =>
value.GetType().GetEnumMemberAttribute<TAttribute>(value.ToString());
static TAttribute GetEnumMemberAttribute<TAttribute>(this Type enumType, string enumMemberName) where TAttribute : Attribute =>
enumType.GetMember(enumMemberName).Single().GetCustomAttribute<TAttribute>();
}
Building further on Aydin's and Todd's answers, here is an extension method that also lets you get the name from a resource file
using AppResources;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Resources;
public static class EnumExtensions
{
public static string GetDisplayName(this Enum enumValue)
{
var enumMember= enumValue.GetType()
.GetMember(enumValue.ToString());
DisplayAttribute displayAttrib = null;
if (enumMember.Any()) {
displayAttrib = enumMember
.First()
.GetCustomAttribute<DisplayAttribute>();
}
string name = null;
Type resource = null;
if (displayAttrib != null)
{
name = displayAttrib.Name;
resource = displayAttrib.ResourceType;
}
return String.IsNullOrEmpty(name) ? enumValue.ToString()
: resource == null ? name
: new ResourceManager(resource).GetString(name);
}
}
and use it like
public enum Season
{
[Display(ResourceType = typeof(Resource), Name = Season_Summer")]
Summer
}
For just displaying enum's display name attribute just use
Microsoft.AspNetCore.Mvc.Rendering's
#Html.DisplayFor(x => EnumType.EnumValue)
That's would be enough.
For displaying SelectList write as following:
<select id="someIdForTheEndPoint" asp-items="Html.GetEnumSelectList<EnumType>()">
<option selected="selected" value="">Select value</option>
</select>
I have two solutions for this Question.
The first solution is on getting display names from enum.
public enum CourseLocationTypes
{
[Display(Name = "On Campus")]
OnCampus,
[Display(Name = "Online")]
Online,
[Display(Name = "Both")]
Both
}
public static string DisplayName(this Enum value)
{
Type enumType = value.GetType();
string enumValue = Enum.GetName(enumType, value);
MemberInfo member = enumType.GetMember(enumValue)[0];
object[] attrs = member.GetCustomAttributes(typeof(DisplayAttribute), false);
string outString = ((DisplayAttribute)attrs[0]).Name;
if (((DisplayAttribute)attrs[0]).ResourceType != null)
{
outString = ((DisplayAttribute)attrs[0]).GetName();
}
return outString;
}
<h3 class="product-title white">#Model.CourseLocationType.DisplayName()</h3>
The second Solution is on getting display name from enum name but that will be enum split in developer language it's called patch.
public static string SplitOnCapitals(this string text)
{
var r = new Regex(#"
(?<=[A-Z])(?=[A-Z][a-z]) |
(?<=[^A-Z])(?=[A-Z]) |
(?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace);
return r.Replace(text, " ");
}
<div class="widget-box pt-0">
#foreach (var item in Enum.GetNames(typeof(CourseLocationType)))
{
<label class="pr-2 pt-1">
#Html.RadioButtonFor(x => x.CourseLocationType, item, new { type = "radio", #class = "iCheckBox control-label" }) #item.SplitOnCapitals()
</label>
}
#Html.ValidationMessageFor(x => x.CourseLocationType)
</div>
2020 Update: An updated version of the function provided by many in this thread but now for C# 7.3 onwards:
Now you can restrict generic methods to enums types so you can write a single method extension to use it with all your enums like this:
The generic extension method:
public static string ATexto<T>(this T enumeraci贸n) where T : struct, Enum {
var tipo = enumeraci贸n.GetType();
return tipo.GetMember(enumeraci贸n.ToString())
.Where(x => x.MemberType == MemberTypes.Field && ((FieldInfo)x).FieldType == tipo).First()
.GetCustomAttribute<DisplayAttribute>()?.Name ?? enumeraci贸n.ToString();
}
The enum:
public enum TipoImpuesto {
IVA, INC, [Display(Name = "IVA e INC")]IVAeINC, [Display(Name = "No aplica")]NoAplica };
How to use it:
var tipoImpuesto = TipoImpuesto.IVAeINC;
var textoTipoImpuesto = tipoImpuesto.ATexto(); // Prints "IVA e INC".
Bonus, Enums with Flags: If you are dealing with normal enums the function above is enough, but if any of your enums can take multiple values with the use of flags then you will need to modify it like this (This code uses C#8 features):
public static string ATexto<T>(this T enumeraci贸n) where T : struct, Enum {
var tipo = enumeraci贸n.GetType();
var textoDirecto = enumeraci贸n.ToString();
string obtenerTexto(string textoDirecto) => tipo.GetMember(textoDirecto)
.Where(x => x.MemberType == MemberTypes.Field && ((FieldInfo)x).FieldType == tipo)
.First().GetCustomAttribute<DisplayAttribute>()?.Name ?? textoDirecto;
if (textoDirecto.Contains(", ")) {
var texto = new StringBuilder();
foreach (var textoDirectoAux in textoDirecto.Split(", ")) {
texto.Append($"{obtenerTexto(textoDirectoAux)}, ");
}
return texto.ToString()[0..^2];
} else {
return obtenerTexto(textoDirecto);
}
}
The enum with flags:
[Flags] public enum TipoContribuyente {
[Display(Name = "Com煤n")] Com煤n = 1,
[Display(Name = "Gran Contribuyente")] GranContribuyente = 2,
Autorretenedor = 4,
[Display(Name = "Retenedor de IVA")] RetenedorIVA = 8,
[Display(Name = "R茅gimen Simple")] R茅gimenSimple = 16 }
How to use it:
var tipoContribuyente = TipoContribuyente.RetenedorIVA | TipoContribuyente.GranContribuyente;
var textoAux = tipoContribuyente.ATexto(); // Prints "Gran Contribuyente, Retenedor de IVA".
I want to contribute with culture-dependent GetDisplayName enum extension. Hope this will be usefull for anyone googling this answer like me previously:
"standart" way as Aydin Adn and Todd mentioned:
public static string GetDisplayName(this Enum enumValue)
{
return enumValue
.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>()
.GetName();
}
"Culture-dependent" way:
public static string GetDisplayName(this Enum enumValue, CultureInfo ci)
{
var displayAttr = enumValue
.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>();
var resMan = displayAttr.ResourceType?.GetProperty(#"ResourceManager", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(null, null) as ResourceManager;
return resMan?.GetString(displayAttr.Name, ci) ?? displayAttr.GetName();
}
It is maybe cheating, but it's works:
#foreach (var yourEnum in Html.GetEnumSelectList<YourEnum>())
{
#yourEnum.Text
}
Based on previous answers I've created this comfortable helper to support all DisplayAttribute properties in a readable way:
public static class EnumExtensions
{
public static DisplayAttributeValues GetDisplayAttributeValues(this Enum enumValue)
{
var displayAttribute = enumValue.GetType().GetMember(enumValue.ToString()).First().GetCustomAttribute<DisplayAttribute>();
return new DisplayAttributeValues(enumValue, displayAttribute);
}
public sealed class DisplayAttributeValues
{
private readonly Enum enumValue;
private readonly DisplayAttribute displayAttribute;
public DisplayAttributeValues(Enum enumValue, DisplayAttribute displayAttribute)
{
this.enumValue = enumValue;
this.displayAttribute = displayAttribute;
}
public bool? AutoGenerateField => this.displayAttribute?.GetAutoGenerateField();
public bool? AutoGenerateFilter => this.displayAttribute?.GetAutoGenerateFilter();
public int? Order => this.displayAttribute?.GetOrder();
public string Description => this.displayAttribute != null ? this.displayAttribute.GetDescription() : string.Empty;
public string GroupName => this.displayAttribute != null ? this.displayAttribute.GetGroupName() : string.Empty;
public string Name => this.displayAttribute != null ? this.displayAttribute.GetName() : this.enumValue.ToString();
public string Prompt => this.displayAttribute != null ? this.displayAttribute.GetPrompt() : string.Empty;
public string ShortName => this.displayAttribute != null ? this.displayAttribute.GetShortName() : this.enumValue.ToString();
}
}
I tried doing this as an edit but it was rejected; I can't see why.
The above will throw an exception if you call it with an Enum that has a mix of custom attributes and plain items, e.g.
public enum CommentType
{
All = 1,
Rent = 2,
Insurance = 3,
[Display(Name="Service Charge")]
ServiceCharge = 4
}
So I've modified the code ever so slightly to check for custom attributes before trying to access them, and use the name if none are found.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
public static class EnumHelper<T>
{
public static IList<T> GetValues(Enum value)
{
var enumValues = new List<T>();
foreach (FieldInfo fi in value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public))
{
enumValues.Add((T)Enum.Parse(value.GetType(), fi.Name, false));
}
return enumValues;
}
public static T Parse(string value)
{
return (T)Enum.Parse(typeof(T), value, true);
}
public static IList<string> GetNames(Enum value)
{
return value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public).Select(fi => fi.Name).ToList();
}
public static IList<string> GetDisplayValues(Enum value)
{
return GetNames(value).Select(obj => GetDisplayValue(Parse(obj))).ToList();
}
private static string lookupResource(Type resourceManagerProvider, string resourceKey)
{
foreach (PropertyInfo staticProperty in resourceManagerProvider.GetProperties(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
{
if (staticProperty.PropertyType == typeof(System.Resources.ResourceManager))
{
System.Resources.ResourceManager resourceManager = (System.Resources.ResourceManager)staticProperty.GetValue(null, null);
return resourceManager.GetString(resourceKey);
}
}
return resourceKey; // Fallback with the key name
}
public static string GetDisplayValue(T value)
{
var fieldInfo = value.GetType().GetField(value.ToString());
var descriptionAttributes = fieldInfo.GetCustomAttributes(
typeof(DisplayAttribute), false) as DisplayAttribute[];
if (descriptionAttributes.Any() && descriptionAttributes[0].ResourceType != null)
return lookupResource(descriptionAttributes[0].ResourceType, descriptionAttributes[0].Name);
if (descriptionAttributes == null) return string.Empty;
return (descriptionAttributes.Length > 0) ? descriptionAttributes[0].Name : value.ToString();
}
}
Using MVC5 you could use:
public enum UserPromotion
{
None = 0x0,
[Display(Name = "Send Job Offers By Mail")]
SendJobOffersByMail = 0x1,
[Display(Name = "Send Job Offers By Sms")]
SendJobOffersBySms = 0x2,
[Display(Name = "Send Other Stuff By Sms")]
SendPromotionalBySms = 0x4,
[Display(Name = "Send Other Stuff By Mail")]
SendPromotionalByMail = 0x8
}
then if you want to create a dropdown selector you can use:
#Html.EnumDropdownListFor(expression: model => model.PromotionSelector, optionLabel: "Select")
assume that your enum name is OrderState, Use this code:
#Html.DropDownList("selectList", new SelectList(Html.GetEnumSelectList<OrderState>(), "Value", "Text",ViewBag.selectedOrderState), new {#id="OrderState", #class = "form-control" })
and set selected option in backend:
var selectedOrderState = ..Data.OrderState.GetHashCode();
ViewBag.selectedOrderState = selectedOrderState;
I'm trying to use the Html.DropDownList extension method but can't figure out how to use it with an enumeration.
Let's say I have an enumeration like this:
public enum ItemTypes
{
Movie = 1,
Game = 2,
Book = 3
}
How do I go about creating a dropdown with these values using the Html.DropDownList extension method?
Or is my best bet to simply create a for loop and create the Html elements manually?
For MVC v5.1 use Html.EnumDropDownListFor
#Html.EnumDropDownListFor(
x => x.YourEnumField,
"Select My Type",
new { #class = "form-control" })
For MVC v5 use EnumHelper
#Html.DropDownList("MyType",
EnumHelper.GetSelectList(typeof(MyType)) ,
"Select My Type",
new { #class = "form-control" })
For MVC 5 and lower
I rolled Rune's answer into an extension method:
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);
}
}
}
This allows you to write:
ViewData["taskStatus"] = task.Status.ToSelectList();
by using MyApp.Common
I know I'm late to the party on this, but thought you might find this variant useful, as this one also allows you to use descriptive strings rather than enumeration constants in the drop down. To do this, decorate each enumeration entry with a [System.ComponentModel.Description] attribute.
For example:
public enum TestEnum
{
[Description("Full test")]
FullTest,
[Description("Incomplete or partial test")]
PartialTest,
[Description("No test performed")]
None
}
Here is my code:
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);
}
You can then do this in your view:
#Html.EnumDropDownListFor(model => model.MyEnumProperty)
**EDIT 2014-JAN-23: Microsoft have just released MVC 5.1, which now has an EnumDropDownListFor feature. Sadly it does not appear to respect the [Description] attribute so the code above still stands.See Enum section in Microsoft's release notes for MVC 5.1.
Update: It does support the Display attribute [Display(Name = "Sample")] though, so one can use that.
[Update - just noticed this, and the code looks like an extended version of the code here: https://blogs.msdn.microsoft.com/stuartleeks/2010/05/21/asp-net-mvc-creating-a-dropdownlist-helper-for-enums/, with a couple of additions. If so, attribution would seem fair ;-)]
In ASP.NET MVC 5.1, they added the EnumDropDownListFor() helper, so no need for custom extensions:
Model:
public enum MyEnum
{
[Display(Name = "First Value - desc..")]
FirstValue,
[Display(Name = "Second Value - desc...")]
SecondValue
}
View:
#Html.EnumDropDownListFor(model => model.MyEnum)
Using Tag Helper (ASP.NET MVC 6):
<select asp-for="#Model.SelectedValue" asp-items="Html.GetEnumSelectList<MyEnum>()">
I bumped into the same problem, found this question, and thought that the solution provided by Ash wasn't what I was looking for; Having to create the HTML myself means less flexibility compared to the built-in Html.DropDownList() function.
Turns out C#3 etc. makes this pretty easy. I have an enum called 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);
This creates a good ol' SelectList that can be used like you're used to in the view:
<td><b>Status:</b></td><td><%=Html.DropDownList("taskStatus")%></td></tr>
The anonymous type and LINQ makes this so much more elegant IMHO. No offence intended, Ash. :)
Here is a better encapsulated solution:
https://www.spicelogic.com/Blog/enum-dropdownlistfor-asp-net-mvc-5
Say here is your model:
Sample Usage:
Generated UI:
And generated HTML
The Helper Extension Source Code snap shot:
You can download the sample project from the link I provided.
EDIT: Here's the code:
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();
}
}
Html.DropDownListFor only requires an IEnumerable, so an alternative to Prise's solution is as follows. This will allow you to simply write:
#Html.DropDownListFor(m => m.SelectedItemType, Model.SelectedItemType.ToSelectList())
[Where SelectedItemType is a field on your model of type ItemTypes, and your model is non-null]
Also, you don't really need to genericize the extension method as you can use enumValue.GetType() rather than typeof(T).
EDIT: Integrated Simon's solution here as well, and included ToDescription extension method.
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();
}
}
So without Extension functions if you are looking for simple and easy.. This is what I did
<%= Html.DropDownListFor(x => x.CurrentAddress.State, new SelectList(Enum.GetValues(typeof(XXXXX.Sites.YYYY.Models.State))))%>
where XXXXX.Sites.YYYY.Models.State is an enum
Probably better to do helper function, but when time is short this will get the job done.
Expanding on Prise and Rune's answers, if you'd like to have the value attribute of your select list items map to the integer value of the Enumeration type, rather than the string value, use the following code:
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);
}
Instead of treating each Enumeration value as a TEnum object, we can treat it as a object and then cast it to integer to get the unboxed value.
Note:
I also added a generic type constraint to restrict the types for which this extension is available to only structs (Enum's base type), and a run-time type validation which ensures that the struct passed in is indeed an Enum.
Update 10/23/12:
Added generic type parameter for underlying type and fixed non-compilation issue affecting .NET 4+.
In .NET Core you can just use this:
#Html.DropDownListFor(x => x.Foo, Html.GetEnumSelectList<MyEnum>())
To solve the problem of getting the number instead of text using Prise's extension method.
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);
}
A super easy way to get this done - without all the extension stuff that seems overkill is this:
Your enum:
public enum SelectedLevel
{
Level1,
Level2,
Level3,
Level4
}
Inside of your controller bind the Enum to a List:
List<SelectedLevel> myLevels = Enum.GetValues(typeof(SelectedLevel)).Cast<SelectedLevel>().ToList();
After that throw it into a ViewBag:
ViewBag.RequiredLevel = new SelectList(myLevels);
Finally simply bind it to the View:
#Html.DropDownList("selectedLevel", (SelectList)ViewBag.RequiredLevel, new { #class = "form-control" })
This is by far the easiest way I found and does not require any extensions or anything that crazy.
UPDATE: See Andrews comment below.
The best solution I found for this was combining this blog with Simon Goldstone's answer.
This allows use of the enum in the model. Essentially the idea is to use an integer property as well as the enum, and emulate the integer property.
Then use the [System.ComponentModel.Description] attribute for annotating the model with your display text, and use an "EnumDropDownListFor" extension in your view.
This makes both the view and model very readable and maintainable.
Model:
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; }
}
View:
#using MyProject.Extensions
{
//...
#Html.EnumDropDownListFor(model => model.CuriousQuestion)
//...
}
Extension (directly from Simon Goldstone's answer, included here for completeness):
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);
}
}
}
You want to look at using something like Enum.GetValues
#Html.DropDownListFor(model => model.Type, Enum.GetNames(typeof(Rewards.Models.PropertyType)).Select(e => new SelectListItem { Text = e }))
Now this feature is supported out-of-the-box in MVC 5.1 through #Html.EnumDropDownListFor()
Check the following link:
https://learn.microsoft.com/en-us/aspnet/mvc/overview/releases/mvc51-release-notes#Enum
It is really shame that it took Microsoft 5 years to implement such as feature which is so in demand according to the voting above!
This is Rune & Prise answers altered to use the Enum int value as the ID.
Sample Enum:
public enum ItemTypes
{
Movie = 1,
Game = 2,
Book = 3
}
Extension method:
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()));
}
Sample of usage:
<%= Html.DropDownList("MyEnumList", ItemTypes.Game.ToSelectList()) %>
Remember to Import the namespace containing the Extension method
<%# Import Namespace="MyNamespace.LocationOfExtensionMethod" %>
Sample of generated HTML:
<select id="MyEnumList" name="MyEnumList">
<option value="1">Movie</option>
<option selected="selected" value="2">Game</option>
<option value="3">Book </option>
</select>
Note that the item that you use to call the ToSelectList on is the selected item.
This is version for 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)
Building on Simon's answer, a similar approach is to get the Enum values to display from a Resource file, instead of in a description attribute within the Enum itself. This is helpful if your site needs to be rendered in more than one language and if you were to have a specific resource file for Enums, you could go one step further and have just Enum values, in your Enum and reference them from the extension by a convention such as [EnumName]_[EnumValue] - ultimately less typing!
The extension then looks like:
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();
}
Resources in the Enums.Resx file looking like
ItemTypes_Movie : Film
One other thing I like to do is, instead of calling the extension method directly, I'd rather call it with a #Html.EditorFor(x => x.MyProperty), or ideally just have the whole form, in one neat #Html.EditorForModel(). To do this I change the string template to look like this
#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))
}
If this interests you, I've put a much more detailed answer here on my blog:
http://paulthecyclist.com/2013/05/24/enum-dropdown/
Well I'm really late to the party, but for what it is worth, I have blogged about this very subject whereby I create a EnumHelper class that enables very easy transformation.
http://jnye.co/Posts/4/creating-a-dropdown-list-from-an-enum-in-mvc-and-c%23
In your controller:
//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);
In your View:
#Html.DropDownList("DropDownList")
#* OR *#
#Html.DropDownListFor(m => m.Property, ViewBag.DropDownList as SelectList, null)
The helper class:
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() });
}
}
I am very late on this one but I just found a really cool way to do this with one line of code, if you are happy to add the Unconstrained Melody NuGet package (a nice, small library from Jon Skeet).
This solution is better because:
It ensures (with generic type constraints) that the value really is an enum value (due to Unconstrained Melody)
It avoids unnecessary boxing (due to Unconstrained Melody)
It caches all the descriptions to avoid using reflection on every call (due to Unconstrained Melody)
It is less code than the other solutions!
So, here are the steps to get this working:
In Package Manager Console, "Install-Package UnconstrainedMelody"
Add a property on your model like so:
//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() });
}
}
Now that you have the List of SelectListItem exposed on your model, you can use the #Html.DropDownList or #Html.DropDownListFor using this property as the source.
I found an answer here. However, some of my enums have [Description(...)] attribute, so I've modified the code to provide support for that:
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
);
}
Hope that helps.
Another fix to this extension method - the current version didn't select the enum's current value. I fixed the last line:
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());
}
If you want to add localization support just change the s.toString() method to something like this:
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()) };
In here the typeof(Resources) is the resource you want to load, and then you get the localized String, also useful if your enumerator has values with multiple words.
This is my version of helper method.
I use this:
var values = from int e in Enum.GetValues(typeof(TEnum))
select new { ID = e, Name = Enum.GetName(typeof(TEnum), e) };
Instead of that:
var values = from TEnum e in Enum.GetValues(typeof(TEnum))
select new { ID = (int)Enum.Parse(typeof(TEnum),e.ToString())
, Name = e.ToString() };
Here it is:
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);
}
You can also use my custom HtmlHelpers in Griffin.MvcContrib. The following code:
#Html2.CheckBoxesFor(model => model.InputType) <br />
#Html2.RadioButtonsFor(model => model.InputType) <br />
#Html2.DropdownFor(model => model.InputType) <br />
Generates:
https://github.com/jgauffin/griffin.mvccontrib
#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 ----"}
}
)
I would like to answer this question in a different way where, user need not to do anything in controller or Linq expression. This way...
I have a 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*/
}
Now I canto simply create a dropdown by using this enum.
#Html.DropDownList("accessLevel",new SelectList(AccessLevelEnum.GetValues(typeof(AccessLevelEnum))),new { #class = "form-control" })
OR
#Html.DropDownListFor(m=>m.accessLevel,new SelectList(AccessLevelEnum.GetValues(typeof(AccessLevelEnum))),new { #class = "form-control" })
If you want to make a index selected then try this
#Html.DropDownListFor(m=>m.accessLevel,new SelectList(AccessLevelEnum.GetValues(typeof(AccessLevelEnum)) , AccessLevelEnum.NoAccess ),new { #class = "form-control" })
Here I have used AccessLevelEnum.NoAccess as an extra parameter for default selecting the dropdown.
#Simon Goldstone: Thanks for your solution, it can be perfectly applied in my case. The only problem is I had to translate it to VB. But now it is done and to save other people's time (in case they need it) I put it here:
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
End You use it like this:
#Html.EnumDropDownListFor(Function(model) (model.EnumField))
I ended up creating extention methods to do what is essentially the accept answer here. The last half of the Gist deals with Enum specifically.
https://gist.github.com/3813767
#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" }
})
I've got a property in my model called Promotion that its type is a flag enum called UserPromotion. Members of my enum have display attributes set as follows:
[Flags]
public enum UserPromotion
{
None = 0x0,
[Display(Name = "Send Job Offers By Mail")]
SendJobOffersByMail = 0x1,
[Display(Name = "Send Job Offers By Sms")]
SendJobOffersBySms = 0x2,
[Display(Name = "Send Other Stuff By Sms")]
SendPromotionalBySms = 0x4,
[Display(Name = "Send Other Stuff By Mail")]
SendPromotionalByMail = 0x8
}
Now I want to be able to create say a ul in my view to show the selected values of my Promotion property. This is what I have done so far but the problem is that how can I get the display names here?
<ul>
#foreach (int aPromotion in #Enum.GetValues(typeof(UserPromotion)))
{
var currentPromotion = (int)Model.JobSeeker.Promotion;
if ((currentPromotion & aPromotion) == aPromotion)
{
<li>Here I don't know how to get the display attribute of "currentPromotion".</li>
}
}
</ul>
One liner - Fluent syntax
public static class Extensions
{
/// <summary>
/// A generic extension method that aids in reflecting
/// and retrieving any attribute that is applied to an `Enum`.
/// </summary>
public static TAttribute GetAttribute<TAttribute>(this Enum enumValue)
where TAttribute : Attribute
{
return enumValue.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<TAttribute>();
}
}
Example
public enum Season
{
[Display(Name = "It's autumn")]
Autumn,
[Display(Name = "It's winter")]
Winter,
[Display(Name = "It's spring")]
Spring,
[Display(Name = "It's summer")]
Summer
}
public class Foo
{
public Season Season = Season.Summer;
public void DisplayName()
{
var seasonDisplayName = Season.GetAttribute<DisplayAttribute>();
Console.WriteLine("Which season is it?");
Console.WriteLine (seasonDisplayName.Name);
}
}
Output
Which season is it?
It's summer
UPDATE
First solution was focused on getting display names from enum. Code below should be exact solution for your problem.
You can use this helper class for enums:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
public static class EnumHelper<T>
where T : struct, Enum // This constraint requires C# 7.3 or later.
{
public static IList<T> GetValues(Enum value)
{
var enumValues = new List<T>();
foreach (FieldInfo fi in value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public))
{
enumValues.Add((T)Enum.Parse(value.GetType(), fi.Name, false));
}
return enumValues;
}
public static T Parse(string value)
{
return (T)Enum.Parse(typeof(T), value, true);
}
public static IList<string> GetNames(Enum value)
{
return value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public).Select(fi => fi.Name).ToList();
}
public static IList<string> GetDisplayValues(Enum value)
{
return GetNames(value).Select(obj => GetDisplayValue(Parse(obj))).ToList();
}
private static string lookupResource(Type resourceManagerProvider, string resourceKey)
{
var resourceKeyProperty = resourceManagerProvider.GetProperty(resourceKey,
BindingFlags.Static | BindingFlags.Public, null, typeof(string),
new Type[0], null);
if (resourceKeyProperty != null)
{
return (string)resourceKeyProperty.GetMethod.Invoke(null, null);
}
return resourceKey; // Fallback with the key name
}
public static string GetDisplayValue(T value)
{
var fieldInfo = value.GetType().GetField(value.ToString());
var descriptionAttributes = fieldInfo.GetCustomAttributes(
typeof(DisplayAttribute), false) as DisplayAttribute[];
if (descriptionAttributes[0].ResourceType != null)
return lookupResource(descriptionAttributes[0].ResourceType, descriptionAttributes[0].Name);
if (descriptionAttributes == null) return string.Empty;
return (descriptionAttributes.Length > 0) ? descriptionAttributes[0].Name : value.ToString();
}
}
And then you can use it in your view as following:
<ul>
#foreach (var value in #EnumHelper<UserPromotion>.GetValues(UserPromotion.None))
{
if (value == Model.JobSeeker.Promotion)
{
var description = EnumHelper<UserPromotion>.GetDisplayValue(value);
<li>#Html.DisplayFor(e => description )</li>
}
}
</ul>
Building on Aydin's great answer, here's an extension method that doesn't require any type parameters.
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
public static class EnumExtensions
{
public static string GetDisplayName(this Enum enumValue)
{
return enumValue.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>()
.GetName();
}
}
NOTE: GetName() should be used instead of the Name property. This ensures that the localized string will be returned if using the ResourceType attribute property.
Example
To use it, just reference the enum value in your view.
#{
UserPromotion promo = UserPromotion.SendJobOffersByMail;
}
Promotion: #promo.GetDisplayName()
Output
Promotion: Send Job Offers By Mail
Based on Aydin's answer I would suggest a less "duplicatious" implementation (because we could easily get the Type from the Enum value itself, instead of providing it as a parameter 馃槈:
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
public static string GetDisplayName(this Enum enumValue)
{
return enumValue.GetType().GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>()
.Name;
}
EDIT (based upon #Vahagn Nahapetyan's comment)
public static string GetDisplayName(this Enum enumValue)
{
return enumValue.GetType()?
.GetMember(enumValue.ToString())?
.First()?
.GetCustomAttribute<DisplayAttribute>()?
.Name;
}
Now we can use it very clean in this way:
public enum Season
{
[Display(Name = "The Autumn")]
Autumn,
[Display(Name = "The Weather")]
Winter,
[Display(Name = "The Tease")]
Spring,
[Display(Name = "The Dream")]
Summer
}
Season.Summer.GetDisplayName();
Which results in
"The Dream"
If you are using MVC 5.1 or upper there is simplier and clearer way: just use data annotation (from System.ComponentModel.DataAnnotations namespace) like below:
public enum Color
{
[Display(Name = "Dark red")]
DarkRed,
[Display(Name = "Very dark red")]
VeryDarkRed,
[Display(Name = "Red or just black?")]
ReallyDarkRed
}
And in view, just put it into proper html helper:
#Html.EnumDropDownListFor(model => model.Color)
Building on Todd's great answer which built on Aydin's great answer, here's a generic extension method which doesn't require any type parameters.
/// <summary>
/// Gets human-readable version of enum.
/// </summary>
/// <returns>effective DisplayAttribute.Name of given enum.</returns>
public static string GetDisplayName<T>(this T enumValue) where T : IComparable, IFormattable, IConvertible // C# 7.3+: where T : struct, Enum
{
if (!typeof(T).IsEnum) // Not needed in C# 7.3+ with above updated constraint
throw new ArgumentException("Argument must be of type Enum");
DisplayAttribute displayAttribute = enumValue.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>();
string displayName = displayAttribute?.GetName();
return displayName ?? enumValue.ToString();
}
I needed this for my project because something like the below code, where not every member of the enum has a DisplayAttribute, throws an exception with Todd's solution:
public class MyClass
{
public enum MyEnum
{
[Display(Name="ONE")]
One,
// No DisplayAttribute
Two
}
public void UseMyEnum()
{
MyEnum foo = MyEnum.One;
MyEnum bar = MyEnum.Two;
Console.WriteLine(foo.GetDisplayName());
Console.WriteLine(bar.GetDisplayName());
}
}
// Output:
//
// ONE
// Two
If this is a complicated solution to a simple problem, please let me know, but this was the fix I used.
You could use Type.GetMember Method, then get the attribute info using reflection:
// display attribute of "currentPromotion"
var type = typeof(UserPromotion);
var memberInfo = type.GetMember(currentPromotion.ToString());
var attributes = memberInfo[0].GetCustomAttributes(typeof(DisplayAttribute), false);
var description = ((DisplayAttribute)attributes[0]).Name;
There were a few similar posts here:
Getting attributes of Enum's value
How to make MVC3 DisplayFor show the value of an Enum's Display-Attribute?
In .NET5, I used DisplayTextFor without needing helper or extension methods:
#Html.DisplayTextFor(m => m.SomeEnumProperty)
Where SomeEnumProperty has a value of:
public enum MyEnum
{
[Display(Name = "Not started")]
NotStarted = 0,
[Display(Name = "Weird display name instead of just 'Started'")]
Started = 1,
}
For ASP.Net Core 3.0, this worked for me (credit to previous answerers).
My Enum class:
using System;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
public class Enums
{
public enum Duration
{
[Display(Name = "1 Hour")]
OneHour,
[Display(Name = "1 Day")]
OneDay
}
// Helper method to display the name of the enum values.
public static string GetDisplayName(Enum value)
{
return value.GetType()?
.GetMember(value.ToString())?.First()?
.GetCustomAttribute<DisplayAttribute>()?
.Name;
}
}
My View Model Class:
public class MyViewModel
{
public Duration Duration { get; set; }
}
An example of a razor view displaying a label and a drop-down list. Notice the drop-down list does not require a helper method:
#model IEnumerable<MyViewModel>
#foreach (var item in Model)
{
<label asp-for="#item.Duration">#Enums.GetDisplayName(item.Duration)</label>
<div class="form-group">
<label asp-for="#item.Duration" class="control-label">Select Duration</label>
<select asp-for="#item.Duration" class="form-control"
asp-items="Html.GetEnumSelectList<Enums.Duration>()">
</select>
</div>
}
With Core 2.1,
public static string GetDisplayName(Enum enumValue)
{
return enumValue.GetType()?
.GetMember(enumValue.ToString())?[0]?
.GetCustomAttribute<DisplayAttribute>()?
.Name;
}
<ul>
#foreach (int aPromotion in #Enum.GetValues(typeof(UserPromotion)))
{
var currentPromotion = (int)Model.JobSeeker.Promotion;
if ((currentPromotion & aPromotion) == aPromotion)
{
<li>#Html.DisplayFor(e => currentPromotion)</li>
}
}
</ul>
combining all edge-cases together from above:
enum members with base object members' names (Equals, ToString)
optional Display attribute
here is my code:
public enum Enum
{
[Display(Name = "What a weird name!")]
ToString,
Equals
}
public static class EnumHelpers
{
public static string GetDisplayName(this Enum enumValue)
{
var enumType = enumValue.GetType();
return enumType
.GetMember(enumValue.ToString())
.Where(x => x.MemberType == MemberTypes.Field && ((FieldInfo)x).FieldType == enumType)
.First()
.GetCustomAttribute<DisplayAttribute>()?.Name ?? enumValue.ToString();
}
}
void Main()
{
Assert.Equals("What a weird name!", Enum.ToString.GetDisplayName());
Assert.Equals("Equals", Enum.Equals.GetDisplayName());
}
You need to use a bit of reflection in order to access that attribute:
var type = typeof(UserPromotion);
var member = type.GetMember(Model.JobSeeker.Promotion.ToString());
var attributes = member[0].GetCustomAttributes(typeof(DisplayAttribute), false);
var name = ((DisplayAttribute)attributes[0]).Name;
I recommend wrapping this method in a extension method or perform this in a view model.
I'm sorry to do this, but I couldn't use any of the other answers as-is and haven't time to duke it out in the comments.
Uses C# 6 syntax.
static class EnumExtensions
{
/// returns the localized Name, if a [Display(Name="Localised Name")] attribute is applied to the enum member
/// returns null if there isnt an attribute
public static string DisplayNameOrEnumName(this Enum value)
// => value.DisplayNameOrDefault() ?? value.ToString()
{
// More efficient form of ^ based on http://stackoverflow.com/a/17034624/11635
var enumType = value.GetType();
var enumMemberName = Enum.GetName(enumType, value);
return enumType
.GetEnumMemberAttribute<DisplayAttribute>(enumMemberName)
?.GetName() // Potentially localized
?? enumMemberName; // Or fall back to the enum name
}
/// returns the localized Name, if a [Display] attribute is applied to the enum member
/// returns null if there is no attribute
public static string DisplayNameOrDefault(this Enum value) =>
value.GetEnumMemberAttribute<DisplayAttribute>()?.GetName();
static TAttribute GetEnumMemberAttribute<TAttribute>(this Enum value) where TAttribute : Attribute =>
value.GetType().GetEnumMemberAttribute<TAttribute>(value.ToString());
static TAttribute GetEnumMemberAttribute<TAttribute>(this Type enumType, string enumMemberName) where TAttribute : Attribute =>
enumType.GetMember(enumMemberName).Single().GetCustomAttribute<TAttribute>();
}
Building further on Aydin's and Todd's answers, here is an extension method that also lets you get the name from a resource file
using AppResources;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Resources;
public static class EnumExtensions
{
public static string GetDisplayName(this Enum enumValue)
{
var enumMember= enumValue.GetType()
.GetMember(enumValue.ToString());
DisplayAttribute displayAttrib = null;
if (enumMember.Any()) {
displayAttrib = enumMember
.First()
.GetCustomAttribute<DisplayAttribute>();
}
string name = null;
Type resource = null;
if (displayAttrib != null)
{
name = displayAttrib.Name;
resource = displayAttrib.ResourceType;
}
return String.IsNullOrEmpty(name) ? enumValue.ToString()
: resource == null ? name
: new ResourceManager(resource).GetString(name);
}
}
and use it like
public enum Season
{
[Display(ResourceType = typeof(Resource), Name = Season_Summer")]
Summer
}
For just displaying enum's display name attribute just use
Microsoft.AspNetCore.Mvc.Rendering's
#Html.DisplayFor(x => EnumType.EnumValue)
That's would be enough.
For displaying SelectList write as following:
<select id="someIdForTheEndPoint" asp-items="Html.GetEnumSelectList<EnumType>()">
<option selected="selected" value="">Select value</option>
</select>
I have two solutions for this Question.
The first solution is on getting display names from enum.
public enum CourseLocationTypes
{
[Display(Name = "On Campus")]
OnCampus,
[Display(Name = "Online")]
Online,
[Display(Name = "Both")]
Both
}
public static string DisplayName(this Enum value)
{
Type enumType = value.GetType();
string enumValue = Enum.GetName(enumType, value);
MemberInfo member = enumType.GetMember(enumValue)[0];
object[] attrs = member.GetCustomAttributes(typeof(DisplayAttribute), false);
string outString = ((DisplayAttribute)attrs[0]).Name;
if (((DisplayAttribute)attrs[0]).ResourceType != null)
{
outString = ((DisplayAttribute)attrs[0]).GetName();
}
return outString;
}
<h3 class="product-title white">#Model.CourseLocationType.DisplayName()</h3>
The second Solution is on getting display name from enum name but that will be enum split in developer language it's called patch.
public static string SplitOnCapitals(this string text)
{
var r = new Regex(#"
(?<=[A-Z])(?=[A-Z][a-z]) |
(?<=[^A-Z])(?=[A-Z]) |
(?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace);
return r.Replace(text, " ");
}
<div class="widget-box pt-0">
#foreach (var item in Enum.GetNames(typeof(CourseLocationType)))
{
<label class="pr-2 pt-1">
#Html.RadioButtonFor(x => x.CourseLocationType, item, new { type = "radio", #class = "iCheckBox control-label" }) #item.SplitOnCapitals()
</label>
}
#Html.ValidationMessageFor(x => x.CourseLocationType)
</div>
2020 Update: An updated version of the function provided by many in this thread but now for C# 7.3 onwards:
Now you can restrict generic methods to enums types so you can write a single method extension to use it with all your enums like this:
The generic extension method:
public static string ATexto<T>(this T enumeraci贸n) where T : struct, Enum {
var tipo = enumeraci贸n.GetType();
return tipo.GetMember(enumeraci贸n.ToString())
.Where(x => x.MemberType == MemberTypes.Field && ((FieldInfo)x).FieldType == tipo).First()
.GetCustomAttribute<DisplayAttribute>()?.Name ?? enumeraci贸n.ToString();
}
The enum:
public enum TipoImpuesto {
IVA, INC, [Display(Name = "IVA e INC")]IVAeINC, [Display(Name = "No aplica")]NoAplica };
How to use it:
var tipoImpuesto = TipoImpuesto.IVAeINC;
var textoTipoImpuesto = tipoImpuesto.ATexto(); // Prints "IVA e INC".
Bonus, Enums with Flags: If you are dealing with normal enums the function above is enough, but if any of your enums can take multiple values with the use of flags then you will need to modify it like this (This code uses C#8 features):
public static string ATexto<T>(this T enumeraci贸n) where T : struct, Enum {
var tipo = enumeraci贸n.GetType();
var textoDirecto = enumeraci贸n.ToString();
string obtenerTexto(string textoDirecto) => tipo.GetMember(textoDirecto)
.Where(x => x.MemberType == MemberTypes.Field && ((FieldInfo)x).FieldType == tipo)
.First().GetCustomAttribute<DisplayAttribute>()?.Name ?? textoDirecto;
if (textoDirecto.Contains(", ")) {
var texto = new StringBuilder();
foreach (var textoDirectoAux in textoDirecto.Split(", ")) {
texto.Append($"{obtenerTexto(textoDirectoAux)}, ");
}
return texto.ToString()[0..^2];
} else {
return obtenerTexto(textoDirecto);
}
}
The enum with flags:
[Flags] public enum TipoContribuyente {
[Display(Name = "Com煤n")] Com煤n = 1,
[Display(Name = "Gran Contribuyente")] GranContribuyente = 2,
Autorretenedor = 4,
[Display(Name = "Retenedor de IVA")] RetenedorIVA = 8,
[Display(Name = "R茅gimen Simple")] R茅gimenSimple = 16 }
How to use it:
var tipoContribuyente = TipoContribuyente.RetenedorIVA | TipoContribuyente.GranContribuyente;
var textoAux = tipoContribuyente.ATexto(); // Prints "Gran Contribuyente, Retenedor de IVA".
I want to contribute with culture-dependent GetDisplayName enum extension. Hope this will be usefull for anyone googling this answer like me previously:
"standart" way as Aydin Adn and Todd mentioned:
public static string GetDisplayName(this Enum enumValue)
{
return enumValue
.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>()
.GetName();
}
"Culture-dependent" way:
public static string GetDisplayName(this Enum enumValue, CultureInfo ci)
{
var displayAttr = enumValue
.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>();
var resMan = displayAttr.ResourceType?.GetProperty(#"ResourceManager", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(null, null) as ResourceManager;
return resMan?.GetString(displayAttr.Name, ci) ?? displayAttr.GetName();
}
It is maybe cheating, but it's works:
#foreach (var yourEnum in Html.GetEnumSelectList<YourEnum>())
{
#yourEnum.Text
}
Based on previous answers I've created this comfortable helper to support all DisplayAttribute properties in a readable way:
public static class EnumExtensions
{
public static DisplayAttributeValues GetDisplayAttributeValues(this Enum enumValue)
{
var displayAttribute = enumValue.GetType().GetMember(enumValue.ToString()).First().GetCustomAttribute<DisplayAttribute>();
return new DisplayAttributeValues(enumValue, displayAttribute);
}
public sealed class DisplayAttributeValues
{
private readonly Enum enumValue;
private readonly DisplayAttribute displayAttribute;
public DisplayAttributeValues(Enum enumValue, DisplayAttribute displayAttribute)
{
this.enumValue = enumValue;
this.displayAttribute = displayAttribute;
}
public bool? AutoGenerateField => this.displayAttribute?.GetAutoGenerateField();
public bool? AutoGenerateFilter => this.displayAttribute?.GetAutoGenerateFilter();
public int? Order => this.displayAttribute?.GetOrder();
public string Description => this.displayAttribute != null ? this.displayAttribute.GetDescription() : string.Empty;
public string GroupName => this.displayAttribute != null ? this.displayAttribute.GetGroupName() : string.Empty;
public string Name => this.displayAttribute != null ? this.displayAttribute.GetName() : this.enumValue.ToString();
public string Prompt => this.displayAttribute != null ? this.displayAttribute.GetPrompt() : string.Empty;
public string ShortName => this.displayAttribute != null ? this.displayAttribute.GetShortName() : this.enumValue.ToString();
}
}
I tried doing this as an edit but it was rejected; I can't see why.
The above will throw an exception if you call it with an Enum that has a mix of custom attributes and plain items, e.g.
public enum CommentType
{
All = 1,
Rent = 2,
Insurance = 3,
[Display(Name="Service Charge")]
ServiceCharge = 4
}
So I've modified the code ever so slightly to check for custom attributes before trying to access them, and use the name if none are found.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
public static class EnumHelper<T>
{
public static IList<T> GetValues(Enum value)
{
var enumValues = new List<T>();
foreach (FieldInfo fi in value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public))
{
enumValues.Add((T)Enum.Parse(value.GetType(), fi.Name, false));
}
return enumValues;
}
public static T Parse(string value)
{
return (T)Enum.Parse(typeof(T), value, true);
}
public static IList<string> GetNames(Enum value)
{
return value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public).Select(fi => fi.Name).ToList();
}
public static IList<string> GetDisplayValues(Enum value)
{
return GetNames(value).Select(obj => GetDisplayValue(Parse(obj))).ToList();
}
private static string lookupResource(Type resourceManagerProvider, string resourceKey)
{
foreach (PropertyInfo staticProperty in resourceManagerProvider.GetProperties(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
{
if (staticProperty.PropertyType == typeof(System.Resources.ResourceManager))
{
System.Resources.ResourceManager resourceManager = (System.Resources.ResourceManager)staticProperty.GetValue(null, null);
return resourceManager.GetString(resourceKey);
}
}
return resourceKey; // Fallback with the key name
}
public static string GetDisplayValue(T value)
{
var fieldInfo = value.GetType().GetField(value.ToString());
var descriptionAttributes = fieldInfo.GetCustomAttributes(
typeof(DisplayAttribute), false) as DisplayAttribute[];
if (descriptionAttributes.Any() && descriptionAttributes[0].ResourceType != null)
return lookupResource(descriptionAttributes[0].ResourceType, descriptionAttributes[0].Name);
if (descriptionAttributes == null) return string.Empty;
return (descriptionAttributes.Length > 0) ? descriptionAttributes[0].Name : value.ToString();
}
}
Using MVC5 you could use:
public enum UserPromotion
{
None = 0x0,
[Display(Name = "Send Job Offers By Mail")]
SendJobOffersByMail = 0x1,
[Display(Name = "Send Job Offers By Sms")]
SendJobOffersBySms = 0x2,
[Display(Name = "Send Other Stuff By Sms")]
SendPromotionalBySms = 0x4,
[Display(Name = "Send Other Stuff By Mail")]
SendPromotionalByMail = 0x8
}
then if you want to create a dropdown selector you can use:
#Html.EnumDropdownListFor(expression: model => model.PromotionSelector, optionLabel: "Select")
assume that your enum name is OrderState, Use this code:
#Html.DropDownList("selectList", new SelectList(Html.GetEnumSelectList<OrderState>(), "Value", "Text",ViewBag.selectedOrderState), new {#id="OrderState", #class = "form-control" })
and set selected option in backend:
var selectedOrderState = ..Data.OrderState.GetHashCode();
ViewBag.selectedOrderState = selectedOrderState;
I need to create a SelectList from any Enum in my project.
I have the code below which I create a select list from a specific enum, but I'd like to make an extension method for ANY enum. This example retrieves the value of the DescriptionAttribute on each Enum value
var list = new SelectList(
Enum.GetValues(typeof(eChargeType))
.Cast<eChargeType>()
.Select(n => new
{
id = (int)n,
label = n.ToString()
}), "id", "label", charge.type_id);
Referencing this post, how do I proceed?
public static void ToSelectList(this Enum e)
{
// code here
}
What I think you are struggling with, is the retrieval of the description. I'm sure once you have those that you can define your final method which gives your exact result.
First, if you define an extension method, it works on a value of the enum, not on the enum type itself. And I think, for easy of usage, you would like to call the method on the type (like a static method). Unfortunately, you cannot define those.
What you can do is the following. First define a method which retrieves the description of the enum value, if it has one:
public static string GetDescription(this Enum value) {
string description = value.ToString();
FieldInfo fieldInfo = value.GetType().GetField(description);
DescriptionAttribute[] attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0) {
description = attributes[0].Description;
}
return description;
}
Next, define a method which takes all values of an enum, and use the previous method to look up the value which we want to show, and return that list. The generic argument can be inferred.
public static List<KeyValuePair<TEnum, string>> ToEnumDescriptionsList<TEnum>(this TEnum value) {
return Enum
.GetValues(typeof(TEnum))
.Cast<TEnum>()
.Select(x => new KeyValuePair<TEnum, string>(x, ((Enum)((object)x)).GetDescription()))
.ToList();
}
And finally, for ease of usage, a method to call it directly without value. But then the generic argument is not optional.
public static List<KeyValuePair<TEnum, string>> ToEnumDescriptionsList<TEnum>() {
return ToEnumDescriptionsList<TEnum>(default(TEnum));
}
Now we can use it like this:
enum TestEnum {
[Description("My first value")]
Value1,
Value2,
[Description("Last one")]
Value99
}
var items = default(TestEnum).ToEnumDescriptionsList();
// or: TestEnum.Value1.ToEnumDescriptionsList();
// Alternative: EnumExtensions.ToEnumDescriptionsList<TestEnum>()
foreach (var item in items) {
Console.WriteLine("{0} - {1}", item.Key, item.Value);
}
Console.ReadLine();
Which outputs:
Value1 - My first value
Value2 - Value2
Value99 - Last one
Late to the party, but since there is no accepted answer and it might help others:
As #Maarten mentioned, an extension method works on the value of an enum, not the enum type itelf, so as with Maarteen's soultion you can create a dummy or default value to call the extension method on, however, you may find, as I did, that it is simpler to just use a static helper method like so:
public static class EnumHelper
{
public static SelectList GetSelectList<T>(string selectedValue, bool useNumeric = false)
{
Type enumType = GetBaseType(typeof(T));
if (enumType.IsEnum)
{
var list = new List<SelectListItem>();
// Add empty option
list.Add(new SelectListItem { Value = string.Empty, Text = string.Empty });
foreach (Enum e in Enum.GetValues(enumType))
{
list.Add(new SelectListItem { Value = useNumeric ? Convert.ToInt32(e).ToString() : e.ToString(), Text = e.Description() });
}
return new SelectList(list, "Value", "Text", selectedValue);
}
return null;
}
private static bool IsTypeNullable(Type type)
{
return (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
}
private static Type GetBaseType(Type type)
{
return IsTypeNullable(type) ? type.GetGenericArguments()[0] : type;
}
You would create the select list like this:
viewModel.ProvinceSelect = EnumHelper.GetSelectList<Province>(model.Province);
or using the optional numeric values instead of strings:
viewModel.MonthSelect = EnumHelper.GetSelectList<Month>(model.Month,true);
The basic idea for this I got from here, though I changed it to suit my needs. One thing I added was the ability to optionally use ints for the value. I also added an enum extension to get the description attribute which is based on this blog post:
public static class EnumExtensions
{
public static string Description(this Enum en)
{
Type type = en.GetType();
MemberInfo[] memInfo = type.GetMember(en.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
{
return ((DescriptionAttribute)attrs[0]).Description;
}
}
return en.ToString();
}
}
Since enum can't have extensions pinned to the entire collection a convenient way to extend the Enum base class is with a static typed class. This will allow concise code such as:
Enum<MyCustomEnumType>.GetSelectItems();
Which can be achieved with the following code:
public static class Enum<T>
{
public static SelectListItem[] GetSelectItems()
{
Type type = typeof(T);
return
Enum.GetValues(type)
.Cast<object>()
.Select(v => new SelectListItem() { Value = v.ToString(), Text = Enum.GetName(type, v) })
.ToArray();
}
}
Since enum do not have a shared interface Type misuse is possible, but the class name Enum should dispell any confusion.
Here is a corrected [type casted value to int] and simplified [uses tostring override instead of getname] version of Nathaniels answer that returns a List instead of an array:
public static class Enum<T>
{
//usage: var lst = Enum<myenum>.GetSelectList();
public static List<SelectListItem> GetSelectList()
{
return Enum.GetValues( typeof(T) )
.Cast<object>()
.Select(i => new SelectListItem()
{ Value = ((int)i).ToString()
,Text = i.ToString() })
.ToList();
}
}
My model contains an enum with a flags attribute
[Flags()]
public enum InvestmentAmount
{
[Description("拢500 - 拢5,000")]
ZeroToFiveThousand,
[Description("拢5,000 - 拢10,000")]
FiveThousandToTenThousand,
//Deleted remaining entries for size
}
I want to be able to display this in my view as a multiselectable List box.
Obviously the current helper for Listfor() doesn't support enums.
I've tried rolling my own but just receive
The parameter 'expression' must evaluate to an IEnumerable when
multiple selection is allowed.
when it executes.
public static MvcHtmlString EnumListFor<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);
RouteValueDictionary htmlattr = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
//htmlattr.Add("multiple", "multiple");
if (expression.GetDescription() != null)
{
htmlattr.Add("data-content", expression.GetDescription());
htmlattr.Add("data-original-title", expression.GetTitle());
htmlattr["class"] = "guidance " + htmlattr["class"];
}
var fieldName = htmlHelper.NameFor(expression).ToString();
return htmlHelper.ListBox(fieldName, items, htmlattr); //Exception thrown here
}
Check out my blog post on a helper method I created to do just this:
http://jnye.co/Posts/4/creating-a-dropdown-list-from-an-enum-in-mvc-and-c%23
This enables you to do something like this:
//If you don't have an enum value use the type
var enumList = EnumHelper.SelectListFor<MyEnum>();
//If you do have an enum value use the value (the value will be marked as selected)
var enumList = EnumHelper.SelectListFor(myEnumValue);
...which you can then use to build your multi list.
The helper class is as follows:
public static class EnumHelper
{
//Creates a SelectList for a nullable enum value
public static SelectList SelectListFor<T>(T? selected)
where T : struct
{
return selected == null ? SelectListFor<T>()
: SelectListFor(selected.Value);
}
//Creates a SelectList for an enum type
public static SelectList SelectListFor<T>() where T : struct
{
Type t = typeof (T);
if (t.IsEnum)
{
var values = Enum.GetValues(typeof(T)).Cast<enum>()
.Select(e => new { Id = Convert.ToInt32(e), Name = e.GetDescription() });
return new SelectList(values, "Id", "Name");
}
return null;
}
//Creates a SelectList for an enum value
public static SelectList SelectListFor<T>(T selected) where T : struct
{
Type t = typeof(T);
if (t.IsEnum)
{
var values = Enum.GetValues(t).Cast<Enum>()
.Select(e => new { Id = Convert.ToInt32(e), Name = e.GetDescription() });
return new SelectList(values, "Id", "Name", Convert.ToInt32(selected));
}
return null;
}
// Get the value of the description attribute if the
// enum has one, otherwise use the value.
public static string GetDescription<TEnum>(this TEnum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
if (fi != null)
{
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute),
false);
if (attributes.Length > 0)
{
return attributes[0].Description;
}
}
return value.ToString();
}
}
The error appears to have been occurring because I was binding to just an "InvestmentAmount" where as it appears that ListFor checks if the model is a list.
I've had to change my model to a List and build in binding logic (Through AutoMapper) to convert from the flagged Enum to a List and back.
A nicer solution would be to create a generic HTML ListFor helper to do it. Which is the way I'll tackle it if I need it any more than once.