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

15

我有一个属性网格,用于让用户为编写成为应用程序插件的对象进行配置。我希望能够告诉编写插件的开发人员使用ComponentModel属性来管理他们的成员,如下所示:

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

以便它们像下面这样显示在属性网格列表中:

 DCIF
 CIF
 QCIF
 CIF4
 CIF2

连同它们可能携带的任何说明和显示名称。

似乎我只能对属性、事件和方法执行此操作。 有人知道如何在枚举中执行此操作吗?

4个回答

19

为了实现这个目标,您需要创建一个EnumConverter类,并使用TypeConverter属性装饰您的属性。

查看此在.NET中使用PropertyGrid的有趣示例:

想象一下,您希望列表中有超过两个项目。布尔类型不足够;您需要在枚举中为每个元素设置名称的描述属性。

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

超链接“在.NET中使用PropertyGrid”现在不正确。 - Eugene Maksimov
1
@EugeneMaksimov 谢谢。我更新了答案,并提取了用户发布的示例代码,以防链接再次失效。 - LarsTech
@LarsTech 这里是代码 https://gist.github.com/anonymous/05563e32ba52eec834d7225c01cce8ff 我已经遇到了这个问题。我做错了什么? - FrancescoDS
@FrancescoDS 我在 PropertyGrid 中得到了“Applica”和“Calcola Cerniere Plastiche”作为我的选项。所以你的代码对我有效。 - LarsTech
很抱歉,那是一个不同于我回答的WinForms PropertyGrid控件。我对WPF的经验非常有限。 - LarsTech
显示剩余8条评论

3
您可以将自定义TypeConverter实现附加到类型为枚举的属性上,并覆盖GetStandardValuesSupportedGetStandardValues以返回自定义项目列表,以显示在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实现中,你可以使用反射来提取各个枚举成员的DisplayNameAttribute(或DescriptionAttribute,这可能更适合此任务)属性,以显示该文本,而不是硬编码要显示的项目列表。

3

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

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

0

这个似乎也可以工作:

[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属性的组件会找到它,就我所知这是有效的。但是有没有什么理由说明这可能不是一个好主意?


我喜欢这个想法的简单性,但不幸的是它对我没用(我正在创建Visual Studio选项页面,而不是普通的PropertyGrids)。 - Cameron
这对我没有用。我正在使用Windows Forms属性网格,但它仍然只显示普通的枚举名称。 - uceumern

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