列挙値のカスタム文字列フォーマットを使用して列挙バインドコンボボックスを作成するにはどうすればよいですか?

2009年04月28日に質問されました。  ·  閲覧回数 85.3k回  ·  ソース

Shalom Craimer picture
2009年04月28日

ポストEnumToStringでは、次のようにカスタム属性DescriptionAttributeを使用するメソッドが説明されています。

Enum HowNice {
  [Description("Really Nice")]
  ReallyNice,
  [Description("Kinda Nice")]
  SortOfNice,
  [Description("Not Nice At All")]
  NotNice
}

次に、次のような構文を使用して、関数GetDescriptionを呼び出します。

GetDescription<HowNice>(NotNice); // Returns "Not Nice At All"

しかし、ComboBoxにGetDescriptionを呼び出すように強制することはできないため、ComboBoxに列挙型の値を単純に入力したい場合はあまり役に立ちません。

私が欲しいものには、次の要件があります。

  • (HowNice)myComboBox.selectedItemを読み取ると、選択した値が列挙値として返されます。
  • ユーザーには、列挙値の名前だけでなく、使いやすい表示文字列が表示されます。 したがって、「 NotNice 」が表示される代わりに、ユーザーには「 Not Nice At All 」が表示されます。
  • うまくいけば、ソリューションは既存の列挙への最小限のコード変更を必要とします。

明らかに、作成する列挙型ごとに新しいクラスを実装し、そのToString()をオーバーライドすることもできますが、それは列挙型ごとに多くの作業であり、むしろ避けたいと思います。

何か案は?

一体、私は賞金として抱擁を投げます:-)

回答

Anton Gogolev picture
2009年04月28日
86

ComboBoxは、必要なものがすべて含まれています。 trueに設定する必要があるFormattingEnabledプロパティと、必要な書式を設定する必要があるFormatイベントです。論理。 したがって、

myComboBox.FormattingEnabled = true;
myComboBox.Format += delegate(object sender, ListControlConvertEventArgs e)
    {
        e.Value = GetDescription<HowNice>((HowNice)e.Value);
    }
Sander picture
2009年04月28日
47

しないでください! 列挙型はプリミティブであり、UIオブジェクトではありません-.ToString()でUIを提供するようにすると、設計の観点からはかなり悪いでしょう。 ここで間違った問題を解決しようとしています。本当の問題は、Enum.ToString()をコンボボックスに表示したくないということです。

今、これは確かに非常に解決可能な問題です! コンボボックスアイテムを表すUIオブジェクトを作成します。

sealed class NicenessComboBoxItem
{
    public string Description { get { return ...; } }
    public HowNice Value { get; private set; }

    public NicenessComboBoxItem(HowNice howNice) { Value = howNice; }
}

次に、このクラスのインスタンスをコンボボックスのItemsコレクションに追加し、次のプロパティを設定します。

comboBox.ValueMember = "Value";
comboBox.DisplayMember = "Description";
sisve picture
2009年04月28日
43

指定された属性を読み取り、リソースでそれらを検索するTypeConverterを作成できます。 したがって、それほど面倒なことなく、表示名の多言語サポートを利用できます。

TypeConverterのConvertFrom / ConvertToメソッドを調べ、リフレクションを使用して列挙型フィールドの属性を読み取ります。

Shalom Craimer picture
2009年04月28日
42

TypeConverter。 これが私が探していたものだと思います。 サイモン・スヴェンソンを歓迎します!

[TypeConverter(typeof(EnumToStringUsingDescription))]
Enum HowNice {
  [Description("Really Nice")]
  ReallyNice,
  [Description("Kinda Nice")]
  SortOfNice,
  [Description("Not Nice At All")]
  NotNice
}

現在の列挙型で変更する必要があるのは、宣言の前にこの行を追加することだけです。

[TypeConverter(typeof(EnumToStringUsingDescription))]

これを行うと、列挙型はそのフィールドのDescriptionAttributeを使用して表示されます。

ああ、そしてTypeConverterは次のように定義されます:

public class EnumToStringUsingDescription : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return (sourceType.Equals(typeof(Enum)));
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return (destinationType.Equals(typeof(String)));
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        return base.ConvertFrom(context, culture, value);
    }

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if (!destinationType.Equals(typeof(String)))
        {
            throw new ArgumentException("Can only convert to string.", "destinationType");
        }

        if (!value.GetType().BaseType.Equals(typeof(Enum)))
        {
            throw new ArgumentException("Can only convert an instance of enum.", "value");
        }

        string name = value.ToString();
        object[] attrs = 
            value.GetType().GetField(name).GetCustomAttributes(typeof(DescriptionAttribute), false);
        return (attrs.Length > 0) ? ((DescriptionAttribute)attrs[0]).Description : name;
    }
}

これは私のComboBoxの場合に役立ちますが、明らかに実際にはToString()オーバーライドしません。 その間に私はこれで解決すると思います...

Tyler Durden picture
2012年08月01日
34

列挙例の使用:

using System.ComponentModel;

Enum HowNice
{
    [Description("Really Nice")]
    ReallyNice,
    [Description("Kinda Nice")]
    SortOfNice,
    [Description("Not Nice At All")]
    NotNice
}

拡張機能を作成する:

public static class EnumExtensions
{
    public static string Description(this Enum value)
    {
        var enumType = value.GetType();
        var field = enumType.GetField(value.ToString());
        var attributes = field.GetCustomAttributes(typeof(DescriptionAttribute),
                                                   false);
        return attributes.Length == 0
            ? value.ToString()
            : ((DescriptionAttribute)attributes[0]).Description;
    }
}

次に、次のようなものを使用できます。

HowNice myEnum = HowNice.ReallyNice;
string myDesc = myEnum.Description();

詳細については、 http参照してください。 解決策はRichrdCarrにクレジットされます

Guffa picture
2009年04月28日
8

説明のあるすべての列挙型に使用できる汎用構造体を作成できます。 クラスとの間の暗黙的な変換を使用しても、ToStringメソッドを除いて、変数は列挙型のように機能します。

public struct Described<T> where T : struct {

    private T _value;

    public Described(T value) {
        _value = value;
    }

    public override string ToString() {
        string text = _value.ToString();
        object[] attr =
            typeof(T).GetField(text)
            .GetCustomAttributes(typeof(DescriptionAttribute), false);
        if (attr.Length == 1) {
            text = ((DescriptionAttribute)attr[0]).Description;
        }
        return text;
    }

    public static implicit operator Described<T>(T value) {
        return new Described<T>(value);
    }

    public static implicit operator T(Described<T> value) {
        return value._value;
    }

}

使用例:

Described<HowNice> nice = HowNice.ReallyNice;

Console.WriteLine(nice == HowNice.ReallyNice); // writes "True"
Console.WriteLine(nice); // writes "Really Nice"
jjnguy picture
2009年04月28日
5

これを行う最良の方法は、クラスを作成することです。

class EnumWithToString {
    private string description;
    internal EnumWithToString(string desc){
        description = desc;
    }
    public override string ToString(){
        return description;
    }
}

class HowNice : EnumWithToString {

    private HowNice(string desc) : base(desc){}

    public static readonly HowNice ReallyNice = new HowNice("Really Nice");
    public static readonly HowNice KindaNice = new HowNice("Kinda Nice");
    public static readonly HowNice NotVeryNice = new HowNice("Really Mean!");
}

それが最善の方法だと思います。

コンボボックスに詰め込むと、きれいなToStringが表示され、クラスのインスタンスをこれ以上作成できないという事実により、基本的に列挙型になります。

ps構文を少し修正する必要があるかもしれませんが、C#はあまり得意ではありません。 (Java男)

Marc Gravell picture
2009年04月28日
5

単に別のタイプにバインドせずにそれを行うことはできないと思います-少なくとも、便利ではありません。 通常、 ToString()制御できない場合でも、 TypeConverterを使用してカスタムフォーマットを行うことができますが、IIRCのSystem.ComponentModelものは列挙型に対してこれを尊重しません。

説明のstring[] 、または本質的にキーと値のペアのようなものにバインドできますか? (説明/値)-次のようなもの:

class EnumWrapper<T> where T : struct
{
    private readonly T value;
    public T Value { get { return value; } }
    public EnumWrapper(T value) { this.value = value; }
    public string Description { get { return GetDescription<T>(value); } }
    public override string ToString() { return Description; }

    public static EnumWrapper<T>[] GetValues()
    {
        T[] vals = (T[])Enum.GetValues(typeof(T));
        return Array.ConvertAll(vals, v => new EnumWrapper<T>(v));
    }
}

そして、 EnumWrapper<HowNice>.GetValues()バインドします

Bj&#246;rn picture
2009年04月28日
3

C#の列挙型のToString()をオーバーライドすることはできません。 ただし、拡張メソッドを使用できます。

public static string ToString(this HowNice self, int neverUsed)
{
    switch (self)
    {
        case HowNice.ReallyNice:
            return "Rilly, rilly nice";
            break;
    ...

もちろん、メソッドを明示的に呼び出す必要があります。

HowNice.ReallyNice.ToString(0)

これは、switchステートメントとすべてを備えた優れたソリューションではありませんが、うまくいくはずであり、多くの書き直しがうまくいけば...

Richard Szalay picture
2009年04月28日
3

列挙型ごとにクラスを作成したくない場合は、列挙型の値/表示テキストの辞書を作成し、代わりにそれをバインドすることをお勧めします。

これは、元の投稿のGetDescriptionメソッドメソッドに依存していることに注意してください。

public static IDictionary<T, string> GetDescriptions<T>()
    where T : struct
{
    IDictionary<T, string> values = new Dictionary<T, string>();

    Type type = enumerationValue.GetType();
    if (!type.IsEnum)
    {
        throw new ArgumentException("T must be of Enum type", "enumerationValue");
    }

    //Tries to find a DescriptionAttribute for a potential friendly name
    //for the enum
    foreach (T value in Enum.GetValues(typeof(T)))
    {
        string text = value.GetDescription();

        values.Add(value, text);
    }

    return values;
}