关于枚举和数据注释

11

我有一个枚举类型(Notebook.cs):

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

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

还有我类中的这个属性(TIDepartment.cs):

public Notebook Notebook { get; set; }

它运作得非常完美,只有一个“问题”:

我创建了一个EnumDDLFor并且它显示了我在DisplayAttribute中设置的名称,带有空格,但是对象没有接收到DisplayAttribute中的那个名称,而是接收到了Enum名称(这是正确的),所以我的问题是:

有没有一种方法可以接收到我在DisplayAttribute中配置的带有空格的名称?


你是如何使用枚举类型来表示笔记本属性的?你的代码有些令人困惑。 - Yosep Kim
我修改了,你现在可以看一下吗? - developer033
看起来 ASP.Net Core 3.0 现在支持在枚举值上使用数据注释,就像您上面输入的那样。 - Steve
3个回答

11

MVC没有使用枚举类型的Display特性(或者我所知道的任何框架都没有使用)。您需要创建一个自定义的枚举扩展类:

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;
    }
}

然后您可以像这样使用它:

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

编辑:支持本地化

这可能不是最高效的方法,但应该有效。

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;
    }
}

在枚举中,必须指定键和资源类型:

[Display(Name = "MyResourceKey", ResourceType = typeof(MyResourceFile)]

1
它不起作用 :( // “ResourceType是一个'type',但被用作'variable'。而ResFileAssembly是什么? - developer033
更新了我的答案,为资源管理器添加了 new 关键字,并使用枚举注释中指定的资源类型。我在本地测试过了,它可以正常工作。 - akousmata
很抱歉,但原始答案已经满足了你的要求,而我的修改后的答案在本地运行良好。因此,请谷歌一下你遇到的编译错误,并提出一个单独的问题。 - akousmata
是的,原始答案很好用,我只是告诉你新版本不起作用。我并没有要求编译这个你刚发布的新版本。谢谢你提供最早的版本 :) - developer033
1
@MarkGood 谢谢你的提醒。我已经更新了答案。 - akousmata
显示剩余3条评论

8
这是akousmata本地化枚举扩展的简化版本(并且是可行的):
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();
}

注意:我将enumType从参数移动到本地变量。
示例用法:
public enum IndexGroupBy 
{
    [Display(Name = "By Alpha")]
    ByAlpha,
    [Display(Name = "By Type")]
    ByType
}

并且

@IndexGroupBy.ByAlpha.DisplayName()

以下是可以与上述扩展方法一起使用的编辑器模板:
```html

这里是一个编辑器模板,可与上面的扩展方法一起使用:

```
@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;
}

以下是一个使用示例:

@Html.EditorFor(m => m.YourEnumMember, "Enum_RadioButtonList")

2
从枚举类型直接获取类型是个好主意,做法更加简洁。 - akousmata

1

既然您担心视觉效果,我建议采用可配置的方法:

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.");
      }
   }
}

我理解了你的回答,但这是唯一的方法吗?(如果我有15个枚举,我必须为所有枚举都这样做)另外,我担心视觉效果,因为我有一些像这样的枚举:'HigherEducationIncomplete',我认为它看起来很糟糕,我只想显示'Higher Education Incomplete'(就像我在DisplayAttribute中配置的那样),现在你能理解了吗? - developer033
@developer033 当然不是。但这种方法可以让您为多种语言配置可视化效果。 - Noel Widmer
它与@akousmata的答案一起工作。无论如何,谢谢,+1尝试。 - developer033
1
@NoelWidmer,您仍然可以通过资源文件来支持本地化,提供的解决方案无需更改。这不是OP所问的问题,但他可以简单地将枚举更改为[Display(Name = ResFileName.ResFileValue)],答案仍然有效。 - akousmata
1
如果您想查看的话,我在我的更新答案中有一个支持通过资源文件本地化的工作版本。 - akousmata
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接