paulsm4
paulsm4

Reputation: 121649

How do I get an Asp.Net Core DropdownList to show the [Display()] values in a C# enum?

I have an enumeration that looks like this:

BoT_DropdownValues.cs:

   public class BoT_DropdownValues
    {
        public enum BlockSizes
        {
            [Display(Name = "12 Months")]
            M12,
            [Display(Name = "24 Months")]
            M24,
            [Display(Name = "36 Months")]
            M36,
            [Display(Name = "48 Months")]
            M48,
            [Display(Name = "60 Months")]
            M60
        }

I want to choose one of the items from "select" element:

CreateBoT.cshtml:

<div class="form-group" id="Q3_div">
    @Html.DropDownListFor(m => 
        m.BoT_Answers.Q3, 
        new SelectList(Enum.GetValues(typeof(BoT_DropdownValues.BlockSizes))),
        "Select One",
        new { @class = "custom-select", @id = "BoT_Answers_Q3" })
    ...

Problem:

This syntax gives me "M12","M24", "M36", ... in the dropdown. I'd LIKE the dropdown to show the [Display()] names: "12 Months", "24 Months", etc. to the user.

Q: Is there syntax I can use with @Html.DropDownListFor() to achieve this?

Update

Most of the answers I found involved writing a static extensions class and much extra work. I'm still looking for a "better answer", but for now this seems to give me everything I'm looking for:

           @Html.DropDownListFor(m =>
                m.BoT_Answers.Q3,
                new List<SelectListItem>
                {
                    new SelectListItem {Text="Select One", Value="", Selected=true },
                    new SelectListItem {Text="12 Months",  Value="12" },
                    new SelectListItem {Text="24 Months", Value="24" },
                    new SelectListItem {Text="36 Months", Value="36" },
                    new SelectListItem {Text="48 Months", Value="48" },
                    new SelectListItem {Text="60 Months", Value="60" }
                },
                new { @class = "custom-select", @id = "BoT_Answers_Q3" })

Q: Can anybody think of any disadvantages to this approach?

Upvotes: 1

Views: 564

Answers (2)

Jeremy Lakeman
Jeremy Lakeman

Reputation: 11100

You're going to need to write a method, or use someone else's. Here's mine;

public static class EnumHelper<T> where T:struct, Enum{
    public static List<(T value, string display)> Values =
        typeof(T)
        .GetFields(BindingFlags.Static | BindingFlags.Public)
        .OrderBy(f => f.MetadataToken)
        .Select(f => (
            (T)f.GetValue(null),
            f.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName
                ?? f.GetCustomAttribute<DisplayAttribute>()?.Name
                ?? f.Name
        ))
        .ToList();
}

public static class Extensions{
    public static IEnumerable<SelectListItem> SelectList<T>(this IHtmlHelper html) where T:struct, Enum
        => EnumHelper<T>.Values.Select(v => new SelectListItem(v.display, v.value.ToString()));
}

<select asp-items="Html.SelectList<BlockSizes>()"></select>

Upvotes: 1

Maxqueue
Maxqueue

Reputation: 2444

I tested following using helper class and it worked. So valuesToDisplay = ["12 months", "24 months" etc...]:

var valuesToDisplay = GetDisplayValues();

static List<string> GetDisplayValues() {
    List<string> ret = new List<string>();
    var enumValues = Enum.GetValues(typeof(BlockSizes));
    foreach (var value in enumValues)
    {
        ret.Add(EnumHelper<BlockSizes>.GetDisplayValue((BlockSizes)value));
    }
    return ret;
}

public static class EnumHelper<T>
{
    public static IList<T> GetValues(Enum value)
    {
        var enumValues = new List<T>();

        foreach (FieldInfo fi in value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public))
        {
            enumValues.Add((T)Enum.Parse(value.GetType(), fi.Name, false));
        }
        return enumValues;
    }

    public static T Parse(string value)
    {
        return (T)Enum.Parse(typeof(T), value, true);
    }

    public static IList<string> GetNames(Enum value)
    {
        return value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public).Select(fi => fi.Name).ToList();
    }

    public static IList<string> GetDisplayValues(Enum value)
    {
        return GetNames(value).Select(obj => GetDisplayValue(Parse(obj))).ToList();
    }

    private static string lookupResource(Type resourceManagerProvider, string resourceKey)
    {
        foreach (PropertyInfo staticProperty in resourceManagerProvider.GetProperties(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
        {
            if (staticProperty.PropertyType == typeof(System.Resources.ResourceManager))
            {
                System.Resources.ResourceManager resourceManager = (System.Resources.ResourceManager)staticProperty.GetValue(null, null);
                return resourceManager.GetString(resourceKey);
            }
        }

        return resourceKey; // Fallback with the key name
    }

    public static string GetDisplayValue(T value)
    {
        var fieldInfo = value.GetType().GetField(value.ToString());

        var descriptionAttributes = fieldInfo.GetCustomAttributes(
            typeof(DisplayAttribute), false) as DisplayAttribute[];

        if (descriptionAttributes[0].ResourceType != null)
            return lookupResource(descriptionAttributes[0].ResourceType, descriptionAttributes[0].Name);

        if (descriptionAttributes == null) return string.Empty;
        return (descriptionAttributes.Length > 0) ? descriptionAttributes[0].Name : value.ToString();
    }
}

Upvotes: 1

Related Questions