如何为枚举提供用户友好的名称?

发布于 2024-08-03 03:07:39 字数 1671 浏览 3 评论 0原文

我有一个像这样的枚举

Enum Complexity
{
  NotSoComplex,
  LittleComplex,
  Complex,
  VeryComplex
}

,我想在下拉列表中使用它,但不想在列表中看到这样的骆驼名称(对于用户来说看起来真的很奇怪)。相反,我想用正常的措辞,比如 没那么复杂 有点复杂(等等)

另外,我的应用程序是多语言的,我希望能够显示这些本地化的字符串,我使用一个帮助器 TranslationHelper(string strID) ,它为我提供了字符串 id 的本地化版本。

我有一个可行的解决方案,但不是很优雅: 我为枚举创建一个辅助类,其中一个成员 Complexity 和 ToString() 被覆盖,如下所示(代码简化)

public class ComplexityHelper
{
    public ComplexityHelper(Complexity c, string desc)
    { m_complex = c; m_desc=desc; }

    public Complexity Complexity { get { ... } set {...} }
    public override ToString() { return m_desc; }

    //Then a static field like this 

    private static List<Complexity> m_cxList = null;

    // and method that returns the status lists to bind to DataSource of lists
    public static List<ComplexityHelper> GetComplexities() 
    {
        if (m_cxList == null)
        {
           string[] list = TranslationHelper.GetTranslation("item_Complexities").Split(',');
           Array listVal = Enum.GetValues(typeof(Complexities));
           if (list.Length != listVal.Length)
               throw new Exception("Invalid Complexities translations (item_Complexities)");
           m_cxList = new List<Complexity>();
           for (int i = 0; i < list.Length; i++)
           {
             Complexity cx = (ComplexitylistVal.GetValue(i);
             ComplexityHelper ch = new ComplexityHelper(cx, list[i]);
             m_cxList.Add(ch);
           }
        }
        return m_cxList;
    }
}

虽然可行,但我对此不满意,因为我必须为需要使用的各种枚举进行类似的编码在选项列表中。

有人对更简单或更通用的解决方案有建议吗?

谢谢 博格丹

I have an enumeration like

Enum Complexity
{
  NotSoComplex,
  LittleComplex,
  Complex,
  VeryComplex
}

And I want to use it in a dropdown list, but don't want to see such Camel names in list (looks really odd for users). Instead I would like to have in normal wording, like
Not so complex
Little complex (etc)

Also, my application is multi-lang and I would like to be able to display those strings localized, and I use a helper, TranslationHelper(string strID) which gives me the localized version for a string id.

I have a working solution, but not very elegant:
I create a helper class for the enum, with one member Complexity and ToString() overwritten, like below (code simplified)

public class ComplexityHelper
{
    public ComplexityHelper(Complexity c, string desc)
    { m_complex = c; m_desc=desc; }

    public Complexity Complexity { get { ... } set {...} }
    public override ToString() { return m_desc; }

    //Then a static field like this 

    private static List<Complexity> m_cxList = null;

    // and method that returns the status lists to bind to DataSource of lists
    public static List<ComplexityHelper> GetComplexities() 
    {
        if (m_cxList == null)
        {
           string[] list = TranslationHelper.GetTranslation("item_Complexities").Split(',');
           Array listVal = Enum.GetValues(typeof(Complexities));
           if (list.Length != listVal.Length)
               throw new Exception("Invalid Complexities translations (item_Complexities)");
           m_cxList = new List<Complexity>();
           for (int i = 0; i < list.Length; i++)
           {
             Complexity cx = (ComplexitylistVal.GetValue(i);
             ComplexityHelper ch = new ComplexityHelper(cx, list[i]);
             m_cxList.Add(ch);
           }
        }
        return m_cxList;
    }
}

While workable, I'm not happy with it, since I have to code it similarily for various enums I need to use in picklists.

Does anyone have a suggestion for a simpler or more generic solution?

Thanks
Bogdan

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

只涨不跌 2024-08-10 03:07:39

基本友好名称

使用描述属性:*

enum MyEnum
{
    [Description("This is black")]
    Black,
    [Description("This is white")]
    White
}

以及一个方便的枚举扩展方法:

public static string GetDescription(this Enum value)
{
    FieldInfo field = value.GetType().GetField(value.ToString());
    object[] attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), true);
    if(attribs.Length > 0)
    {
        return ((DescriptionAttribute)attribs[0]).Description;
    }
    return string.Empty;
}

像这样使用:(

MyEnum val = MyEnum.Black;
Console.WriteLine(val.GetDescription()); //writes "This is black"

注意这并不完全适用于位标志...)

用于本地化< /strong>

.NET 中有一个完善的模式,用于处理每个字符串值的多种语言 - 使用 资源文件,并扩展扩展方法以从资源文件中读取:

public static string GetDescription(this Enum value)
{
    FieldInfo field = value.GetType().GetField(value.ToString());
    object[] attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), true));
    if(attribs.Length > 0)
    {
        string message = ((DescriptionAttribute)attribs[0]).Description;
        return resourceMgr.GetString(message, CultureInfo.CurrentCulture);
    }
    return string.Empty;
}

任何时候我们可以利用现有的 BCL 功能来实现我们想要的,这绝对是第一个探索的途径。这最大限度地降低了复杂性,并使用了许多其他开发人员已经熟悉的模式。

将它们放在一起

为了将其绑定到 DropDownList,我们可能希望跟踪控件中的真实枚举值,并将翻译后的友好名称限制为视觉糖。我们可以通过使用匿名类型和列表上的 DataField 属性来做到这一点:

<asp:DropDownList ID="myDDL"
                  DataTextField="Description"
                  DataValueField="Value" />

myDDL.DataSource = Enum.GetValues(typeof(MyEnum)).OfType<MyEnum>().Select(
    val => new { Description = val.GetDescription(), Value = val.ToString() });

myDDL.DataBind();

让我们分解该 DataSource 行:

  • 首先我们调用 Enum.GetValues(typeof(MyEnum)),这为我们提供了一个松散的-值的类型化 Array
  • 接下来我们调用 OfType() ,将数组转换为 IEnumerable
  • 然后我们调用 Select() 并提供一个 lambda,该 lambda 可以投影一个具有两个字段(Description 和 Value)的新对象。

DataTextField 和 DataValueField 属性在数据绑定时进行反射式评估,因此它们将搜索 DataItem 上具有匹配名称的字段。

-请注意,在主文章中,作者编写了自己的 DescriptionAttribute 类,这是不必要的,因为 .NET 标准库中已经存在该类。-

Basic Friendly names

Use the Description attribute:*

enum MyEnum
{
    [Description("This is black")]
    Black,
    [Description("This is white")]
    White
}

And a handy extension method for enums:

public static string GetDescription(this Enum value)
{
    FieldInfo field = value.GetType().GetField(value.ToString());
    object[] attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), true);
    if(attribs.Length > 0)
    {
        return ((DescriptionAttribute)attribs[0]).Description;
    }
    return string.Empty;
}

Used like so:

MyEnum val = MyEnum.Black;
Console.WriteLine(val.GetDescription()); //writes "This is black"

(Note this doesn't exactly work for bit flags...)

For localization

There is a well-established pattern in .NET for handling multiple languages per string value - use a resource file, and expand the extension method to read from the resource file:

public static string GetDescription(this Enum value)
{
    FieldInfo field = value.GetType().GetField(value.ToString());
    object[] attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), true));
    if(attribs.Length > 0)
    {
        string message = ((DescriptionAttribute)attribs[0]).Description;
        return resourceMgr.GetString(message, CultureInfo.CurrentCulture);
    }
    return string.Empty;
}

Any time we can leverage existing BCL functionality to achieve what we want, that's definitely the first route to explore. This minimizes complexity and uses patterns already familiar to many other developers.

Putting it all together

To get this to bind to a DropDownList, we probably want to track the real enum values in our control and limit the translated, friendly name to visual sugar. We can do so by using an anonymous type and the DataField properties on the list:

<asp:DropDownList ID="myDDL"
                  DataTextField="Description"
                  DataValueField="Value" />

myDDL.DataSource = Enum.GetValues(typeof(MyEnum)).OfType<MyEnum>().Select(
    val => new { Description = val.GetDescription(), Value = val.ToString() });

myDDL.DataBind();

Let's break down that DataSource line:

  • First we call Enum.GetValues(typeof(MyEnum)), which gets us a loosely-typed Array of the values
  • Next we call OfType<MyEnum>() which converts the array to an IEnumerable<MyEnum>
  • Then we call Select() and provide a lambda that projects a new object with two fields, Description and Value.

The DataTextField and DataValueField properties are evaluated reflectively at databind-time, so they will search for fields on DataItem with matching names.

-Note in the main article, the author wrote their own DescriptionAttribute class which is unnecessary, as one already exists in .NET's standard libraries.-

时间你老了 2024-08-10 03:07:39

像其他答案一样使用属性是一个好方法,但是如果您只想使用枚举值中的文本,则以下代码将根据值的驼峰式大小写进行分割

public static string GetDescriptionOf(Enum enumType)
{
    Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled);
    return capitalLetterMatch.Replace(enumType.ToString(), " 
amp;");
}

: >GetDescriptionOf(Complexity.NotSoComplex) 将返回 Not So Complex。这可以与任何枚举值一起使用。

为了使其更有用,您可以将其设为扩展方法:

public static string ToFriendlyString(this Enum enumType)
{
    Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled);
    return capitalLetterMatch.Replace(enumType.ToString(), " 
amp;");
}

您现在可以使用 Complexity.NotSoComplex.ToFriendlyString() 调用它以返回 Not So Complex


编辑:刚刚注意到在你的问题中你提到你需要本地化文本。在这种情况下,我将使用一个属性来包含一个键来查找本地化值,但如果找不到本地化文本,则默认使用友好字符串方法作为最后的手段。您可以这样定义枚举:

enum Complexity
{
    [LocalisedEnum("Complexity.NotSoComplex")]
    NotSoComplex,
    [LocalisedEnum("Complexity.LittleComplex")]
    LittleComplex,
    [LocalisedEnum("Complexity.Complex")]
    Complex,
    [LocalisedEnum("Complexity.VeryComplex")]
    VeryComplex
}

您还需要以下代码:

[AttributeUsage(AttributeTargets.Field, AllowMultiple=false, Inherited=true)]
public class LocalisedEnum : Attribute
{
    public string LocalisationKey{get;set;}

    public LocalisedEnum(string localisationKey)
    {
        LocalisationKey = localisationKey;
    }
}

public static class LocalisedEnumExtensions
{
    public static string ToLocalisedString(this Enum enumType)
    {
        // default value is the ToString();
        string description = enumType.ToString();

        try
        {
            bool done = false;

            MemberInfo[] memberInfo = enumType.GetType().GetMember(enumType.ToString());

            if (memberInfo != null && memberInfo.Length > 0)
            {
                object[] attributes = memberInfo[0].GetCustomAttributes(typeof(LocalisedEnum), false);

                if (attributes != null && attributes.Length > 0)
                {
                    LocalisedEnum descriptionAttribute = attributes[0] as LocalisedEnum;

                    if (description != null && descriptionAttribute != null)
                    {
                        string desc = TranslationHelper.GetTranslation(descriptionAttribute.LocalisationKey);

                        if (desc != null)
                        {
                            description = desc;
                            done = true;
                        }
                    }
                }
            }

            if (!done)
            {
                Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled);
                description = capitalLetterMatch.Replace(enumType.ToString(), " 
amp;");
            }
        }
        catch
        {
            description = enumType.ToString();
        }

        return description;
    }
}

要获取本地化描述,您可以调用:

Complexity.NotSoComplex.ToLocalisedString()

这有几种后备情况:

  • 如果枚举定义了 LocalizedEnum 属性,它将 如果枚举定义了 LocalizedEnum 属性,但未找到本地化文本,则使用该键查找翻译文本,
  • 则默认使用驼峰式分割方法
  • 如果枚举没有 , >LocalizedEnum 属性已定义,出现错误时将使用驼峰式分割方法
  • ,默认为枚举值的 ToString

The use of attributes as in the other answers is a good way to go, but if you just want to use the text from the values of the enum, the following code will split based on the camel-casing of the value:

public static string GetDescriptionOf(Enum enumType)
{
    Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled);
    return capitalLetterMatch.Replace(enumType.ToString(), " 
amp;");
}

Calling GetDescriptionOf(Complexity.NotSoComplex) will return Not So Complex. This can be used with any enum value.

To make it more useful, you could make it an extension method:

public static string ToFriendlyString(this Enum enumType)
{
    Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled);
    return capitalLetterMatch.Replace(enumType.ToString(), " 
amp;");
}

You cal now call it using Complexity.NotSoComplex.ToFriendlyString() to return Not So Complex.


EDIT: just noticed that in your question you mention that you need to localise the text. In that case, I'd use an attribute to contain a key to look up the localised value, but default to the friendly string method as a last resort if the localised text cannot be found. You would define you enums like this:

enum Complexity
{
    [LocalisedEnum("Complexity.NotSoComplex")]
    NotSoComplex,
    [LocalisedEnum("Complexity.LittleComplex")]
    LittleComplex,
    [LocalisedEnum("Complexity.Complex")]
    Complex,
    [LocalisedEnum("Complexity.VeryComplex")]
    VeryComplex
}

You would also need this code:

[AttributeUsage(AttributeTargets.Field, AllowMultiple=false, Inherited=true)]
public class LocalisedEnum : Attribute
{
    public string LocalisationKey{get;set;}

    public LocalisedEnum(string localisationKey)
    {
        LocalisationKey = localisationKey;
    }
}

public static class LocalisedEnumExtensions
{
    public static string ToLocalisedString(this Enum enumType)
    {
        // default value is the ToString();
        string description = enumType.ToString();

        try
        {
            bool done = false;

            MemberInfo[] memberInfo = enumType.GetType().GetMember(enumType.ToString());

            if (memberInfo != null && memberInfo.Length > 0)
            {
                object[] attributes = memberInfo[0].GetCustomAttributes(typeof(LocalisedEnum), false);

                if (attributes != null && attributes.Length > 0)
                {
                    LocalisedEnum descriptionAttribute = attributes[0] as LocalisedEnum;

                    if (description != null && descriptionAttribute != null)
                    {
                        string desc = TranslationHelper.GetTranslation(descriptionAttribute.LocalisationKey);

                        if (desc != null)
                        {
                            description = desc;
                            done = true;
                        }
                    }
                }
            }

            if (!done)
            {
                Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled);
                description = capitalLetterMatch.Replace(enumType.ToString(), " 
amp;");
            }
        }
        catch
        {
            description = enumType.ToString();
        }

        return description;
    }
}

To get the localised descriptions, you would then call:

Complexity.NotSoComplex.ToLocalisedString()

This has several fallback cases:

  • if the enum has a LocalisedEnum attribute defined, it will use the key to look up the translated text
  • if the enum has a LocalisedEnum attribute defined but no localised text is found, it defaults to using the camel-case split method
  • if the enum does not have a LocalisedEnum attribute defined, it will use the camel-case split method
  • upon any error, it defaults to the ToString of the enum value
み青杉依旧 2024-08-10 03:07:39

我使用下面的类

    public class EnumUtils
    {
    /// <summary>
    ///     Reads and returns the value of the Description Attribute of an enumeration value.
    /// </summary>
    /// <param name="value">The enumeration value whose Description attribute you wish to have returned.</param>
    /// <returns>The string value portion of the Description attribute.</returns>
    public static string StringValueOf(Enum value)
    {
        FieldInfo fi = value.GetType().GetField(value.ToString());
        DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
        if (attributes.Length > 0)
        {
            return attributes[0].Description;
        }
        else
        {
            return value.ToString();
        }
    }

    /// <summary>
    ///     Returns the Enumeration value that has a given Description attribute.
    /// </summary>
    /// <param name="value">The Description attribute value.</param>
    /// <param name="enumType">The type of enumeration in which to search.</param>
    /// <returns>The enumeration value that matches the Description value provided.</returns>
    /// <exception cref="ArgumentException">Thrown when the specified Description value is not found with in the provided Enumeration Type.</exception>
    public static object EnumValueOf(string value, Type enumType)
    {
        string[] names = Enum.GetNames(enumType);
        foreach (string name in names)
        {
            if (StringValueOf((Enum)Enum.Parse(enumType, name)).Equals(value))
            {
                return Enum.Parse(enumType, name);
            }
        }

        throw new ArgumentException("The string is not a description or value of the specified enum.");
    }

读取一个名为描述的属性

public enum PuppyType
{
    [Description("Cute Puppy")]
    CutePuppy = 0,
    [Description("Silly Puppy")]
    SillyPuppy
}

I use the following class

    public class EnumUtils
    {
    /// <summary>
    ///     Reads and returns the value of the Description Attribute of an enumeration value.
    /// </summary>
    /// <param name="value">The enumeration value whose Description attribute you wish to have returned.</param>
    /// <returns>The string value portion of the Description attribute.</returns>
    public static string StringValueOf(Enum value)
    {
        FieldInfo fi = value.GetType().GetField(value.ToString());
        DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
        if (attributes.Length > 0)
        {
            return attributes[0].Description;
        }
        else
        {
            return value.ToString();
        }
    }

    /// <summary>
    ///     Returns the Enumeration value that has a given Description attribute.
    /// </summary>
    /// <param name="value">The Description attribute value.</param>
    /// <param name="enumType">The type of enumeration in which to search.</param>
    /// <returns>The enumeration value that matches the Description value provided.</returns>
    /// <exception cref="ArgumentException">Thrown when the specified Description value is not found with in the provided Enumeration Type.</exception>
    public static object EnumValueOf(string value, Type enumType)
    {
        string[] names = Enum.GetNames(enumType);
        foreach (string name in names)
        {
            if (StringValueOf((Enum)Enum.Parse(enumType, name)).Equals(value))
            {
                return Enum.Parse(enumType, name);
            }
        }

        throw new ArgumentException("The string is not a description or value of the specified enum.");
    }

Which reads an attribute called description

public enum PuppyType
{
    [Description("Cute Puppy")]
    CutePuppy = 0,
    [Description("Silly Puppy")]
    SillyPuppy
}
一袭白衣梦中忆 2024-08-10 03:07:39

谢谢大家的所有回答。
最后,我使用了 Rex M 和 adrianbanks 的组合,并添加了我自己的改进,以简化与 ComboBox 的绑定。

需要进行这些更改是因为,在处理代码时,我意识到有时需要能够从组合中排除一个枚举项。
例如

Enum Complexity
{
  // this will be used in filters, 
  // but not in module where I have to assign Complexity to a field
  AllComplexities,  
  NotSoComplex,
  LittleComplex,
  Complex,
  VeryComplex
}

,有时我希望选项列表显示除 AllComplexities 之外的所有内容(在添加 - 编辑模块中),其他时间显示所有内容(在过滤器中)。

这就是我所做的:

  1. 我创建了一个扩展方法,它使用 Description 属性作为本地化查找键。如果缺少 Description 属性,我将查找本地化键创建为 EnumName_
    枚举值。最后,如果翻译丢失,我只是根据驼峰命名法分割枚举名称来分隔单词,如 adrianbanks 所示。顺便说一句,TranslationHelper 是 resourceMgr.GetString(...) 的包装器。

完整的代码如下所示。

public static string GetDescription(this System.Enum value)
{
    string enumID = string.Empty;
    string enumDesc = string.Empty;
    try 
    {         
        // try to lookup Description attribute
        FieldInfo field = value.GetType().GetField(value.ToString());
        object[] attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), true);
        if (attribs.Length > 0)
        {
            enumID = ((DescriptionAttribute)attribs[0]).Description;
            enumDesc = TranslationHelper.GetTranslation(enumID);
        }
        if (string.IsNullOrEmpty(enumID) || TranslationHelper.IsTranslationMissing(enumDesc))
        {
            // try to lookup translation from EnumName_EnumValue
            string[] enumName = value.GetType().ToString().Split('.');
            enumID = string.Format("{0}_{1}", enumName[enumName.Length - 1], value.ToString());
            enumDesc = TranslationHelper.GetTranslation(enumID);
            if (TranslationHelper.IsTranslationMissing(enumDesc))
                enumDesc = string.Empty;
        }

        // try to format CamelCase to proper names
        if (string.IsNullOrEmpty(enumDesc))
        {
            Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled);
            enumDesc = capitalLetterMatch.Replace(value.ToString(), " 
amp;");
        }
    }
    catch (Exception)
    {
        // if any error, fallback to string value
        enumDesc = value.ToString();
    }

    return enumDesc;
}

我创建了一个基于 Enum 的通用帮助器类,它允许将枚举轻松绑定到 DataSource

public class LocalizableEnum
{
    /// <summary>
    /// Column names exposed by LocalizableEnum
    /// </summary>
    public class ColumnNames
    {
        public const string ID = "EnumValue";
        public const string EntityValue = "EnumDescription";
    }
}

public class LocalizableEnum<T>
{

    private T m_ItemVal;
    private string m_ItemDesc;

    public LocalizableEnum(T id)
    {
        System.Enum idEnum = id as System.Enum;
        if (idEnum == null)
            throw new ArgumentException(string.Format("Type {0} is not enum", id.ToString()));
        else
        {
            m_ItemVal = id;
            m_ItemDesc = idEnum.GetDescription();
        }
    }

    public override string ToString()
    {
        return m_ItemDesc;
    }

    public T EnumValue
    {
        get { return m_ID; }
    }

    public string EnumDescription
    {
        get { return ToString(); }
    }

}

然后我创建了一个通用静态方法,该方法返回List>,如下所示,

public static List<LocalizableEnum<T>> GetEnumList<T>(object excludeMember)
{
    List<LocalizableEnum<T>> list =null;
    Array listVal = System.Enum.GetValues(typeof(T));
    if (listVal.Length>0)
    {
        string excludedValStr = string.Empty;
        if (excludeMember != null)
            excludedValStr = ((T)excludeMember).ToString();

        list = new List<LocalizableEnum<T>>();
        for (int i = 0; i < listVal.Length; i++)
        {
            T currentVal = (T)listVal.GetValue(i);
            if (excludedValStr != currentVal.ToString())
            {
                System.Enum enumVal = currentVal as System.Enum;
                LocalizableEnum<T> enumMember = new LocalizableEnum<T>(currentVal);
                list.Add(enumMember);
            }
        }
    }
    return list;
}

以及一个返回包含所有成员的列表的包装器

public static List<LocalizableEnum<T>> GetEnumList<T>()
{
        return GetEnumList<T>(null);
}

现在让我们将所有内容放在一起并绑定到实际组合:

// in module where we want to show items with all complexities
// or just filter on one complexity

comboComplexity.DisplayMember = LocalizableEnum.ColumnNames.EnumValue;
comboComplexity.ValueMember = LocalizableEnum.ColumnNames.EnumDescription;
comboComplexity.DataSource = EnumHelper.GetEnumList<Complexity>();
comboComplexity.SelectedValue = Complexity.AllComplexities;

// ....
// and here in edit module where we don't want to see "All Complexities"
comboComplexity.DisplayMember = LocalizableEnum.ColumnNames.EnumValue;
comboComplexity.ValueMember = LocalizableEnum.ColumnNames.EnumDescription;
comboComplexity.DataSource = EnumHelper.GetEnumList<Complexity>(Complexity.AllComplexities);
comboComplexity.SelectedValue = Complexity.VeryComplex; // set default value

要读取选定的值并使用它,我使用如下代码

Complexity selComplexity = (Complexity)comboComplexity.SelectedValue;

Thank you all for all answers.
Finally I used a combination from Rex M and adrianbanks, and added my own improvements, to simplify the binding to ComboBox.

The changes were needed because, while working on the code, I realized sometimes I need to be able to exclude one enumeration item from the combo.
E.g.

Enum Complexity
{
  // this will be used in filters, 
  // but not in module where I have to assign Complexity to a field
  AllComplexities,  
  NotSoComplex,
  LittleComplex,
  Complex,
  VeryComplex
}

So sometimes I want the picklist to show all but AllComplexities (in add - edit modules) and other time to show all (in filters)

Here's what I did:

  1. I created a extension method, that uses Description Attribute as localization lookup key. If Description attribute is missing, I create the lookup localization key as EnumName_
    EnumValue. Finally, if translation is missing I just split enum name based on camelcase to separate words as shown by adrianbanks. BTW, TranslationHelper is a wrapper around resourceMgr.GetString(...)

The full code is shown below

public static string GetDescription(this System.Enum value)
{
    string enumID = string.Empty;
    string enumDesc = string.Empty;
    try 
    {         
        // try to lookup Description attribute
        FieldInfo field = value.GetType().GetField(value.ToString());
        object[] attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), true);
        if (attribs.Length > 0)
        {
            enumID = ((DescriptionAttribute)attribs[0]).Description;
            enumDesc = TranslationHelper.GetTranslation(enumID);
        }
        if (string.IsNullOrEmpty(enumID) || TranslationHelper.IsTranslationMissing(enumDesc))
        {
            // try to lookup translation from EnumName_EnumValue
            string[] enumName = value.GetType().ToString().Split('.');
            enumID = string.Format("{0}_{1}", enumName[enumName.Length - 1], value.ToString());
            enumDesc = TranslationHelper.GetTranslation(enumID);
            if (TranslationHelper.IsTranslationMissing(enumDesc))
                enumDesc = string.Empty;
        }

        // try to format CamelCase to proper names
        if (string.IsNullOrEmpty(enumDesc))
        {
            Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled);
            enumDesc = capitalLetterMatch.Replace(value.ToString(), " 
amp;");
        }
    }
    catch (Exception)
    {
        // if any error, fallback to string value
        enumDesc = value.ToString();
    }

    return enumDesc;
}

I created a generic helper class based on Enum, which allow to bind the enum easily to DataSource

public class LocalizableEnum
{
    /// <summary>
    /// Column names exposed by LocalizableEnum
    /// </summary>
    public class ColumnNames
    {
        public const string ID = "EnumValue";
        public const string EntityValue = "EnumDescription";
    }
}

public class LocalizableEnum<T>
{

    private T m_ItemVal;
    private string m_ItemDesc;

    public LocalizableEnum(T id)
    {
        System.Enum idEnum = id as System.Enum;
        if (idEnum == null)
            throw new ArgumentException(string.Format("Type {0} is not enum", id.ToString()));
        else
        {
            m_ItemVal = id;
            m_ItemDesc = idEnum.GetDescription();
        }
    }

    public override string ToString()
    {
        return m_ItemDesc;
    }

    public T EnumValue
    {
        get { return m_ID; }
    }

    public string EnumDescription
    {
        get { return ToString(); }
    }

}

Then I created a generic static method that returns a List>, as below

public static List<LocalizableEnum<T>> GetEnumList<T>(object excludeMember)
{
    List<LocalizableEnum<T>> list =null;
    Array listVal = System.Enum.GetValues(typeof(T));
    if (listVal.Length>0)
    {
        string excludedValStr = string.Empty;
        if (excludeMember != null)
            excludedValStr = ((T)excludeMember).ToString();

        list = new List<LocalizableEnum<T>>();
        for (int i = 0; i < listVal.Length; i++)
        {
            T currentVal = (T)listVal.GetValue(i);
            if (excludedValStr != currentVal.ToString())
            {
                System.Enum enumVal = currentVal as System.Enum;
                LocalizableEnum<T> enumMember = new LocalizableEnum<T>(currentVal);
                list.Add(enumMember);
            }
        }
    }
    return list;
}

and a wrapper to return list with all members

public static List<LocalizableEnum<T>> GetEnumList<T>()
{
        return GetEnumList<T>(null);
}

Now let's put all things together and bind to actual combo:

// in module where we want to show items with all complexities
// or just filter on one complexity

comboComplexity.DisplayMember = LocalizableEnum.ColumnNames.EnumValue;
comboComplexity.ValueMember = LocalizableEnum.ColumnNames.EnumDescription;
comboComplexity.DataSource = EnumHelper.GetEnumList<Complexity>();
comboComplexity.SelectedValue = Complexity.AllComplexities;

// ....
// and here in edit module where we don't want to see "All Complexities"
comboComplexity.DisplayMember = LocalizableEnum.ColumnNames.EnumValue;
comboComplexity.ValueMember = LocalizableEnum.ColumnNames.EnumDescription;
comboComplexity.DataSource = EnumHelper.GetEnumList<Complexity>(Complexity.AllComplexities);
comboComplexity.SelectedValue = Complexity.VeryComplex; // set default value

To read selected the value and use it, I use code as below

Complexity selComplexity = (Complexity)comboComplexity.SelectedValue;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文