REMESQ
REMESQ

Reputation: 1200

Trying to use [Description] data annotation attribute with existing code

SLIGHT UPDATE BELOW

I am trying to use the [Description] data annotation attribute with enums in order to display a friendly name. I've searched around a lot and cannot get anything implemented. Right now I have code that will display an enum as a string (using an extension), but I am not liking ThisIsAnEnum as an enum name (which is spaced out by the string extension) and it prohibits me from having longer names (which I need to maintain) such as for a radio button item. My goal is to have longer descriptions for radio button items without having to write really long enums. An extension/helper will probably be the right way to go, but I need to "fit" it into the code I am using, which is where I failed using the many examples out there.

The code I am using is generic, in that depending upon some logic either a radio button list, check box list, drop down list, select list or regular text boxes are displayed. For multi-item lists enum's are used, and the enum name is what is displayed (after using the string extension).

Here is the particular code that displays the enum:

public static IEnumerable<SelectListItem> GetItemsFromEnum<T>
    (T selectedValue = default(T)) where T : struct
    {
        return from name in Enum.GetNames(typeof(T))
               let enumValue = Convert.ToString((T)Enum.Parse(typeof(T), name, true))

               select new SelectListItem
               {
                   Text = name.ProperCase(),
                   Value = enumValue,
                   Selected = enumValue.Equals(selectedValue)
               };
    }

ProperCase is the class that changes the enum to something readable.

I found something that almost worked:

public static string GetEnumDescription<TEnum>(TEnum value)
    {
        FieldInfo fi = value.GetType().GetField(value.ToString());

        DescriptionAttribute[] attributes = 
            (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

        if ((attributes != null) && (attributes.Length > 0))
            return attributes[0].Description;
        else
            return value.ToString();
    }

in which case I changed code from Text = name.ProperCase(), to Text = name.GetEnumDescription(...) but if I put value in the parenthesis I get a "does not exist in the current context" message (which I tried fixing but just made the problem worse). If I leave it blank I get the "No overload for ... takes 0 arguments" (again, understandable - but I don't know how to fix). And if I put name in the parenthesis the code compiles but upon viewing the page I get the "Object reference not set..." error on this line:

DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes
    (typeof(DescriptionAttribute), false); 

I've spent a lot of time on this and know that my stumbling block is the

Text = name.ProperCase(),

code. Any ideas/help? Thanks in advance.

UPDATE:

If I do:

Text = GetEnumDescription(selectedValue),

I actually DO get the [Description] text, however, it just displays for the first enum. So, if I have 5 enums all with different [Description]'s the code just repeats the [Description] for the first enum 5 times instead of displaying differently for each. I hope that makes sense and gets to narrow down the problem.

Upvotes: 1

Views: 2298

Answers (1)

Darin Dimitrov
Darin Dimitrov

Reputation: 1038720

I'd recommend you the Display attribute:

public static IEnumerable<SelectListItem> GetItemsFromEnum<T>(T selectedValue = default(T)) where T : struct
{
    return 
        from name in Enum.GetNames(typeof(T))
        let enumValue = Convert.ToString((T)Enum.Parse(typeof(T), name, true))
        select new SelectListItem
        {
            Text = GetEnumDescription(name, typeof(T)),
            Value = enumValue,
            Selected = name == selectedValue.ToString()
        };
}

public static string GetEnumDescription(string value, Type enumType)
{
    var fi = enumType.GetField(value.ToString());
    var display = fi
        .GetCustomAttributes(typeof(DisplayAttribute), false)
        .OfType<DisplayAttribute>()
        .FirstOrDefault();
    if (display != null)
    {
        return display.Name;
    }
    return value;
}

and then you could have:

public enum Foo
{
    [Display(Name = "value 1")]
    Value1,

    Value2,

    [Display(Name = "value 3")]
    Value3
}

And now you could have:

var foo = Foo.Value2;
var values = GetItemsFromEnum(foo);

Also notice that I have modified the Selected clause in the LINQ expression as yours is not correct.

This being said, personally I would recommend you staying away from enums on your view models as they don't play nicely with what's built-in ASP.NET MVC and you will have to reinvent most of the things.

Upvotes: 2

Related Questions