10

I have a public enum like so:

public enum occupancyTimeline
{
    TwelveMonths,
    FourteenMonths,
    SixteenMonths,
    EighteenMonths
}

which I will be using for a DropDown menu like so:

@Html.DropDownListFor(model => model.occupancyTimeline, 
   new SelectList(Enum.GetValues(typeof(CentralParkLCPreview.Models.occupancyTimeline))), "")

Now I am looking for away to have my values like so

12 Months, 14 Months, 16 Months, 18 Months instead of TweleveMonths, FourteenMonths, SixteenMonths, EighteenMonths

How would I accomplish this?

5
  • You mean displayed in your dropdown menu? Commented Sep 6, 2016 at 17:35
  • displayed in dropdown and as its values Commented Sep 6, 2016 at 17:42
  • Unfortunatly, there is no such thing as setting an enums .ToString() function, i believe. Commented Sep 6, 2016 at 17:46
  • 1
    stackoverflow.com/questions/13099834/… Commented Sep 6, 2016 at 20:53
  • Did you have a look at this SO question? Commented Sep 15, 2016 at 17:29

11 Answers 11

4

I've made myself an extension method, which I now use in every project:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web.Mvc;

namespace App.Extensions
{
    public static class EnumExtensions
    {
        public static SelectList ToSelectList(Type enumType)
        {
            return new SelectList(ToSelectListItems(enumType));
        }

        public static List<SelectListItem> ToSelectListItems(Type enumType, Func<object, bool> itemSelectedAction = null)
        {
            var arr = Enum.GetValues(enumType);
            return (from object item in arr
                    select new SelectListItem
                    {
                        Text = ((Enum)item).GetDescriptionEx(typeof(MyResources)),
                        Value = ((int)item).ToString(),
                        Selected = itemSelectedAction != null && itemSelectedAction(item)
                    }).ToList();
        }

        public static string GetDescriptionEx(this Enum @this)
        {
            return GetDescriptionEx(@this, null);
        }

        public static string GetDescriptionEx(this Enum @this, Type resObjectType)
        {
            // If no DescriptionAttribute is present, set string with following name
            // "Enum_<EnumType>_<EnumValue>" to be the default value.
            // Could also make some code to load value from resource.

            var defaultResult = $"Enum_{@this.GetType().Name}_{@this}";

            var fi = @this.GetType().GetField(@this.ToString());
            if (fi == null)
                return defaultResult;

            var customAttributes = fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
            if (customAttributes.Length <= 0 || customAttributes.IsNot<DescriptionAttribute[]>())
            {
                if (resObjectType == null)
                    return defaultResult;

                var res = GetFromResource(defaultResult, resObjectType);
                return res ?? defaultResult;
            }

            var attributes = (DescriptionAttribute[])customAttributes;
            var result = attributes.Length > 0 ? attributes[0].Description : defaultResult;
            return result ?? defaultResult;
        }

        public static string GetFromResource(string defaultResult, Type resObjectType)
        {
            var searchingPropName = defaultResult;
            var props = resObjectType.GetProperties();
            var prop = props.FirstOrDefault(t => t.Name.Equals(searchingPropName, StringComparison.InvariantCultureIgnoreCase));
            if (prop == null)
                return defaultResult;
            var res = prop.GetValue(resObjectType) as string;
            return res;
        }

        public static bool IsNot<T>(this object @this)
        {
            return !(@this is T);
        }
    }
}

And then use it like this (in a View.cshtml, for example) (code is broken in two lines for clarity; could also make oneliner):

// A SelectList without default value selected
var list1 = EnumExtensions.ToSelectListItems(typeof(occupancyTimeline));
@Html.DropDownListFor(model => model.occupancyTimeline, new SelectList(list1), "")

// A SelectList with default value selected if equals "DesiredValue"
// Selection is determined by lambda expression as a second parameter to
// ToSelectListItems method which returns bool.
var list2 = EnumExtensions.ToSelectListItems(typeof(occupancyTimeline), item => (occupancyTimeline)item == occupancyTimeline.DesiredValue));
@Html.DropDownListFor(model => model.occupancyTimeline, new SelectList(list2), "")

Update

Based on Phil's suggestion, I've updated above code with possibility to read enum's display value from some resource (if you have any). Name of item in a resource should be in a form of Enum_<EnumType>_<EnumValue> (e.g. Enum_occupancyTimeline_TwelveMonths). This way you can provide text for your enum values in resource file without decorating your enum values with some attributes. Type of resource (MyResource) is included directly inside ToSelectItems method. You could extract it as a parameter of an extension method.

The other way of naming enum values is applying Description attribute (this works without adapting the code to the changes I've made). For example:

public enum occupancyTimeline
{
    [Description("12 Months")]
    TwelveMonths,
    [Description("14 Months")]
    FourteenMonths,
    [Description("16 Months")]
    SixteenMonths,
    [Description("18 Months")]
    EighteenMonths
}
Sign up to request clarification or add additional context in comments.

4 Comments

To me, it seems that it might be a good answer but some crucial information is missing. How do you specify custom text associated to an enum value?
Updated my answer. Hope it helps.
I get an error with this var defaultResult = $"Enum_{@this.GetType().Name}_{@this}";
$"" is string interpolation first introduced in C# 6.0 (Visual studio 2015). If it's not working, you can use string.Format instead.
4
+50

You might check this link

his solution was targeting the Asp.NET , but by easy modification you can use it in MVC like

/// <span class="code-SummaryComment"><summary></span>
/// Provides a description for an enumerated type.
/// <span class="code-SummaryComment"></summary></span>
[AttributeUsage(AttributeTargets.Enum | AttributeTargets.Field, 
 AllowMultiple = false)]
public sealed class EnumDescriptionAttribute :  Attribute
{
   private string description;

   /// <span class="code-SummaryComment"><summary></span>
   /// Gets the description stored in this attribute.
   /// <span class="code-SummaryComment"></summary></span>
   /// <span class="code-SummaryComment"><value>The description stored in the attribute.</value></span>
   public string Description
   {
      get
      {
         return this.description;
      }
   }

   /// <span class="code-SummaryComment"><summary></span>
   /// Initializes a new instance of the
   /// <span class="code-SummaryComment"><see cref="EnumDescriptionAttribute"/> class.</span>
   /// <span class="code-SummaryComment"></summary></span>
   /// <span class="code-SummaryComment"><param name="description">The description to store in this attribute.</span>
   /// <span class="code-SummaryComment"></param></span>
   public EnumDescriptionAttribute(string description)
       : base()
   {
       this.description = description;
   }
}

the helper that will allow you to build a list of Key and Value

/// <span class="code-SummaryComment"><summary></span>
/// Provides a static utility object of methods and properties to interact
/// with enumerated types.
/// <span class="code-SummaryComment"></summary></span>
public static class EnumHelper
{
   /// <span class="code-SummaryComment"><summary></span>
   /// Gets the <span class="code-SummaryComment"><see cref="DescriptionAttribute" /> of an <see cref="Enum" /></span>
   /// type value.
   /// <span class="code-SummaryComment"></summary></span>
   /// <span class="code-SummaryComment"><param name="value">The <see cref="Enum" /> type value.</param></span>
   /// <span class="code-SummaryComment"><returns>A string containing the text of the</span>
   /// <span class="code-SummaryComment"><see cref="DescriptionAttribute"/>.</returns></span>
   public static string GetDescription(Enum value)
   {
      if (value == null)
      {
         throw new ArgumentNullException("value");
      }

      string description = value.ToString();
      FieldInfo fieldInfo = value.GetType().GetField(description);
      EnumDescriptionAttribute[] attributes =
         (EnumDescriptionAttribute[])
       fieldInfo.GetCustomAttributes(typeof(EnumDescriptionAttribute), false);

      if (attributes != null && attributes.Length > 0)
      {
         description = attributes[0].Description;
      }
      return description;
   }

   /// <span class="code-SummaryComment"><summary></span>
   /// Converts the <span class="code-SummaryComment"><see cref="Enum" /> type to an <see cref="IList" /> </span>
   /// compatible object.
   /// <span class="code-SummaryComment"></summary></span>
   /// <span class="code-SummaryComment"><param name="type">The <see cref="Enum"/> type.</param></span>
   /// <span class="code-SummaryComment"><returns>An <see cref="IList"/> containing the enumerated</span>
   /// type value and description.<span class="code-SummaryComment"></returns></span>
   public static IList ToList(Type type)
   {
      if (type == null)
      {
         throw new ArgumentNullException("type");
      }

      ArrayList list = new ArrayList();
      Array enumValues = Enum.GetValues(type);

      foreach (Enum value in enumValues)
      {
         list.Add(new KeyValuePair<Enum, string>(value, GetDescription(value)));
      }

      return list;
   }
}

then you decorate your enum as

public enum occupancyTimeline
{
    [EnumDescriptionAttribute ("12 months")]
    TwelveMonths,
    [EnumDescriptionAttribute ("14 months")]
    FourteenMonths,
    [EnumDescriptionAttribute ("16 months")]
    SixteenMonths,
    [EnumDescriptionAttribute ("18 months")]
    EighteenMonths
}

you can use it in the controller to fill the drop down list as

ViewBag.occupancyTimeline =new SelectList( EnumHelper.ToList(typeof(occupancyTimeline)),"Value","Key");

and in your view, you can use the following

@Html.DropdownList("occupancyTimeline")

hope it will help you

Comments

1

Make extension Description for enumeration

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.ComponentModel;
public static class EnumerationExtensions
{
//This procedure gets the <Description> attribute of an enum constant, if any.
//Otherwise, it gets the string name of then enum member.
[Extension()]
public static string Description(Enum EnumConstant)
{
    Reflection.FieldInfo fi = EnumConstant.GetType().GetField(EnumConstant.ToString());
    DescriptionAttribute[] attr = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
    if (attr.Length > 0) {
        return attr(0).Description;
    } else {
        return EnumConstant.ToString();
    }
}

}

1 Comment

Given that the question is in C#, you should respond in C# or at least tell that it is VB.NET and can be (easily) converted so that we know you are aware of that.
1

You can use EnumDropDownListFor for this purpose. Here is an example of what you want. (Just don't forget to use EnumDropDownListFor you should use ASP MVC 5 and Visual Studio 2015.):

Your View:

@using System.Web.Mvc.Html
@using WebApplication2.Models
@model WebApplication2.Models.MyClass

@{
    ViewBag.Title = "Index";
}

@Html.EnumDropDownListFor(model => model.occupancyTimeline)

And your model:

public enum occupancyTimeline
{
    [Display(Name = "12 months")]
    TwelveMonths,
    [Display(Name = "14 months")]
    FourteenMonths,
    [Display(Name = "16 months")]
    SixteenMonths,
    [Display(Name = "18 months")]
    EighteenMonths
}

Reference: What's New in ASP.NET MVC 5.1

Comments

1
public namespace WebApplication16.Controllers{

    public enum occupancyTimeline:int {
        TwelveMonths=12,
        FourteenMonths=14,
        SixteenMonths=16,
        EighteenMonths=18
    }

    public static class MyExtensions {
        public static SelectList ToSelectList(this string enumObj)
        {
            var values = from occupancyTimeline e in Enum.GetValues(typeof(occupancyTimeline))
                         select new { Id = e, Name = string.Format("{0} Months",Convert.ToInt32(e)) };
            return new SelectList(values, "Id", "Name", enumObj);
        }
    }

}

Usage

@using WebApplication16.Controllers
@Html.DropDownListFor(model => model.occupancyTimeline,Model.occupancyTimeline.ToSelectList());

5 Comments

I get an error...@Html.DropDownListFor(model => model.occupancyTimeline,Model.occupancyTimeline.ToSelectList()); : 'string' does not contain a definition for 'ToSelectList' and no extension method 'ToSelectList' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)
Because you must declare MyExtension namespace in View.
new error : 'string' does not contain a definition for 'ToSelectList' and the best extension method overload 'CentralParkVIPPreview.Models.MyExtensions.ToSelectList(CentralParkVIPPreview.Models.occupancyTimeline)' has some invalid arguments
I was thinking your Model.occupancyTimeline is enum type. But it is string type. Now i will update my post to compatible with string
Please replace enumObj parameter type to string. Then all work normal
0
public enum occupancyTimeline
{
    TwelveMonths=0,
    FourteenMonths=1,
    SixteenMonths=2,
    EighteenMonths=3
}
public string[] enumString = {
    "12 Months", "14 Months", "16 Months", "18 Months"};

string selectedEnum = enumString[(int)occupancyTimeLine.TwelveMonths];

or

public enum occupancyTimeline
{
    TwelveMonths,
    FourteenMonths,
    SixteenMonths,
    EighteenMonths
}
public string[] enumString = {
    "12 Months", "14 Months", "16 Months", "18 Months"};


string selectedEnum = enumString[DropDownList.SelectedIndex];

1 Comment

I would replace your string[] enumStrings with a Dictionary<occupancyTimeline, string> :much easier to maintain (e.g, if you move the enum definition in a different file)
0

In addition to using an attribute for description (see other answers or use Google), I often use RESX files where the key is the enum text (for ex. TwelveMonths) and the value is the desired text.

Then it is relatively trivial to do a function that would return the desired text and it is also easy to translate values for multilingual applications.

I also like to add some unit tests to ensure that all values have an associated text. If there are some exceptions (like maybe a Count value at the end, then the unit test would exclude those from the check.

All that stuff is not really hard and quite flexible. If you use DescriptionAttribute and want multilingual support, you need to use resource files anyway.

Usually, I would have one class to get values (displayed name) per enum type that need to be displayed and one unit test file per resource file although I have some other classes for some common code like comparing key in resource file with value in enum for unit test (and one overload allows to specify exception).

By the way, in a case like that, it could make sense to have the value of an enum matches the number of months (for ex. TwelveMonths = 12). In such case, you can also use string.Format for displayed values and also have exceptions in resource (like singular).

Comments

0

MVC 5.1

@Html.EnumDropDownListFor(model => model.MyEnum)

MVC 5

@Html.DropDownList("MyType", 
   EnumHelper.GetSelectList(typeof(MyType)) , 
   "Select My Type", 
   new { @class = "form-control" })

MVC 4

You can refer this link Create a dropdown list from an Enum in Asp.Net MVC

Comments

0

Posting template for your solution, you may change some parts according your needs.

Define generic EnumHelper for formating enums:

public abstract class EnumHelper<T> where T : struct
{
    static T[] _valuesCache = (T[])Enum.GetValues(typeof(T));

    public virtual string GetEnumName()
    {
        return GetType().Name;
    }

    public static T[] GetValuesS()
    {
        return _valuesCache;
    }

    public T[] GetValues()
    {
        return _valuesCache;
    }      

    virtual public string EnumToString(T value)
    {
        return value.ToString();
    }                
}

Define MVC generic drop down list extension helper:

public static class SystemExt
{
    public static MvcHtmlString DropDownListT<T>(this HtmlHelper htmlHelper,
        string name,
        EnumHelper<T> enumHelper,
        string value = null,
        string nonSelected = null,
        IDictionary<string, object> htmlAttributes = null)
        where T : struct
    {
        List<SelectListItem> items = new List<SelectListItem>();

        if (nonSelected != null)
        {
            items.Add(new SelectListItem()
            {
                Text = nonSelected,
                Selected = string.IsNullOrEmpty(value),
            });
        }

        foreach (T item in enumHelper.GetValues())
        {
            if (enumHelper.EnumToIndex(item) >= 0)
                items.Add(new SelectListItem()
                {
                    Text = enumHelper.EnumToString(item),
                    Value = item.ToString(),                 //enumHelper.Unbox(item).ToString()
                    Selected = value == item.ToString(),
                });
        }

        return htmlHelper.DropDownList(name, items, htmlAttributes);
    }
}

Any time you need to format some Enums define EnumHelper for particular enum T:

public class OccupancyTimelineHelper : EnumHelper<OccupancyTimeline>
{
    public override string EnumToString(occupancyTimeline value)
    {
        switch (value)
        {
            case OccupancyTimelineHelper.TwelveMonths:
                return "12 Month";
            case OccupancyTimelineHelper.FourteenMonths:
                return "14 Month";
            case OccupancyTimelineHelper.SixteenMonths:
                return "16 Month";
            case OccupancyTimelineHelper.EighteenMonths:
                return "18 Month";
            default:
                return base.EnumToString(value);
        }
    }
}

Finally use the code in View:

@Html.DropDownListT("occupancyTimeline", new OccupancyTimelineHelper())

Comments

0

I'd go for a slightly different and perhaps simpler approach:

public static List<int> PermittedMonths = new List<int>{12, 14, 16, 18};

Then simply:

foreach(var permittedMonth in PermittedMonths)
{
    MyDropDownList.Items.Add(permittedMonth.ToString(), permittedMonth + " months");
}

Using enums in the way you describe I feel can be a bit of a trap.

Comments

0

You could use a getter casted into an integer, instead of trying to draw a property in a view from an Enum. My problem was that I was writing the value of enum as string, but I would like to have its int value.

For example you could have the below two properties in a View Model class:

public enum SomeEnum
{
   Test = 0,
   Test1 = 1,
   Test2,
}

public SomeEnum SomeEnum {get; init;}

public int SomeEnumId => (int) SomeEnum;

Then you could access your enum through a razor View as such:

<input type="hidden" asp-for="@Model.SomeEntityId" />

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.