Подтвердить что ты не робот

Измените отображаемое имя элементов перечисления в PropertyGrid

У меня есть сетка свойств, которую я использую для пользователей, чтобы иметь возможность настраивать объекты для любого плагина, который написан для использования в моем приложении. Я хотел бы, чтобы разработчики могли писать плагины для использования атрибутов 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

Наряду с любыми описаниями и отображаемыми именами они могут иметь с собой.

Кажется, что я могу делать это только со свойствами, событиями и методами. Кто-нибудь знает, как я могу это сделать для перечисления?

4b9b3361

Ответ 1

Вам нужно будет создать класс EnumConverter и украсить ваше свойство атрибутом TypeConverter, чтобы сделать это.

Смотрите Используя PropertyGrid в .NET, это забавный пример:

Представьте, что вам нужно больше двух элементов в списке. Булевского типа недостаточно; вам нужно установить атрибуты описания с именем для каждого элемента в перечислении.

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

Ответ 2

Ответ, который я дал здесь Имеет рабочий пример этого. Вот конкретный код из этого примера, который вы хотите:

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

Ответ 3

Вы можете присоединить 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, которые могут быть более подходящими для этой задачи) атрибутов различных членов перечисления, чтобы показать этот текст вместо жесткого кодирования список элементов для показа.

Ответ 4

Это также работает:

[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 через Reflection, найдут один, и насколько я могу сказать, что это работает. Есть ли причина, почему это может быть плохой идеей?