84

Trying to get my project updated to MVC3, something I just can't find:

I have a simple datatype of ENUMS:

public enum States()
{
  AL,AK,AZ,...WY
}

Which I want to use as a DropDown/SelectList in my view of a model that contains this datatype:

public class FormModel()
{
    public States State {get; set;}
}

Pretty straight forward: when I go to use the auto-generate view for this partial class, it ignores this type.

I need a simple select list that sets the value of the enum as the selected item when I hit submit and process via my AJAX - JSON POST Method.

And than the view (?!):

    <div class="editor-field">
        @Html.DropDownListFor(model => model.State, model => model.States)
    </div>
starball
  • 20,030
  • 7
  • 43
  • 238
jordan.baucke
  • 4,308
  • 10
  • 54
  • 77
  • 8
    For anyone who comes across this thread and is using MVC 5.1 or above, the helper method @Html.EnumDropDownListFor() is now built in to MVC - see http://www.asp.net/mvc/overview/releases/mvc51-release-notes – mecsco May 01 '14 at 09:28

13 Answers13

199

I found a way simpler solution for this here: http://coding-in.net/asp-net-mvc-3-method-extension/

using System;
using System.Linq.Expressions;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace EnumHtmlHelper.Helper
{    
    public static class EnumDropDownList
    {
        public static HtmlString EnumDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> modelExpression, string firstElement)
        {
            var typeOfProperty = modelExpression.ReturnType;
            if(!typeOfProperty.IsEnum)
                throw new ArgumentException(string.Format("Type {0} is not an enum", typeOfProperty));     
            var enumValues = new SelectList(Enum.GetValues(typeOfProperty));
            return htmlHelper.DropDownListFor(modelExpression, enumValues, firstElement);
}   }   }

One line in razor will do it:

@Html.DropDownListFor(model => model.State, new SelectList(Enum.GetValues(typeof(MyNamespace.Enums.States))))

You can also find code for doing it with an extension method in the linked article.

EricP
  • 3,395
  • 3
  • 33
  • 46
Mike McLaughlin
  • 1,991
  • 2
  • 11
  • 2
  • 6
    I think this one should have been marked as the solution. The best is not marked by complexity but by simplicity. – Lord of Scripts Sep 26 '12 at 23:22
  • 3
    For the people who look a DropDowList version (like me): @Html.DropDownList("listName", new SelectList(Enum.GetValues(typeof(MyNamespace.Enums.States)))) – dstr Oct 10 '12 at 18:22
  • One think this lacks, which is very useful, is that the more complex option includes `Descrpiption` attributes applied to the Enum items to give nicer (including spaced) text in the DDL. – Jon Egerton Dec 18 '12 at 15:24
  • 2
    @JonEgerton If you mean the same as me then I agree. If you want to display enums + description + an image you are lost with Mike McLaughlin`s solution. – Elisabeth Dec 28 '12 at 19:40
  • Is it possible to change the names of the enumeration values? Like if you wanted a 2 word dropdown name. How would you do this using this method? Thanks! – John Edwards May 29 '13 at 20:25
  • 1
    The only problem I've found with this solutions is that it doesn't map the selected value correctly when loading it. Aside from that, pretty good. – triangulito May 30 '13 at 01:34
  • 3
    @triangulito this is not a problem at all :) `@Html.DropDownListFor(model => model.State, new SelectList(Enum.GetValues(typeof(MyNamespace.Enums.States)),model.State))` – VladL Nov 26 '13 at 12:29
55

I've just made one for my own project. The code below is part of my helper class, I hope that I got all methods needed. Write a comment if it doesn't work, and I'll check again.

public static class SelectExtensions
{

    public static string GetInputName<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
    {
        if (expression.Body.NodeType == ExpressionType.Call)
        {
            MethodCallExpression methodCallExpression = (MethodCallExpression)expression.Body;
            string name = GetInputName(methodCallExpression);
            return name.Substring(expression.Parameters[0].Name.Length + 1);

        }
        return expression.Body.ToString().Substring(expression.Parameters[0].Name.Length + 1);
    }

    private static string GetInputName(MethodCallExpression expression)
    {
        // p => p.Foo.Bar().Baz.ToString() => p.Foo OR throw...
        MethodCallExpression methodCallExpression = expression.Object as MethodCallExpression;
        if (methodCallExpression != null)
        {
            return GetInputName(methodCallExpression);
        }
        return expression.Object.ToString();
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) where TModel : class
    {
        string inputName = GetInputName(expression);
        var value = htmlHelper.ViewData.Model == null
            ? default(TProperty)
            : expression.Compile()(htmlHelper.ViewData.Model);

        return htmlHelper.DropDownList(inputName, ToSelectList(typeof(TProperty), value.ToString()));
    }

    public static SelectList ToSelectList(Type enumType, string selectedItem)
    {
        List<SelectListItem> items = new List<SelectListItem>();
        foreach (var item in Enum.GetValues(enumType))
        {
            FieldInfo fi = enumType.GetField(item.ToString());
            var attribute = fi.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
            var title = attribute == null ? item.ToString() : ((DescriptionAttribute)attribute).Description;
            var listItem = new SelectListItem
                {
                    Value = ((int)item).ToString(),
                    Text = title,
                    Selected = selectedItem == ((int)item).ToString()
                };
            items.Add(listItem);
        }

        return new SelectList(items, "Value", "Text", selectedItem);
    }
}

Use it as:

Html.EnumDropDownListFor(m => m.YourEnum);

Update

I've created alternative Html Helpers. All you need to do to use them is to change your baseviewpage in views\web.config.

With them you can just do:

@Html2.DropDownFor(m => m.YourEnum);
@Html2.CheckboxesFor(m => m.YourEnum);
@Html2.RadioButtonsFor(m => m.YourEnum);

More info here: http://blog.gauffin.org/2011/10/first-draft-of-my-alternative-html-helpers/

Community
  • 1
  • 1
jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • Thanks! It strikes me this shouldn't be so complicated! Quick question, how should I reference this code in my 'ViewPage', @Html.EnumDropDownListFor (returns: Model doesn't contain a definition for ...") I tried placing this class in my "App_code" folder, which seems to be the same Namespace for @html in MVC3 -- Right? – jordan.baucke Jan 11 '11 at 18:45
  • I imported the namespace for "App_code" in my View's web.config - so the methods such as "EnumDropDownListFor" should be avalible under @Html.. correct? But illisense isn't coming up with this method under @Html for the model, @helper finds it but won't associate it properly with the model - and @using MYNAMSPACE doesn't really have an effect in either case. – jordan.baucke Jan 11 '11 at 20:12
  • Put it in another folder. I'm unsure of app_code. I usually create a folder called Helpers and import "@using AppName.Helpers" in my view. – jgauffin Jan 11 '11 at 21:40
  • 1
    Ok well it works either way, I'm just getting one compilation error: Line 41: return htmlHelper.DropDownList(inputName, ToSelectList(typeof(TProperty), value.ToString())); 'System.Web.Mvc.HtmlHelper' does not contain a definition for 'DropDownList' and no extension method 'DropDownList' accepting a first argument of type 'System.Web.Mvc.HtmlHelper' could be found (are you missing a using directive or an assembly reference?) – jordan.baucke Jan 12 '11 at 05:59
  • this doesn't return the correct selected item, don't know why, the first item in the enum is always selected, debugging doesn't show anything to be wrong – Para Jan 31 '11 at 13:38
  • Excellent! A couple of changes and validation is a snap (using OP's enum example): 1. make the first enum value equal to 0: `public enum States() { [Description("Select a State")]Unkown=0, AL, ... }` 2. In the `ToSelectList` method, change this line `Value = ( ( int )item ).ToString(),` to: `Value = ( int )item == 0 ? string.Empty : ( ( int )item ).ToString(),`. Finally, set your model's enum property to have a required attribute. Don't forget to set a Validation Message in your view, i.e. `@Html.ValidationMessageFor(m => m.YourEnum)` – Metro Smurf Apr 04 '11 at 19:07
  • 1
    @jordan I have the same error. Did you manage to fix the problem? – SF Developer Apr 06 '11 at 19:20
  • 9
    @filu @jordan add `using System.Web.Mvc.Html;` as you need to access the [`SelectExtensionsClass`](http://msdn.microsoft.com/en-us/library/system.web.mvc.html.selectextensions.aspx) – Simon Hartcher Apr 09 '11 at 09:21
  • @jgauffin use namespace System.Web.Mvc.Html for your helpers class and it should be accessible from the views anywhere without using any other namespaces. – JuanMa Apr 22 '11 at 17:34
  • It is better to add the additional namespace to the Views\web.config because its generally frowned upon to pollute other namespaces with your own code. It becomes more difficult to maintain the code later on. – Bryan Migliorisi Apr 22 '11 at 18:27
  • 3
    @Para I'm getting the same issue, the selected value does not appear selected in the View. (I had to change `((int)item).ToString()` for `Enum.GetName(enumType, item)` to get the `SelectListItem` correctly saved as selected, but it still does not work) – Fernando Neira Jun 21 '11 at 09:24
  • Fixed!! Doing the same change as I pointed in my previous comment to `Value = ((int)item).ToString()` did the trick. I guess the issue comes from the fact that, depending on how you define the Enum, the underlying representation for the Enum is the string instead of the int value, therefore when using value.ToString() you get the name of the Enum and not the number inside the string. – Fernando Neira Jun 21 '11 at 10:22
  • @Maxmalin I have that problem binding the Selected option, I just used `Value = title` and it worked, but I would prefer to have the option values rendered as the integers and not the same as the text. It seems the problem happens because the field in the model has the Enum type and the framework uses the enum name instead of the value to choose the selected option. – pauloya Jul 18 '11 at 10:02
  • I could make it work using `Value = ((int)item).ToString(),` and `if (selectedItem == item.ToString()) selectedValue = listItem.Value;` and `return new SelectList(items, "Value", "Text", selectedValue);`. But this is for calling `@Html.EnumDropDownListFor(model => model)` inside a template. – pauloya Jul 29 '11 at 10:25
  • When calling `@Html.EnumDropDownListFor(model => model.Title)` it only works by setting `Value = title`. And in this case I don't need to set the selectedValue `return new SelectList(items, "Value", "Text");`. It seems to be related with what types are received for **TModel** and **TProperty** on the **EnumDropDownListFor**. – pauloya Jul 29 '11 at 10:31
  • 1
    Just added an answer below that covers for the selection issue - stems from misunderstanding of the behaviours of the DropDownList overloads. – Jon Egerton Dec 18 '12 at 15:33
  • @jgauffin I wonder how do you apply CSS style to this. I mean: EnumDropDownListFor. – t_plusplus Mar 19 '14 at 15:06
24

As of ASP.NET MVC 5.1 (RC1), EnumDropDownListFor is included by default as an extension method of HtmlHelper.

Vincent Sels
  • 2,711
  • 1
  • 24
  • 31
17

If you want something really simple then there's another way, depending on how you store the state in the database.

If you had an entity like this:

public class Address
{
     //other address fields

     //this is what the state gets stored as in the db
     public byte StateCode { get; set; }

     //this maps our db field to an enum
     public States State
     {
         get
         {
             return (States)StateCode;
         }
         set
         {
             StateCode = (byte)value;
         }
     }
}

Then generating the dropdown would be as easy as this:

@Html.DropDownListFor(x => x.StateCode,
    from State state in Enum.GetValues(typeof(States))
    select new SelectListItem() { Text = state.ToString(), Value = ((int)state).ToString() }
);

Isn't LINQ pretty?

sjmeverett
  • 1,277
  • 1
  • 11
  • 23
  • where do you define the States enum- in the Model or the View? – superartsy Jun 18 '12 at 14:07
  • in the model, as it is used by the model class – sjmeverett Jul 07 '12 at 19:39
  • 1
    @stewartml When my ViewModel has the enum property + the "SelectedCodeProperty" then this is one property too much in your post. Why not have the enum in both as selected value post back to the server + as item value. – Elisabeth Dec 28 '12 at 19:49
13

I was able to do this in a one liner.

@Html.DropDownListFor(m=>m.YourModelProperty,new SelectList(Enum.GetValues(typeof(YourEnumType))))
JM1990
  • 143
  • 1
  • 7
8

Based on the accepted answer by @jgauffin, I've created my own version of EnumDropDownListFor, which deals with the problem of selecting items.

The problem is detailed in another SO answer here:, and is basically down to a misunderstanding of the behaviour of the different overloads of DropDownList.

My full code (which includes overloads for htmlAttributes etc is:

public static class EnumDropDownListForHelper
{

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression
        ) where TModel : class
    {
        return EnumDropDownListFor<TModel, TProperty>(
                            htmlHelper, expression, null, null);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression, 
            object htmlAttributes
        ) where TModel : class
    {
        return EnumDropDownListFor<TModel, TProperty>(
                            htmlHelper, expression, null, htmlAttributes);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression, 
            IDictionary<string, object> htmlAttributes
        ) where TModel : class
    {
        return EnumDropDownListFor<TModel, TProperty>(
                            htmlHelper, expression, null, htmlAttributes);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression, 
            string optionLabel
        ) where TModel : class
    {
        return EnumDropDownListFor<TModel, TProperty>(
                            htmlHelper, expression, optionLabel, null);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression, 
            string optionLabel, 
            IDictionary<string,object> htmlAttributes
        ) where TModel : class
    {
        string inputName = GetInputName(expression);
        return htmlHelper.DropDownList(
                            inputName, ToSelectList(typeof(TProperty)), 
                            optionLabel, htmlAttributes);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression, 
            string optionLabel, 
            object htmlAttributes
        ) where TModel : class
    {
        string inputName = GetInputName(expression);
        return htmlHelper.DropDownList(
                            inputName, ToSelectList(typeof(TProperty)), 
                            optionLabel, htmlAttributes);
    }


    private static string GetInputName<TModel, TProperty>(
            Expression<Func<TModel, TProperty>> expression)
    {
        if (expression.Body.NodeType == ExpressionType.Call)
        {
            MethodCallExpression methodCallExpression 
                            = (MethodCallExpression)expression.Body;
            string name = GetInputName(methodCallExpression);
            return name.Substring(expression.Parameters[0].Name.Length + 1);

        }
        return expression.Body.ToString()
                    .Substring(expression.Parameters[0].Name.Length + 1);
    }

    private static string GetInputName(MethodCallExpression expression)
    {
        // p => p.Foo.Bar().Baz.ToString() => p.Foo OR throw...
        MethodCallExpression methodCallExpression 
                            = expression.Object as MethodCallExpression;
        if (methodCallExpression != null)
        {
            return GetInputName(methodCallExpression);
        }
        return expression.Object.ToString();
    }


    private static SelectList ToSelectList(Type enumType)
    {
        List<SelectListItem> items = new List<SelectListItem>();
        foreach (var item in Enum.GetValues(enumType))
        {
            FieldInfo fi = enumType.GetField(item.ToString());
            var attribute = fi.GetCustomAttributes(
                                       typeof(DescriptionAttribute), true)
                                  .FirstOrDefault();
            var title = attribute == null ? item.ToString() 
                              : ((DescriptionAttribute)attribute).Description;
            var listItem = new SelectListItem
            {
                Value = item.ToString(),
                Text = title,
            };
            items.Add(listItem);
        }

        return new SelectList(items, "Value", "Text");
    }
}

I've written this up on my blog here.

Community
  • 1
  • 1
Jon Egerton
  • 40,401
  • 11
  • 97
  • 129
3
    public enum EnumStates
    {
        AL = 0,
        AK = 1,
        AZ = 2,
        WY = 3
    }


@Html.DropDownListFor(model => model.State, (from EnumStates e in Enum.GetValues(typeof(EnumStates))
                                                               select new SelectListItem { Value = ((int)e).ToString(), Text = e.ToString() }), "select", new { @style = "" })
                @Html.ValidationMessageFor(model => model.State)  //With select



//Or


@Html.DropDownListFor(model => model.State, (from EnumStates e in Enum.GetValues(typeof(EnumStates))
                                                               select new SelectListItem { Value = ((int)e).ToString(), Text = e.ToString() }), null, new { @style = "" })
                @Html.ValidationMessageFor(model => model.State)   //With out select
Thulasiram
  • 8,432
  • 8
  • 46
  • 54
3

This would be helpful for selecting an int value from enum: Here SpecType is an int field... and enmSpecType is an enum.

@Html.DropDownList(
    "SpecType", 
     YourNameSpace.SelectExtensions.ToSelectList(typeof(NREticaret.Core.Enums.enmSpecType), 
     Model.SpecType.ToString()), "Tip Seçiniz", new 
     { 
         gtbfieldid = "33", 
         @class = "small" 
     })
Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
user687314
  • 41
  • 3
3

I made the following change to the SelectList method to make it work a little better for me. Maybe it will be useful for others.

public static SelectList ToSelectList<T>(T selectedItem)
        {
            if (!typeof(T).IsEnum) throw new InvalidEnumArgumentException("The specified type is not an enum");

            var selectedItemName = Enum.GetName(typeof (T), selectedItem);
            var items = new List<SelectListItem>();
            foreach (var item in Enum.GetValues(typeof(T)))
            {
                var fi = typeof(T).GetField(item.ToString());
                var attribute = fi.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();

                var enumName = Enum.GetName(typeof (T), item);
                var title = attribute == null ? enumName : ((DescriptionAttribute)attribute).Description;

                var listItem = new SelectListItem
                {
                    Value = enumName,
                    Text = title,
                    Selected = selectedItemName == enumName
                };
                items.Add(listItem);
            }

            return new SelectList(items, "Value", "Text");
        }
Jason
  • 15,915
  • 3
  • 48
  • 72
2

Same as Mike's (which is buried between lengthy responses)

model.truckimagelocation is class instance property of the TruckImageLocation enumeration type

@Html.DropDownListFor(model=>model.truckimagelocation,Enum.GetNames(typeof(TruckImageLocation)).ToArray().Select(f=> new SelectListItem() {Text = f, Value = f, Selected = false}))
user794791
  • 31
  • 2
2

This is most generic code which will be used for all Enums.

public static class UtilitiesClass
{

    public static SelectList GetEnumType(Type enumType)
    {
        var value = from e in Enum.GetNames(enumType)
                    select new
                    {
                        ID = Convert.ToInt32(Enum.Parse(enumType, e, true)),
                        Name = e
                    };
        return new SelectList(value, "ID", "Name");
    }
}

Action Method

ViewBag.Enum= UtilitiesClass.GetEnumType(typeof (YourEnumType));

View.cshtml

 @Html.DropDownList("Type", (IEnumerable<SelectListItem>)ViewBag.Enum, new { @class = "form-control"})
Kamran Khan
  • 1,042
  • 10
  • 21
1

you can use enum in your model

your Enum

public enum States()
{
  AL,AK,AZ,...WY
}

make a model

public class enumclass
{
public States statesprop {get; set;}
}

in view

@Html.Dropdownlistfor(a=>a.statesprop)
MaTya
  • 782
  • 6
  • 11
1

The easiest answer in MVC5 is Define Enum:

public enum ReorderLevels {
          zero = 0,
            five = 5,
            ten = 10,
            fifteen = 15,
            twenty = 20,
            twenty_five = 25,
            thirty = 30
        }

Bind In View:

        <div class="form-group">
            <label>Reorder Level</label>
            @Html.EnumDropDownListFor(m => m.ReorderLevel, "Choose Me", new { @class = "form-control" })
        </div>