In this post I'll go over how to build a MVC3 HtmlHelper extension to map Enum model properties to a dropdown listbox.
The extension will follow the same pattern as out-of-box generic helper functions like @Html.LabelFor<> and @Html.TextBoxFor<>.
Say we have a simple Employee model with Name (string) and Department (nullable enum) fields, and need to make a form for creating new employees:
We can create a label and textbox for the Name with this View cshtml:
Which renders into:
There is, however, no way to handle the Department field. Our goal is to build a custom HtmlHelper to map enum fields like Department to a drop down list. If the field is Nullable, the dropdown needs to include an empty option. The end product will work like this:
and render into:
The bulk of our work will be in building an appropriate SelectList from a given enum.
First, let's create the DropDownEnumListFor<> extension method. Add a static class to your MVC project. It doesn't matter where you put it or what it's called. Remember to import System.Web.Mvc, System.Web.Mvc.Html, and System.Linq.Expressions namespaces.
The expr parameter is a lamba function that takes as input the model and returns your property value. On the View page, this is the model => model.Dept part. selectedItem is the enum value that should be selected initially.
At this point, the new method should be available in Intellisense. Remember to add a using statement in the View cshtml for the namespace your extension lives under.
Now we have to implement ToSelectList<T>, which converts any given Enum type T to a SelectList.
IsTypeNullable simply checks if the specified type is Nullable. GetBaseType converts a Nullable type to it's non-nullable type (ie decimal? to decimal).
Code:
Done!
The extension will follow the same pattern as out-of-box generic helper functions like @Html.LabelFor<> and @Html.TextBoxFor<>.
Say we have a simple Employee model with Name (string) and Department (nullable enum) fields, and need to make a form for creating new employees:
public enum Department
{
public class Employee
{
{
Development,
Sales,
HR
}Sales,
HR
public class Employee
{
public string Name { get; set; }
public Department? Dept { get; set; }
}
public Department? Dept { get; set; }
We can create a label and textbox for the Name with this View cshtml:
@model Employee
<h2>New Employee</h2>
@Html.LabelFor(model => model.Name)
<br />
@Html.TextBoxFor(model => model.Name)
<h2>New Employee</h2>
@Html.LabelFor(model => model.Name)
<br />
@Html.TextBoxFor(model => model.Name)
Which renders into:
There is, however, no way to handle the Department field. Our goal is to build a custom HtmlHelper to map enum fields like Department to a drop down list. If the field is Nullable, the dropdown needs to include an empty option. The end product will work like this:
@Html.LabelFor(model => model.Dept)
<br />
@Html.DropDownEnumListFor(model => model.Dept, Department.Sales)
<br />
@Html.DropDownEnumListFor(model => model.Dept, Department.Sales)
and render into:
Create Extension Method
Luckily, we don't have to start from scratch. MVC3 already has a DropDownListFor<> helper, which takes as input a SelectList object containing objects to populate the dropdown.The bulk of our work will be in building an appropriate SelectList from a given enum.
First, let's create the DropDownEnumListFor<> extension method. Add a static class to your MVC project. It doesn't matter where you put it or what it's called. Remember to import System.Web.Mvc, System.Web.Mvc.Html, and System.Linq.Expressions namespaces.
public static class Extensions
{
{
public static MvcHtmlString DropDownEnumListFor<TModel, TValue>(
public static SelectList ToSelectList<T>(T selectedValue)
{
}
this HtmlHelper<TModel> helper,
Expression<Func<TModel, TValue>> expr,
TValue selectedItem)
{
Expression<Func<TModel, TValue>> expr,
TValue selectedItem)
return helper.DropDownListFor(expr, ToSelectList<TValue>(selectedItem));
}
public static SelectList ToSelectList<T>(T selectedValue)
{
throw new NotImplementedException();
}
The expr parameter is a lamba function that takes as input the model and returns your property value. On the View page, this is the model => model.Dept part. selectedItem is the enum value that should be selected initially.
At this point, the new method should be available in Intellisense. Remember to add a using statement in the View cshtml for the namespace your extension lives under.
Handle Nullable Enums
Let's start by creating two helper methods to deal with Nullable enum types.
public static bool IsTypeNullable(Type type)
{
public static Type GetBaseType(Type type)
{
{
return (type.IsGenericType
}
&& type.GetGenericTypeDefinition() == typeof(Nullable<>));
public static Type GetBaseType(Type type)
{
return IsTypeNullable(type) ? type.GetGenericArguments()[0] : type;
}
IsTypeNullable simply checks if the specified type is Nullable. GetBaseType converts a Nullable type to it's non-nullable type (ie decimal? to decimal).
Implement ToSelectList
Finally, implement ToSelectList<T>:- If T is Nullable, retrieve the Enum type with GetBaseType
- If T is Nullable, add an empty option
- Get list of enum values using Enum.GetValues
- Build an ArrayList pairing each enum value with its display name
- Create a SelectList from the ArrayList
Code:
public static SelectList ToSelectList<T>(T selectedValue)
{
{
bool isNullable = IsTypeNullable( typeof(T) );
Type enumType = GetBaseType( typeof(T) );
if (enumType.IsEnum)
{
return null;
}Type enumType = GetBaseType( typeof(T) );
if (enumType.IsEnum)
{
ArrayList items = new ArrayList();
// Add empty option
if (isNullable)
{
// Add enum values
var enumValues= Enum.GetValues(enumType);
foreach (T value in enumValues)
{
return new SelectList(items, "Value", "Name", selectedValue);
}// Add empty option
if (isNullable)
{
items.Add( new { Name = "", Value = default(T) } );
}// Add enum values
var enumValues= Enum.GetValues(enumType);
foreach (T value in enumValues)
{
string displayName = Enum.GetName(enumType, value);
items.Add( new { Name = displayName, Value = value } );
}items.Add( new { Name = displayName, Value = value } );
return new SelectList(items, "Value", "Name", selectedValue);
return null;
Done!