About Enum and DataAnnotation
Asked Answered
P

4

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?

Pyelitis answered 26/5, 2015 at 19:43 Comment(3)
How are you using the enums for the Notebook property? Your code is bit confusing.Sanches
I edited, can you look now?Pyelitis
It seems that ASP.Net Core 3.0 now supports dataannotations on Enum values as you have typed it above.Schwing
A
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)]
Aesthete answered 26/5, 2015 at 20:9 Comment(7)
It doesn't work :( // "ResourceType is a 'type' but is used like a 'variable'. And what is ResFileAssembly?Pyelitis
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.Aesthete
It's not compiling.. can't instantiate the ResourceManager, and getString method also not works.Pyelitis
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.Aesthete
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 :)Pyelitis
@Aesthete 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!! =)Raila
@MarkGood thanks for the heads up. I updated the answer.Aesthete
R
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")
Raila answered 3/12, 2015 at 14:9 Comment(1)
Good catch on getting the type directly from the enum. Much cleaner approach.Aesthete
F
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.");
      }
   }
}
Factitious answered 26/5, 2015 at 19:59 Comment(9)
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?Pyelitis
@Pyelitis No certainly not. But this approach let's you configure the visuals for multilpe languages.Factitious
It's working with the @Aesthete 's answer. Anyway, thanks, +1 for try.Pyelitis
@Pyelitis Sure, if you do not need localization then the accepted answer is easier to maintain :)Factitious
@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.Aesthete
@Aesthete Oh, I thought 'Name' has to be a constant value.Factitious
Ahh, you are correct, I will update my answer with support for localization.Aesthete
@Aesthete No, the OP did not ask for that. And he said he wouldn't need it. Although I would be interested :)Factitious
I have a working version that supports localization through resource files in my updated answer if you want to check it out.Aesthete
H
0

While looking at akousmata, I liked everything except the parameters he's passing in. So unless your trying to use this extension for all Enums, I took his code and changed it a bit. While using:

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

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

Setup the extension:

public static class EnumExtensions
{
    public static string Display(this Notebook enumValue)
    {
        string displayName;
        var info = enumValue.GetType().GetMember(enumValue.ToString()).First();

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

        return displayName;
    }
}

To call it:

var n = Notebook.NotebookHP;
Console.WriteLine(n.Display());

Response:

Notebook HP
Hoehne answered 22/12, 2023 at 17:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.