3

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."}  
Casey Crookston
  • 13,016
  • 24
  • 107
  • 193
  • What happens if you just try `@Model.Category.GetDispayName()` – mxmissile Oct 02 '15 at 15:01
  • You didn't post your model that contains the `Category` property. You posted your enum... – Jamie Rees Oct 02 '15 at 15:08
  • @mxmissile: It would need to be @item.Category.GetDispayName(), becuase we are in a loop. And when I try that, I get: CS1061: 'Genre' does not contain a definition for 'GetDispayName' and no extension method 'GetDispayName' accepting a first argument of type 'Genre' could be found – Casey Crookston Oct 02 '15 at 15:14
  • @Jamie, true. But it's boring :-). Edited OP. – Casey Crookston Oct 02 '15 at 15:14
  • What happens when you put the `[DisplayName]` attribute on all your enum values? – Jamie Rees Oct 02 '15 at 15:17
  • 1
    @mxmissile - oh, wait! I don't know what I did wrong the first time, but I tried it again, and this time it worked! Weird. Anyway, if you put that as an answer, I'll select it. Thanks. – Casey Crookston Oct 02 '15 at 15:17
  • If you use asp.net mvc5 then for showing Enum it has it's own helper: `@Html.EnumDropDownListFor(modelItem => item.Genre)` – Mahbubur Rahman Oct 02 '15 at 15:21
  • @Mahbubur Rahman: Yes! And that does work great for dropdowns. I'm already using that elsewhere. But there isn't yet any such helper for displaying an Enum just as a string, and not in a dropdown. – Casey Crookston Oct 02 '15 at 15:24

2 Answers2

4

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;
    }
JamieD77
  • 13,796
  • 1
  • 17
  • 27
  • Perfect. I had found the same solution myself, but I'm marking yours as the answer because it looks better than selecting my own answer. Thanks. – Casey Crookston Oct 02 '15 at 15:43
  • Yeah i read that other article too and it has a lot of good ideas using the new enumhelper.. i'd definitely apply those suggestions to your solution – JamieD77 Oct 02 '15 at 15:47
  • Not sure if this is an MVC variation, but to make this work using the standard DataAnnotations I changed DescriptionAttribute for DisplayAttribute, because that's the attribute on the enum - and the value is held in Name (not Description) - but the technique is sound. – Kaine Nov 17 '16 at 14:44
2

Ok, found a couple of ways to work around this. First, as mxmissile suggested, just use:

@item.Category.GetDisplayName()

Turns out the error message told me exactly what I needed to know. I just wasn't catching on that @Html.DisplayFor() is a template, and I can't use it with the helper extension.

But, a better solution turned out to be one I found here:

http://www.codeproject.com/Articles/776908/Dealing-with-Enum-in-MVC

In this solution, the author provides a display template that works by default for all enums, without having to ass the GetDisplayName(). With this soluiton, the original code works just fine:

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

And furthermore, it will work across the board, by default.

(NOTE: This is all assuming you are using MVC5.x)

Community
  • 1
  • 1
Casey Crookston
  • 13,016
  • 24
  • 107
  • 193