65.9K
CodeProject 正在变化。 阅读更多。
Home

枚举的 DisplayNameAttribute

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.67/5 (12投票s)

2008年7月24日

CPOL

3分钟阅读

viewsIcon

72042

downloadIcon

1184

一篇关于为.NET中枚举字段创建DisplayNameAttribute的文章,该值将显示在PropertyGrid中。

FieldDisplayNameAttribute.gif

引言

.NET Framework 缺少用于枚举字段成员的 DisplayNameAttribute 类。 DisplayNameAttribute 类可以用于类属性,但它不支持字段的显示名称。在 PropertyGrid 控件中,对于具有枚举类型的属性,使用一些有意义的值在组合框中为最终用户对话框显示比较困难。 将会显示原始值,而这些值仅对源代码的开发人员有用。

Using the Code

首先,我们从属性类开始。它非常简单,因为我们使用 .NET 类 DisplayNameAttribute 作为基类,该类提供了所有需要的功能。我们唯一需要做的更改是将 AttributeTargets 设置为 field。

[System.AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class FieldDisplayNameAttribute : System.ComponentModel.DisplayNameAttribute
...

新的属性类由应该很好地显示的枚举使用。

public enum Driver
{
   /// <summary>
   /// unknown
   /// </summary>

   [FieldDisplayName("Not defined")]
   Unknown,

   /// <summary>
   /// driver for MS SQL Server
   /// </summary>

   [FieldDisplayName(".Net Provider for Microsoft SQL Server")]
   MsSqlClient,
...

目前,PropertyGrid 控件无法识别新的属性类,也不会显示显示名称。我们需要一个新的 TypeConverter,它可以转换枚举的值和显示名称。

/// <summary>
/// TypeConverter for enum types which supports the FieldDisplayName-attribute
/// </summary>

public class EnumTypeConverter : EnumConverter
{
...

EnumTypeConverter 类支持所有枚举类型和字符串类型之间的转换。

public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
   //
   // we support converting between string type an enum
   //
   if (sourceType == typeof(string))
      return true;
   return base.CanConvertFrom(context, sourceType);
}

public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
   //
   // we support converting between string type an enum
   //
   if (destinationType == typeof(string))
      return true;
   return base.CanConvertTo(context, destinationType);
}

通过映射表进行翻译,该映射表在每次需要时为每种枚举类型构建一次。映射表通过反射构建。代码迭代枚举的字段成员并查找 FieldDisplayNameAttribute。如果缺少该属性,则转换器使用 ToString() 值进行映射。

第二个版本的 EnumTypeConverter 添加了对资源管理器的支持。您可以为不同的语言通过资源文件提供显示名称。资源文件中的定义会覆盖 FieldDisplayNameAttribute 的值。资源字符串的键必须是枚举类型的完整类型名称,后跟“_<枚举值>”。完整类型名称中的点必须替换为下划线。示例:System_Windows_Forms_BorderStyle_None

public override object ConvertTo(ITypeDescriptorContext context,
    System.Globalization.CultureInfo culture, object value, Type destinationType)
{
  object result = value;

   //
   // source value have to be a enumeration type or string, destination a string type
   //
   if (destinationType == typeof(string) &&
       value != null)
   {
      if (value.GetType().IsEnum)
      {
         // ensure that the mapping table is available for the enumeration type
         EnsureMappingsAvailable(value.GetType(), culture);
         MappingContainer container = mappings[value.GetType()];
         MappingPerCulture mapping = container.mappingsPerCulture[culture];
         string valueStr = value.ToString();

         if (mapping.fieldDisplayNameFound)
         {
            if (valueStr.IndexOf(',') < 0)
            {
               // simple enum value
               if (mapping.mappings.ContainsKey(valueStr))
               {
                  result = mapping.mappings[valueStr];
               }
               else
               {
                  throw GetConvertToException(valueStr, destinationType);
               }
            }
            else
            {
               // flag enum with more then one enum value
               string[] parts = valueStr.Split(new string[] { ", " },
                   StringSplitOptions.RemoveEmptyEntries);
               System.Text.StringBuilder builder = new System.Text.StringBuilder();
               string tmp;
               for (int index = 0; index < parts.Length; index++)
               {
                  tmp = parts[index];
                  if (mapping.mappings.ContainsKey(tmp))
                  {
                     builder.Append(mapping.mappings[tmp]);
                     builder.Append(", ");
                  }
                  else
                  {
                     throw GetConvertToException(valueStr, destinationType);
                  }
               }

               builder.Length -= 2;
               result = builder.ToString();
            }
         }
         else
         {
            result = value.ToString();
         }
      }
   }
   else
   {
      result = base.ConvertTo(context, culture, value, destinationType);
   }

   return result;
}

但是我们还想要 PropertyGrid 控件中的组合框。如果未以正确的方式扩展 EnumTypeConverter,则它不存在。对于组合框,PropertyGrid 控件需要一些标准值。 TypeConverter 支持针对该问题的三种方法。第一种方法通常告诉该类型是否支持标准值。当然,所有枚举都有标准值。

public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
   //
   // enumerations support standard values which are a list of the fields of
   // the enumeration
   //
   return true;
}

第二种方法说明这些值是否为互斥值。所有枚举值都是互斥的。除了这些值之外,不会有其他值。

public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
   //
   // all standard values of an enumeration are exclusiv values, no others are allowed
   //
   return true;
}

最重要的方法提供了标准值的列表。该列表必须包括枚举的原始值,而不是已翻译的显示名称值。这一点很重要,因为标准值的翻译以正常方式进行。

public override TypeConverter.StandardValuesCollection GetStandardValues(
    ITypeDescriptorContext context)
{
   // ensure that the mapping table is available for the enumeration type
   // it builds the standard value collection too
   EnsureMappingsAvailable(EnumType, CultureInfo.CurrentCulture);
   MappingContainer container = mappings[EnumType];
   // wrap it with the right type. it is also possible to use Enum.GetValues
   TypeConverter.StandardValuesCollection values = new StandardValuesCollection(
       container.standardValues);
   return values;
}

现在我们只有一件事要做。我们必须用一个属性来修饰我们的枚举示例,该属性告诉所有感兴趣的控件和类使用我们的类型转换器类。

[TypeConverter(typeof(EnumTypeConverter))]
public enum Driver
{
...

从该示例的第 2 版开始,您可以使用 EnumTypeDescriptionProvider。创建一个实例,EnumTypeConverter 将用于每个枚举类型,而无需 TypeConverter 属性。

就这样。一个带有 PropertyGrid 控件的示例对话框和一个使用枚举 Driver 的示例类完成了该示例。

结论

如果您想使用该解决方案,则只需执行以下操作

  • 将类 FieldDisplayNameAttributeEnumTypeConverter 添加到您的项目中
  • 将属性 [TypeConverter(typeof(EnumTypeConverter))] 添加到您的枚举中
  • 将属性 [FieldDisplayName("任何有意义的显示名称")] 添加到枚举的字段中

对于扩展版本,请使用以下步骤

  • 将所有必要的类添加到您的项目中
  • 创建 EnumTypeDescriptionProvider 的一个实例
  • 通过调用函数 EnumTypeConverter.RegisterResourceManager 注册 ResourceManager

历史

版本 2

  • public class EnumTypeConverter : TypeConverter 更改为 public class EnumTypeConverter : EnumConverter
  • 修复了对 Flags 枚举的支持
  • 为枚举添加了 TypeDescriptor,该描述符添加了对没有 TypeConverter/FieldDisplayName 属性的枚举的支持
  • 添加了对资源管理器和不同语言的支持

版本 1

  • 首次发布
© . All rights reserved.