ASP.NET MVC drop down list for enums

From time to time I use enums in my domain models. Entity Framework has a nice integration and they’re a lot better than hard-coded strings or meaningless numbers. But when it comes to creating insert and update forms for the entity, it might be a hassle use enums in your view. In this post I will explain how to create a stronly typed HtmlHelper extension method to create a drop down list with enum values.

Let’s say we have an enum called EDeliveryTime:

public enum EDeliveryTime
{
    OneDay,
    TwoDays,
    ThreeDays,
    OneWeekOrMore
}

And a property called DeliveryTime in the (View)Model:

public EDeliveryTime DeliveryTime { get; set; }

Now let’s create an html helper called EnumDropDownListFor:

public static HtmlString EnumDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
    // Get the type of the property.
    var typeOfProperty = expression.ReturnType;
    if (!typeOfProperty.IsEnum)
    {
        throw new ArgumentException(string.Format("Type {0} is not an enum", typeOfProperty));
    }

    // Get all the enum values.
    var values = Enum.GetValues(typeOfProperty);
 
    // Create a dictionary of the enum values.
    var items = values.Cast<object>().ToDictionary(key => key, value => value.ToString()));

    // Get the metadata for the expression and ViewData of the current view.
    var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);

    // Create a select list from the dictionary and use the default DropDownListFor method to create a drop down list.
    return htmlHelper.DropDownListFor(expression, new SelectList(items, "Key", "Value", metadata.Model));
}

Notice how we retrieve the metadata for the expression. The Model property contains the value of the expression, in this case: the DeliveryTime enum.

Usage in views:

@Html.EnumDropDownListFor(item => item.DeliveryTime)

This is nice, but, the drop down list will show the ugly enum names like ‘OneDay’ and ‘ThreeDays’. We can solve this by using the DisplayAttribute:

public enum EDeliveryTime
{
    [Display(Name = "In 24 hours")]
    OneDay,

    [Display(Name = "In 2 days")]
    TwoDays,

    [Display(Name = "In 3 days")]
    ThreeDays,

    [Display(Name = "One week or more")]
    OneWeekOrMore
}

And create a method to get the display name of a an enum value.

private static string GetDisplayName(object value)
{
    var type = value.GetType();
    if (!type.IsEnum)
    {
        throw new ArgumentException(string.Format("Type {0} is not an enum", type));
    }

    // Get the enum field.
    var field = type.GetField(value.ToString());
    if (field == null)
    {
        return value.ToString();
    }

    // Gets the value of the Name property on the DisplayAttribute, this can be null.
    var attributes = field.GetCustomAttribute<DisplayAttribute>();
    return attributes != null ? attributes.Name : value.ToString()
}

We get the FieldInfo for the current enum value, then we get fetch the DisplayAttribute from it and return the Name property. All we have to do is use this method when the dictionary is created:

var items = values.Cast<object>().ToDictionary(key => key, value => GetDisplayName(value));

Now the drop down list should render the display name if specified. If it isn’t, it will convert the enum to a string as fall back.

Written on July 31, 2013