ASP.NET Html Helper for Enumeration Based Drop Downs
Not a particularly mind-blowing post today, but I found this little bit of code helpful and it might help someone else. So here you go. A simple html helper for ASP.NET MVC for creating drop downs based on Enumeration values. The nice bit is that it will respect Data Annotations Describe attributes.
The helper:
public static class HtmlEnumHelper
{
private static Type GetNonNullableModelType(ModelMetadata modelMetadata)
{
var realModelType = modelMetadata.ModelType;
var 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)
{
var fi = value.GetType().GetField(value.ToString());
var attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
return (attributes.Length > 0) ? attributes[0].Description : value.ToString();
}
public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TEnum>> expression,
object htmlAttributes = null)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var enumType = GetNonNullableModelType(metadata);
var values = Enum.GetValues(enumType).Cast<TEnum>();
var items = from value in values
select new SelectListItem
{
Text = GetEnumDescription(value),
Value = value.ToString(),
Selected = value.Equals(metadata.Model)
};
if (metadata.IsNullableValueType)
items = SingleEmptyItem.Concat(items);
return htmlHelper.DropDownListFor(expression, items, htmlAttributes);
}
}
And some tests that demonstrate it's usage:
[TestFixture]
public class HtmlEnumHelperTests
{
[Test]
public void CanGetDropDownForDecoratedEnum()
{
var result = CreateHtmlHelper<SimpleModel>()
.EnumDropDownListFor(x => x.DecoratedType)
.ToHtmlString();
Assert.That(result, Contains.Substring("foo"));
Assert.That(result, Contains.Substring("bar"));
Assert.That(result, Contains.Substring("baz"));
}
[Test]
public void CanGetDropDownForUndecoratedEnum()
{
var result = CreateHtmlHelper<SimpleModel>()
.EnumDropDownListFor(x => x.UndecoratedType)
.ToHtmlString();
Assert.That(result, Contains.Substring("Fiz"));
Assert.That(result, Contains.Substring("Buz"));
Assert.That(result, Contains.Substring("FizBuz"));
}
[Test]
public void CanGetDropDownForMixedEnum()
{
var result = CreateHtmlHelper<SimpleModel>()
.EnumDropDownListFor(x => x.MixedType)
.ToHtmlString();
Assert.That(result, Contains.Substring("foo"));
Assert.That(result, Contains.Substring("Buz"));
Assert.That(result, Contains.Substring("FizBuz"));
}
[Test]
public void CanGetDropDownForNullableEnum()
{
var result = CreateHtmlHelper<SimpleModel>()
.EnumDropDownListFor(x => x.NullableType)
.ToHtmlString();
Assert.That(result, Contains.Substring("<option value=\"\">"));
}
public static HtmlHelper<TModel> CreateHtmlHelper<TModel>()
{
var viewContext = new ViewContext
{
HttpContext = new FakeHttpContext(),
ViewData = new ViewDataDictionary()
};
return new HtmlHelper<TModel>(viewContext, new FakeViewDataContainer());
}
}
public class FakeViewDataContainer : IViewDataContainer
{
public FakeViewDataContainer()
{
ViewData = new ViewDataDictionary();
}
public ViewDataDictionary ViewData { get; set; }
}
public class FakeHttpContext : HttpContextBase
{
private readonly Dictionary<object, object> items = new Dictionary<object, object>();
public override IDictionary Items
{
get
{
return items;
}
}
}
public class SimpleModel
{
public DecoratedType DecoratedType { get; set; }
public UndecoratedType UndecoratedType { get; set; }
public MixedType MixedType { get; set; }
public UndecoratedType? NullableType { get; set; }
}
public enum DecoratedType
{
[Description("foo")]
Fiz,
[Description("bar")]
Buz,
[Description("baz")]
FizBuz
}
public enum UndecoratedType
{
Fiz,
Buz,
FizBuz
}
public enum MixedType
{
[Description("foo")]
Fiz,
Buz,
FizBuz
}
See, I can still write C#. 8)