I Am Not Myself

Bills.Pay(Developer.Skills).ShouldBeTrue()

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)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: