编辑PropertyGrid中枚举成员的显示名称

本文关键字:显示 枚举成员 PropertyGrid 编辑 | 更新日期: 2023-09-27 18:08:31

我有一个属性网格,我正在使用的用户能够配置对象的任何插件是写在我的应用程序中使用。我希望能够告诉开发人员编写插件使用组件模型属性为他们的成员,像这样:

[CategoryAttribute("On Screen Display Settings"),
 DescriptionAttribute("Whether or not to show the session timer."),
 DisplayName("Show Session Timer")]
 public bool ShowTimer
 {
    get;
    set;
 }

这个效果很好。现在我希望枚举的成员也能够被编辑。例如

public enum Resolution_ : byte
{
    DCIF,
    CIF,
    QCIF,
    [DisplayName("4CIF")]
    CIF4,
    [DisplayName("2CIF")]
    CIF2
}

使它们像这样显示在PropertyGrid的列表中:

 DCIF
 CIF
 QCIF
 CIF4
 CIF2

以及它们可能具有的任何描述和显示名称。

似乎我只能用属性、事件和方法来做到这一点。有人知道我怎么对枚举做这个吗?

编辑PropertyGrid中枚举成员的显示名称

为了做到这一点,您必须创建一个EnumConverter类并使用TypeConverter属性来装饰您的属性。

在。net中使用PropertyGrid,这是一个有趣的例子:

假设您想在列表中包含两个以上的项。布尔类型是不够的;您需要为enum中的每个元素设置一个名称的Description属性。

enum DrinkDoses {
  [Description("Half of litre")]
  litre,
  [Description("One litre")]
  oneLitre,
  [Description("Two litres")]
  twoLitre,
  [Description("Three litres")]
  threeLitres,
  [Description("Four litres")]
  fourLitres,
  [Description("Death dose, five litres")]
  fiveLitres
}

在另一个类中,您需要使用EnumConverter类型。

class DrinkDosesConverter : EnumConverter {
  private Type enumType;
  public DrinkDosesConverter(Type type) : base(type) {
    enumType = type;
  }
  public override bool CanConvertTo(ITypeDescriptorContext context, Type destType) {
    return destType == typeof(string);
  }
  public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture,
                                   object value, Type destType) {
    FieldInfo fi = enumType.GetField(Enum.GetName(enumType, value));
    DescriptionAttribute dna = (DescriptionAttribute)Attribute.GetCustomAttribute(fi, 
                                typeof(DescriptionAttribute)); 
    if (dna != null)
      return dna.Description;
    else
      return value.ToString();
  }
  public override bool CanConvertFrom(ITypeDescriptorContext context, Type srcType) {
    return srcType == typeof(string);
  } 
  public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture,
                                     object value) {
    foreach (FieldInfo fi in enumType.GetFields()) {
      DescriptionAttribute dna = (DescriptionAttribute)Attribute.GetCustomAttribute(fi, 
                                  typeof(DescriptionAttribute)); 
      if ((dna != null) && ((string)value == dna.Description))
        return Enum.Parse(enumType, fi.Name);
    }
    return Enum.Parse(enumType, (string)value);
  }
}

第三,您需要设置属性TypeConverter以显示该属性。

class DrinkerDoses {
  DrinkDoses doses;
  [DisplayName("Doses")]
  [Description("Drinker doses")]
  [Category("Alcoholics drinking")]
  [TypeConverter(typeof(DrinkDosesConverter))]
  public DrinkDoses Doses {
    get { return doses; }
    set { doses = value; }
  }
  int dataInt; 
  public int DataInt {
    get { return dataInt; }
    set { dataInt = value; }
  }
}

您可以将自定义TypeConverter实现附加到枚举类型的属性上,并覆盖GetStandardValuesSupported和GetStandardValues,以返回要在PropertyGrid中的下拉列表中显示的项的自定义列表。然后,您可以覆盖ConvertFrom/ConvertTo方法来处理值与枚举类型之间的转换。

你可能还想重写GetStandardValuesExclusive,让它返回"true",这样用户就不能在属性值中输入任何东西。

就像这样:

public class MyTypeConverter : TypeConverter
{
  //Override GetStandardValuesExclusive, 
  //GetStandardValues and GetStandardValuesSupported
}
public class SomeClass
{
   [TypeConverter(typeof(MyTypeConverter))]
   public Resolution SomePropertry
   {
      ...
   }
}

在GetStandardValues/ConvertFrom/ConvertTo的实现中,您可以使用Reflection提取各种枚举成员的DisplayNameAttribute(或DescriptionAttribute,这可能更适合此任务)属性来显示该文本,而不是硬编码要显示的项列表

我在这里给出的答案有一个工作示例。下面是您想要的示例中的特定代码:

/// <summary>
/// This attribute is used to represent a string value
/// for a value in an enum.
/// </summary>
public class StringValueAttribute : Attribute {
    #region Properties
    /// <summary>
    /// Holds the stringvalue for a value in an enum.
    /// </summary>
    public string StringValue { get; protected set; }
    #endregion
    #region Constructor
    /// <summary>
    /// Constructor used to init a StringValue Attribute
    /// </summary>
    /// <param name="value"></param>
    public StringValueAttribute(string value) {
        this.StringValue = value;
    }
    #endregion
}
public static class MyExtension
{
    public static string GetStringValue(this Enum value)
    {
        // Get the type
        Type type = value.GetType();
        // Get fieldinfo for this type
        FieldInfo fieldInfo = type.GetField(value.ToString());
        // Get the stringvalue attributes
        StringValueAttribute[] attribs = fieldInfo.GetCustomAttributes(
            typeof(StringValueAttribute), false) as StringValueAttribute[];
        // Return the first if there was a match.
        return attribs.Length > 0 ? attribs[0].StringValue : null;
    }
    public static String[] GetEnumNames(Type t)
    {
        Array enumValueArray= Enum.GetValues(t);
        string[] enumStrings = new String[enumValueArray.Length];
        for(int i = 0; i< enumValueArray.Length; ++i)
        {
            enumStrings[i] = GetStringValue((test)enumValueArray.GetValue(i));
        }
        return enumStrings;
    }
}
enum test
{
    [StringValue("test ONE")]
    test1,
    [StringValue("test TWO")]
    test2
}

这似乎也起作用了:

[AttributeUsage(AttributeTargets.Field)]
public class EnumDisplayNameAttribute : System.ComponentModel.DisplayNameAttribute
{
    public EnumDisplayNameAttribute(string data) : base(data) { }
}
public enum Resolution_ : byte
{
    DCIF,
    CIF,
    QCIF,
    [EnumDisplayName("4CIF")]
    CIF4,
    [EnumDisplayName("2CIF")]
    CIF2
}

组件通过反射寻找一个DisplayName属性将找到一个,据我所知,这是有效的。有什么理由证明这是个坏主意吗?