views:

392

answers:

2

Hi, The class that's returned to the view has several enum values from other tables:

class Person{
int id;
enum Rank;
enum Status;
}

In the 'edit' View I would like to have a select box with the selected value so the user is able to change to another enum value.

I get the enum values into a ViewData:

<%= Html.DropDownList("Rank")%>

how can you select the value from the Model? Like so:

<%= Html.DropDownList("Rank", item.Rank)%>

And once changed how to save the changes?

A: 

I think you're looking for Enum.Parse:

Rank rankValue = (Rank) Enum.Parse(typeof(Rank), rankString);       
Dave Swersky
+2  A: 

I found myself wanting to do dropdown lists for enums frequently enough that I created some convenient extension methods. They allow me to create dropdowns in the following ways:

This uses the FooFor HtmlHelper extension method pattern to do strongly-typed dropdown creation:

<%= Html.EnumDropdownFor(model => model.MyProperty) %>

Here are some less-strongly-typed versions:

<%= Html.EnumDropdown("MyProperty", MyEnum.AValue /* selected value */ ) %>

<%= Html.EnumDropdown(
        "MyProperty", 
        new MyEnum[] { MyEnum.AValue, MyEnum.BValue } /* choices */ ) %>

<%= Html.EnumDropdown(
        "MyProperty",
        MyEnum.AValue                                 /* selected value */,
        new MyEnum[] { MyEnum.AValue, MyEnum.BValue } /* choices        */ ) %>

In my implementation, I also created a custom attribute called EnumDisplayNameAttribute to assign pretty names to enum values if the default ToString result is not acceptable. Here's the extension methods and helper classes I wrote to support all of this:

public class EnumDisplayNameAttribute : Attribute
{
    public EnumDisplayNameAttribute(string name)
    {
        this.DisplayName = name;
    }

    public string DisplayName { get; private set; }
}

public static class EnumUtils
{
    private static Dictionary<Type, IDictionary<object, string>> _nameLookups = 
        new Dictionary<Type, IDictionary<object, string>>();

    public static string GetDisplayName<T>(this T value)
    {
        var type = typeof(T);
        if (!_nameLookups.ContainsKey(type))
        {
            _nameLookups[typeof(T)] = GetEnumFields<T>()
                .ToDictionary(
                    f => f.GetValue(null),
                    f => f.GetCustomAttributes(typeof(EnumDisplayNameAttribute), true)
                            .OfType<EnumDisplayNameAttribute>()
                            .Select(a => a.DisplayName)
                            .FirstOrDefault() 
                        ?? f.Name);
        }
        return _nameLookups[type][value];
    }

    public static IEnumerable<FieldInfo> GetEnumFields<T>()
    {
        return typeof(T)
            .GetFields(BindingFlags.Public | BindingFlags.Static)
            .OfType<FieldInfo>();
    }

    public static IEnumerable<T> GetEnumValues<T>()
    {
        return Enum.GetValues(typeof(T)).Cast<T>();
    }
}

public static class HtmlHelperExtensions
{
    public static MvcHtmlString EnumDropdownFor<TModel, TValue>(
        this HtmlHelper<TModel> helper, 
        Expression<Func<TModel, TValue>> expression)
    {
        var data = expression.Compile()(helper.ViewData.Model);
        StringBuilder builder = new StringBuilder();
        builder.AppendFormat(
            "<select name='{0}' id='{0}'>", 
            helper.Encode(
                (expression.Body as MemberExpression).Member.Name));
        EnumUtils.GetEnumFields<TValue>()
            .ForEach(f => {
                var nameAttrib = f
                    .GetCustomAttributes(
                        typeof(EnumDisplayNameAttribute), true)
                    .OfType<EnumDisplayNameAttribute>().FirstOrDefault();
                var displayName = (nameAttrib == null)
                    ? f.Name : nameAttrib.DisplayName;

                var optionData = (TValue)f.GetRawConstantValue();
                builder.AppendFormat(
                    "<option value=\"{0}\" {1}>{2}</option>",
                    optionData, 
                    optionData.Equals(data) ? "selected=\"selected\"" : "",
                    displayName);
            }
        );
        builder.Append("</select>");
        return MvcHtmlString.Create(builder.ToString());
    }

    public static MvcHtmlString EnumDropdown<TModel, TValue>(
        this HtmlHelper<TModel> helper, string name, TValue value)
    {
        return helper.EnumDropdown(
            name, value, EnumUtils.GetEnumValues<TValue>());
    }

    public static MvcHtmlString EnumDropdown<TModel, TValue>(
        this HtmlHelper<TModel> helper, string name, 
        IEnumerable<TValue> choices)
    {
        return helper.EnumDropdown(name, choices.FirstOrDefault(), choices);
    }

    public static MvcHtmlString EnumDropdown<TModel, TValue>(
        this HtmlHelper<TModel> helper, string name, TValue value,
        IEnumerable<TValue> choices)
    {
        StringBuilder builder = new StringBuilder();
        builder.AppendFormat("<select name='{0}'>", helper.Encode(name));
        if (choices != null)
        {
            choices.ForEach(
                c => builder.AppendFormat(
                    "<option value=\"{0}\"{2}>{1}</option>",
                    Convert.ToInt32(c),
                    helper.Encode(EnumUtils.GetDisplayName(c)),
                    value.Equals(c) ? " selected='selected'" : ""));
        }
        builder.Append("</select>");
        return MvcHtmlString.Create(builder.ToString());
    }
}

Edit:

I forgot to include the extension method that adds ForEach to IEnumerable:

public static class CollectionUtils
{
    public static void ForEach<T>(
        this IEnumerable<T> collection, Action<T> action)
    {
        foreach (var item in collection)
        {
            action(item);
        }
    }
}
Jacob
<%= Html.EnumDropdownFor(model => model.MyProperty) %> is great!thank you very much for that Jacob.I still need to complete my studying of certain areas to understand it.I wonder why they have not made all the 'Form'For elements. this is cool.
CohenA
Hi,I forgot to mention i'm using framework 4.0 that does'nt support the .Foreach operation on the generic types.How would you replace that?
CohenA
Yet another extension method. I'll add that to the bottom of my answer.
Jacob