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

发布于 2024-12-04 09:17:29 字数 678 浏览 1 评论 0原文

我有一个属性网格,用户可以使用它来为我的应用程序中使用的任何插件配置对象。我希望能够告诉编写插件的开发人员为其成员使用 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
}

,以便它们显示在 PropertyGrid 的列表中,如下所示:

 DCIF
 CIF
 QCIF
 CIF4
 CIF2

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

看来我只能用属性、事件和方法来做到这一点。有谁知道我该如何进行枚举?

I have a property grid that I am using for users to be able to configure objects for any plugin that is written to be used in my application. I would like to be able to tell developers writing plugins to use the ComponentModel Attributes for their members like so:

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

This works great. Now I would like for the members of an enumeration to be able to be edited as well. i.e.

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

So that they are displayed in the PropertyGrid's list like so:

 DCIF
 CIF
 QCIF
 CIF4
 CIF2

Along with any Descriptions and Display names they may have with them.

It seems that I can only do this with properties, events, and methods. Does anyone know how I can do this for an enumeration?

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

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

发布评论

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

评论(4

木緿 2024-12-11 09:17:29

您必须创建一个 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; }
  }
}

You will have to make an EnumConverter class and decorate your property with a TypeConverter attribute in order to do this.

See this Using PropertyGrid in .NET, it's a fun example:

Imagine that you want more than two items in list. The boolean type is not enough; you need to set Description attributes with a name for every element in enum.

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
}

In another class you need to utilize the type 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);
  }
}

Third, you need set the attribute TypeConverter for displaying the property.

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; }
  }
}
救赎№ 2024-12-11 09:17:29

您可以将自定义 TypeConverter 实现附加到其属性type 是您的枚举并覆盖 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 的实现中,您可以使用 Reflection 提取各种枚举成员的 DisplayNameAttribute(或 DescriptionAttribute,可能更适合此任务)属性来显示文本而不是对要显示的项目列表进行硬编码。

You can attach a custom TypeConverter implementation to the property whose type is your enumeration and override the GetStandardValuesSupported and GetStandardValues to return a custom list of items to show in the drop-down list in the PropertyGrid. You can then override ConvertFrom/ConvertTo methods to handle converting values to/from your enumeration type.

You may also want to override GetStandardValuesExclusive and have it return "true" so the user can't type anything into the property value.

So, something like this:

public class MyTypeConverter : TypeConverter
{
  //Override GetStandardValuesExclusive, 
  //GetStandardValues and GetStandardValuesSupported
}

public class SomeClass
{
   [TypeConverter(typeof(MyTypeConverter))]
   public Resolution SomePropertry
   {
      ...
   }
}

In your implementation of GetStandardValues/ConvertFrom/ConvertTo you could then use Reflection to pull out the DisplayNameAttribute (or DescriptionAttribute, which may be more suited to this task) attributes of the various enum members to show that text instead of hard-coding the list of items to show.

只有影子陪我不离不弃 2024-12-11 09:17:29

我在此处给出的答案有一个有效的示例。以下是您想要的该示例中的特定代码:

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

The answer I gave here Has a working example of this. Here is the specific code from that example that you want:

/// <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
}
绮筵 2024-12-11 09:17:29

这似乎也有效:

[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 属性的组件会找到一个,据我所知,这是有效的。有理由认为这可能是一个坏主意吗?

This also seems to work:

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

Components looking for a DisplayName attribute via Reflection will find one, and as far as I can tell this works. Is there a reason why this might be a bad idea?

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