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?

Salah Akbari
  • 39,330
  • 10
  • 79
  • 109
user979331
  • 11,039
  • 73
  • 223
  • 418

11 Answers11

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
}
Jure
  • 1,156
  • 5
  • 15
  • 1
    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? – Phil1970 Sep 06 '16 at 20:34
  • Updated my answer. Hope it helps. – Jure Sep 06 '16 at 21:42
  • I get an error with this `var defaultResult = $"Enum_{@this.GetType().Name}_{@this}";` – user979331 Sep 12 '16 at 18:11
  • 1
    $"" is string interpolation first introduced in C# 6.0 (Visual studio 2015). If it's not working, you can use string.Format instead. – Jure Sep 13 '16 at 08:32
4

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

Monah
  • 6,714
  • 6
  • 22
  • 52
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();
    }
}

}
Tony Dong
  • 3,213
  • 1
  • 29
  • 32
  • 2
    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. – Phil1970 Sep 06 '16 at 20:36
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

Salah Akbari
  • 39,330
  • 10
  • 79
  • 109
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());
ebattulga
  • 10,774
  • 20
  • 78
  • 116
  • 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?) – user979331 Sep 12 '16 at 14:01
  • Because you must declare `MyExtension` namespace in View. – ebattulga Sep 13 '16 at 08:09
  • 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` – user979331 Sep 13 '16 at 14:44
  • I was thinking your `Model.occupancyTimeline` is enum type. But it is `string` type. Now i will update my post to compatible with `string` – ebattulga Sep 15 '16 at 03:13
  • Please replace `enumObj` parameter type to `string`. Then all work normal – ebattulga Sep 15 '16 at 03:22
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];
Shannon Holsinger
  • 2,293
  • 1
  • 15
  • 21
  • I would replace your `string[]` enumStrings with a `Dictionary` :much easier to maintain (e.g, if you move the enum definition in a different file) – Gian Paolo Sep 06 '16 at 19:44
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).

Phil1970
  • 2,605
  • 2
  • 14
  • 15
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

Jibin Balachandran
  • 3,381
  • 1
  • 24
  • 38
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())
Tomas Kubes
  • 23,880
  • 18
  • 111
  • 148
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.

Tom Gullen
  • 61,249
  • 84
  • 283
  • 456
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" />
ageroh
  • 93
  • 1
  • 3