11

I have this Enum (Notebook.cs):

public enum Notebook : byte
{
   [Display(Name = "Notebook HP")]
   NotebookHP,

   [Display(Name = "Notebook Dell")]
   NotebookDell
}

Also this property in my class (TIDepartment.cs):

public Notebook Notebook { get; set; }

It's working perfectly, I just have one "problem":

I created an EnumDDLFor and it's showing the name I setted in DisplayAttribute, with spaces, but the object doesn't receive that name in DisplayAttribute, receives the Enum name (what is correct), so my question is:

Is there a way to receive the name with spaces which one I configured in DisplayAttribute?

developer033
  • 24,267
  • 8
  • 82
  • 108

3 Answers3

11

MVC doesn't make use of the Display attribute on enums (or any framework I'm aware of). You need to create a custom Enum extension class:

public static class EnumExtensions
{
    public static string GetDisplayAttributeFrom(this Enum enumValue, Type enumType)
    {
        string displayName = "";
        MemberInfo info = enumType.GetMember(enumValue.ToString()).First();

        if (info != null && info.CustomAttributes.Any())
        {
            DisplayAttribute nameAttr = info.GetCustomAttribute<DisplayAttribute>();
            displayName = nameAttr != null ? nameAttr.Name : enumValue.ToString();
        }
        else
        {
            displayName = enumValue.ToString();
        }
        return displayName;
    }
}

Then you can use it like this:

Notebook n = Notebook.NotebookHP;
String displayName = n.GetDisplayAttributeFrom(typeof(Notebook));

EDIT: Support for localization

This may not be the most efficient way, but SHOULD work.

public static class EnumExtensions
{
    public static string GetDisplayAttributeFrom(this Enum enumValue, Type enumType)
    {
        string displayName = "";
        MemberInfo info = enumType.GetMember(enumValue.ToString()).First();

        if (info != null && info.CustomAttributes.Any())
        {
            DisplayAttribute nameAttr = info.GetCustomAttribute<DisplayAttribute>();

            if(nameAttr != null) 
            {
                // Check for localization
                if(nameAttr.ResourceType != null && nameAttr.Name != null)
                {
                    // I recommend not newing this up every time for performance
                    // but rather use a global instance or pass one in
                    var manager = new ResourceManager(nameAttr.ResourceType);
                    displayName = manager.GetString(nameAttr.Name)
                }
                else if (nameAttr.Name != null)
                {
                    displayName = nameAttr != null ? nameAttr.Name : enumValue.ToString();
                }
            }
        }
        else
        {
            displayName = enumValue.ToString();
        }
        return displayName;
    }
}

On the enum, the key and resource type must be specified:

[Display(Name = "MyResourceKey", ResourceType = typeof(MyResourceFile)]
akousmata
  • 1,005
  • 14
  • 34
  • 1
    It doesn't work :( // "ResourceType is a 'type' but is used like a 'variable'. And what is ResFileAssembly? – developer033 May 27 '15 at 20:54
  • Updated my answer, added the `new` keyword for the resource manager and am using the resource type specified by the `ResourceType` in the enum's annotation. I tested this locally and it works. – akousmata May 27 '15 at 21:51
  • It's not compiling.. can't instantiate the ResourceManager, and getString method also not works. – developer033 May 28 '15 at 02:20
  • Well, I'm sorry, but the original answer does what you asked for and my edited answer works for me locally, so please Google the compilation error you're getting and ask a separate question. – akousmata May 29 '15 at 13:22
  • Yes, the original answer works fine, I'm just telling you that new version not works. I'm not asking for compiling this new, you just posted. Ty for the oldest version :) – developer033 May 30 '15 at 17:35
  • @akousmata developer033 is right. The code above doesn't compile. There are a couple problems with it. First, `manager.getString()` is invalid. It should be `manager.GetString()`. Second, `new ResourceManager(typeof(nameAttr.ResourceType))` is invalid. Remove `typeof()`. Third, `nameAttr != null` is redundant. Otherwise, it works great!! =) – Mark Good Dec 03 '15 at 13:10
  • 1
    @MarkGood thanks for the heads up. I updated the answer. – akousmata Dec 07 '15 at 16:03
8

Here's a simplified (and working) version of akousmata's localized enum extension:

public static string DisplayName(this Enum enumValue)
{
    var enumType = enumValue.GetType();
    var memberInfo = enumType.GetMember(enumValue.ToString()).First();

    if (memberInfo == null || !memberInfo.CustomAttributes.Any()) return enumValue.ToString();

    var displayAttribute = memberInfo.GetCustomAttribute<DisplayAttribute>();

    if (displayAttribute == null) return enumValue.ToString();

    if (displayAttribute.ResourceType != null && displayAttribute.Name != null)
    {
        var manager = new ResourceManager(displayAttribute.ResourceType);
        return manager.GetString(displayAttribute.Name);
    }

    return displayAttribute.Name ?? enumValue.ToString();
}

Note: I move enumType from a parameter to a local variable.

Example usage:

public enum IndexGroupBy 
{
    [Display(Name = "By Alpha")]
    ByAlpha,
    [Display(Name = "By Type")]
    ByType
}

And

@IndexGroupBy.ByAlpha.DisplayName()

Here is a editor template that can be used with the extension method above:

@model Enum

@{    
    var listItems = Enum.GetValues(Model.GetType()).OfType<Enum>().Select(e =>
        new SelectListItem
        {
            Text = e.DisplayName(),
            Value = e.ToString(),
            Selected = e.Equals(Model)
        });
    var prefix = ViewData.TemplateInfo.HtmlFieldPrefix;
    var index = 0;
    ViewData.TemplateInfo.HtmlFieldPrefix = string.Empty;

    foreach (var li in listItems)
    {
        var fieldName = string.Format(CultureInfo.InvariantCulture, "{0}_{1}", prefix, index++);
        <div class="editor-radio">
            @Html.RadioButton(prefix, li.Value, li.Selected, new {@id = fieldName})
            @Html.Label(fieldName, li.Text)
        </div>
    }
    ViewData.TemplateInfo.HtmlFieldPrefix = prefix;
}

And here is an example usage:

@Html.EditorFor(m => m.YourEnumMember, "Enum_RadioButtonList")
Mark Good
  • 4,271
  • 2
  • 31
  • 43
1

Since you are worrying about visuals I would use a configurable approach:

public NotebookTypes NotebookType;

public enum NotebookTypes{
   NotebookHP,
   NotebookDell
}

public string NotebookTypeName{
   get{
      switch(NotebookType){
         case NotebookTypes.NotebookHP:
            return "Notebook HP"; //You may read the language dependent value from xml...
         case NotebookTypes.NotebookDell:
            return "Notebook Dell"; //You may read the language dependent value from xml...
         default:
            throw new NotImplementedException("'" + typeof(NotebookTypes).Name + "." + NotebookType.ToString() + "' is not implemented correctly.");
      }
   }
}
Noel Widmer
  • 4,444
  • 9
  • 45
  • 69
  • I understood your answer, but is the only way? (If I have 15 enums, I have to do it for all).. Btw, I'm worring about visual because I have some enums like it: 'HigherEducationIncomplete', I think it looks horrible, I just want show 'Higher Education Incomplete' (as I configured in DisplayAttribute), can you understand now? – developer033 May 26 '15 at 20:12
  • @developer033 No certainly not. But this approach let's you configure the visuals for multilpe languages. – Noel Widmer May 26 '15 at 21:04
  • It's working with the @akousmata 's answer. Anyway, thanks, +1 for try. – developer033 May 26 '15 at 21:09
  • @developer033 Sure, if you do not need localization then the accepted answer is easier to maintain :) – Noel Widmer May 26 '15 at 21:11
  • 1
    @NoelWidmer you can still support localization through resource files and the provided solution doesn't need to change. That wasn't what the OP was asking but he could simply change his enum to `[Display(Name = ResFileName.ResFileValue)]` and the answer would still work. – akousmata May 27 '15 at 14:51
  • @akousmata Oh, I thought 'Name' has to be a constant value. – Noel Widmer May 27 '15 at 15:15
  • Ahh, you are correct, I will update my answer with support for localization. – akousmata May 27 '15 at 15:43
  • @akousmata No, the OP did not ask for that. And he said he wouldn't need it. Although I would be interested :) – Noel Widmer May 27 '15 at 15:53
  • 1
    I have a working version that supports localization through resource files in my updated answer if you want to check it out. – akousmata May 27 '15 at 21:54