Search code examples
c#asp.net-mvcenumsdisplayname-attribute

Enum DisplayName: Templates can be used only with field access


I know there are already other threads about this. I've been reading them. Here's what I've got:

namespace Books.Entities
{
    public enum Genre
    {
        [Display(Name = "Non Fiction")]
        NonFiction,
        Romance,
        Action,
        [Display(Name = "Science Fiction")]
        ScienceFiction
    }
}

Model:

namespace Books.Entities
{
    public class Book
    {
        public int ID { get; set; }

        [Required]
        [StringLength(255)]
        public string Title  { get; set; }

        public Genre Category { get; set; }
    }
}

Then, in a view:

@foreach (var item in Model) {
<tr>
    <td>
        @Html.DisplayFor(modelItem => item.Title)
    </td>
    <td>
        @Html.DisplayFor(modelItem => item.Category)
    </td>
</tr>

I would think the framework would use the DisplayName property automatically. Seems weird that it doesn't. But, whatever. Trying to overcome that with an extension (found this in another thread on the same question)...

using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;

public static class EnumExtensions
{
    public static string GetDisplayName(this Enum enumValue)
    {
        return enumValue.GetType()
                    .GetMember(enumValue.ToString())
                    .First()
                    .GetCustomAttribute<DisplayAttribute>()
                    .GetName();
    }
}

Looks like it should work, but when I try and use it:

 @Html.DisplayFor(modelItem => item.Category.GetDispayName())

I get this error:

{"Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions."}  

Solution

  • One thing you might want to consider would be to add a DisplayTemplate for Enum and your @Html.DiplayFor() will use this.

    If you create a folder in your ~/Views/Shared folder called DisplayTemplates, add a new view called Enum.cshtml and add this code to the view

    @model Enum
    @{
        var display = Model.GetDisplayName();
    }
    @display
    

    Then all you have to do is use @Html.DisplayFor(modelItem => item.Category) in your other views.

    BTW your GetDisplayName code will throw an error if there is no description attribute so you might want to use something like

    public static string GetDisplayName(this Enum enumValue)
        {
    
            Type type = enumValue.GetType();
            string name = Enum.GetName(type, enumValue);
            if (name != null)
            {
                FieldInfo field = type.GetField(name);
                if (field != null)
                {
                    DescriptionAttribute attr =
                           Attribute.GetCustomAttribute(field,
                             typeof(DescriptionAttribute)) as DescriptionAttribute;
                    if (attr != null)
                    {
                        return attr.Description;
                    }
                }
            }
            return name;
        }