列挙値のカスタム文字列フォーマットを使用して、列挙型にバインドされたコンボボックスをどのように作成しますか?


135

Enum ToStringの投稿では、次の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にを強制的に呼び出すことはできないため、列挙型の値をComboBoxに単純に入力する場合GetDescriptionは、実際には役に立ちません。

私が欲しいものは次の要件があります:

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

もちろん、作成した列挙ごとに新しいクラスを実装し、そのをオーバーライドすることもできますがToString()、それは列挙ごとに多くの作業を行うため、避けたいと思います。

何か案は?

一体、私は恵みとして抱擁を投げ入れます:-)


1
jjnguyはJava enumがこれをうまく解決する(javahowto.blogspot.com/2006/10/…)のは正しいですが、それは疑わしい関連性があります。
Matthew Flaschen、2009

8
Java Enumは冗談です。多分彼らは2020年に物件を追加するでしょう:/
チャドグラント

より軽量な(ただし、おそらく堅牢性が低い)ソリューションについては、私のスレッドを参照しください。
Gutblender

回答:


42

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

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


OK、私はいくつかのコードを書きました(この質問に対する私の回答を参照してください)-それで十分だと思いますか、何か不足していますか?
シャロームクレイマー2009

1
良いですね。全体的には優れていますが、一般的なソフトウェアでは決してグローバル化されることはありません。(私は知っています、その仮定は後で間違っていることが判明します。
;

85

ComboBoxあなたが必要なものがすべてしている:FormattingEnabledあなたが設定する必要があり財産、trueFormatあなたは配置する必要がありますイベントは、ロジックをフォーマットたいです。したがって、

myComboBox.FormattingEnabled = true;
myComboBox.Format += delegate(object sender, ListControlConvertEventArgs e)
    {
        e.Value = GetDescription<HowNice>((HowNice)e.Value);
    }

これはデータバインドされたコンボボックスでのみ機能しますか?それ以外の場合は、Formatイベントを発生させることができません。
SomethingBetter

ここでの唯一の問題は、リストをロジックでソートできないことです
GorillaApe

これは素晴らしいソリューションです。でも動作するようにそれが必要になりDataGridComboBoxColumnます。それを解決する機会はありますか?私はへのアクセスを取得する方法を見つけることができないんだComboBoxのをDataGridComboBoxColumn
Soko

46

やめて!列挙型はプリミティブであり、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";

1
私は心から同意します。また、ToString()の結果をUIに公開しないでください。そして、あなたはローカリゼーションを取得しません。
ØyvindSkaar

これは古いことはわかっていますが、どう違うのですか?
nportelli 2013

2
カスタムクラスを使用する代わりに、列挙型の値をaにマップし、およびプロパティをand として使用する同様のソリューションを確認しました。DictionaryKeyValueDisplayMemberValueMember
ジェフB

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()。その間私はこれで解決すると思います...


3
Enum-> Stringを処理していますが、完全な実装が必要な場合は、Enum> InstanceDescriptor、およびString-> Enumも必要です。しかし、私はそれを表示することは現時点であなたのニーズには十分だと思います。;)
sisve

1
このソリューションは、残念ながら説明が静的な場合にのみ機能します。
Llyle、2010

ちなみに、TypeConverterの使用は静的記述にバインドされていないため、coverterは属性以外のソースから値を入力できます。
ドミトリータシキノフ2010

3
私の髪を数時間引っ張っていますが、単純なコンソールアプリでもまだ機能しないようです。enumを[TypeConverter(typeof(EnumToStringUsingDescription))] public enum MyEnum {[Description("Blah")] One}で装飾し、試してみましたが、Console.WriteLine(MyEnum.One)まだ「One」として表示されます。あなたはTypeDescriptor.GetConverter(typeof(MyEnum)).ConvertToString(MyEnum.One)(うまくいく)のような特別な魔法が必要ですか?
ダブ

1
@scraimerフラグをサポートするコードのバージョンを投稿しました。すべての権利はあなたに帰属します...
Avi Turner

32

列挙の例を使用する:

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//www.blackwasp.co.uk/EnumDescription.aspx参照してください。クレジットは、Richrd Carrにソリューションを提供します


参照先のサイトで詳細を確認し、次のように使用しましたが、 'string myDesc = HowNice.ReallyNice.Description();'のように簡単に見えます。myDescはReally Nice
Ananda

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"

5

単に別のタイプにバインドしないと、それができるとは思いません。少なくとも、便利ではありません。通常、を制御できない場合でも、ToString()a TypeConverterを使用してカスタムの書式設定を行うことができます。ただし、IIRCのSystem.ComponentModelスタッフはこれを列挙型で考慮しません。

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

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()


1
「GetDescription」という名前は現在のコンテキストには存在しません。.NET 4.0を使用しているイム
Muhammad Adeel Zahid

@MuhammadAdeelZahidは質問の始まりをよく見てください-これはリンクされた投稿から来ています:stackoverflow.com/questions/479410/enum-tostring
Marc Gravell

申し訳ありませんが、質問から手掛かりを得ることができません。メソッドはコンパイルされておらず、エラーが表示されます。
Muhammad Adeel Zahid

こんにちはマーク、私はあなたのアイデアを試しました。それは機能していますが、それ自体theComboBox.SelectItemEnumWrapper<T>はなくのタイプですT。スクレイマーが欲しいと思うReading (HowNice)myComboBox.selectedItem will return the selected value as the enum value.
Peter Lee

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#にはあまり上手ではありません。(ジャワ男)


1
これはコンボボックスの問題にどのように役立ちますか?
peSHIr 2009

さて、今、新しいオブジェクトがコンボボックスに置かれると、そのToStringは適切に表示され、クラスはまだ列挙型のように機能します。
jjnguy 2009

1
私の答えにもなっていただろう。
Mikko Rantanen、

3
そして、元のポスターが明示的にクラスを望んでいなかった方法を見てください。クラスはそれ以上の仕事だとは思いません。すべての列挙型の説明と親クラスにToStringオーバーライドを抽象化できます。この後必要なのは、コンストラクターprivate HowNice(String desc) : base(desc) { }と静的フィールドだけです。
Mikko Rantanen、

これを回避することを望んでいました。なぜなら、私が作成するすべての列挙には独自のクラスが必要になるからです。ああ。
シャロームクレイマー2009

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

良いアイデア。しかし、コンボボックスでこれをどのように使用しますか?ユーザーがコンボボックスからアイテムを選択したら、どのアイテムを選択したかをどのようにして知ることができますか?説明文字列で検索しますか?それは私の皮膚のかゆみを
引き起こし

選択されたアイテムのキーは実際の列挙値になります。また、説明文字列を衝突させないでください-ユーザーはどのように違いを知るのですか?
Richard Szalay、

<cont>衝突する説明文字列がある場合は、列挙型の値を直接コンボボックスにバインドしないでください。
Richard Szalay、

えっと、コンボボックスにアイテムを追加する方法のサンプルコードを教えてもらえますか?私が考えることができるのは、「foreach(descriptionsDict.Valuesの文字列s){this.combobox.Items.Add(s);}」だけです
Shalom Craimer

1
ComboBox.DataSource =辞書;
Richard Szalay、

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ステートメントなどを使用した優れたソリューションではありません。


enumにバインドするコントロールはこの拡張メソッドを呼び出さず、デフォルトの実装を呼び出すことに注意してください。
Richard Szalay、

正しい。したがって、どこかで説明が必要な場合、これは実行可能なオプションです。コンボボックスの問題の原因にはなりません。
peSHIr 2009

より大きな問題は、これが(拡張メソッドとして)呼び出されないことです。すでに存在するインスタンスメソッドが常に優先されます。
Marc Gravell

もちろん、マークは正しいです(いつものように?)。私の.NETエクスペリエンスは最小限ですが、メソッドにダミーパラメーターを提供することでうまくいくはずです。回答を編集しました。
ビョルン

2

@scraimerの回答に続き、フラグをサポートするenum-to-string型コンバーターのバージョンを次に示します。

    /// <summary>
/// A drop-in converter that returns the strings from 
/// <see cref="System.ComponentModel.DescriptionAttribute"/>
/// of items in an enumaration when they are converted to a string,
/// like in ToString().
/// </summary>
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)))
        {
            string name = value.ToString();
            Type effectiveType = value.GetType();          

            if (name != null)
            {
                FieldInfo fi = effectiveType.GetField(name);
                if (fi != null)
                {
                    object[] attrs =
                    fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
                    return (attrs.Length > 0) ? ((DescriptionAttribute)attrs[0]).Description : name;
                }

            }
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }

    /// <summary>
    /// Coverts an Enums to string by it's description. falls back to ToString.
    /// </summary>
    /// <param name="value">The value.</param>
    /// <returns></returns>
    public string EnumToString(Enum value)
    {
        //getting the actual values
        List<Enum> values = EnumToStringUsingDescription.GetFlaggedValues(value);
        //values.ToString();
        //Will hold results for each value
        List<string> results = new List<string>();
        //getting the representing strings
        foreach (Enum currValue in values)
        {
            string currresult = this.ConvertTo(null, null, currValue, typeof(String)).ToString();;
            results.Add(currresult);
        }

        return String.Join("\n",results);

    }

    /// <summary>
    /// All of the values of enumeration that are represented by specified value.
    /// If it is not a flag, the value will be the only value retured
    /// </summary>
    /// <param name="value">The value.</param>
    /// <returns></returns>
    private static List<Enum> GetFlaggedValues(Enum value)
    {
        //checking if this string is a flaged Enum
        Type enumType = value.GetType();
        object[] attributes = enumType.GetCustomAttributes(true);
        bool hasFlags = false;
        foreach (object currAttibute in attributes)
        {
            if (enumType.GetCustomAttributes(true)[0] is System.FlagsAttribute)
            {
                hasFlags = true;
                break;
            }
        }
        //If it is a flag, add all fllaged values
        List<Enum> values = new List<Enum>();
        if (hasFlags)
        {
            Array allValues = Enum.GetValues(enumType);
            foreach (Enum currValue in allValues)
            {
                if (value.HasFlag(currValue))
                {
                    values.Add(currValue);
                }
            }



        }
        else//if not just add current value
        {
            values.Add(value);
        }
        return values;
    }

}

そしてそれを使うための拡張メソッド:

    /// <summary>
    /// Converts an Enum to string by it's description. falls back to ToString
    /// </summary>
    /// <param name="enumVal">The enum val.</param>
    /// <returns></returns>
    public static string ToStringByDescription(this Enum enumVal)
    {
        EnumToStringUsingDescription inter = new EnumToStringUsingDescription();
        string str = inter.EnumToString(enumVal);
        return str;
    }

1

任意のタイプで使用する汎用クラスを作成します。私は過去にこのようなものを使用しました:

public class ComboBoxItem<T>
{
    /// The text to display.
    private string text = "";
    /// The associated tag.
    private T tag = default(T);

    public string Text
    {
        get
        {
            return text;
        }
    }

    public T Tag
    {
        get
        {
            return tag;
        }
    }

    public override string ToString()
    {
        return text;
    }

    // Add various constructors here to fit your needs
}

この上に、静的な「ファクトリメソッド」を追加して、列挙型を指定したコンボボックス項目のリストを作成できます(そこにあるGetDescriptionsメソッドとほぼ同じです)。これにより、列挙型ごとに1つのエンティティを実装する必要がなくなり、「GetDescriptions」ヘルパーメソッドに適切な/論理的な場所が提供されます(個人的にはFromEnum(T obj)と呼びます...


1

必要なものを含むコレクションを作成します(列挙値Valueを含むプロパティを含む単純なオブジェクトHowNiceや、そのコレクションへのコンボをDescription含むGetDescription<HowNice>(Value)データバインドプロパティなど)。

このようなビット:

Combo.DataSource = new EnumeratedValueCollection<HowNice>();
Combo.ValueMember = "Value";
Combo.DisplayMember = "Description";

次のようなコレクションクラスがある場合:

using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Whatever.Tickles.Your.Fancy
{
    public class EnumeratedValueCollection<T> : ReadOnlyCollection<EnumeratedValue<T>>
    {
        public EnumeratedValueCollection()
            : base(ListConstructor()) { }
        public EnumeratedValueCollection(Func<T, bool> selection)
            : base(ListConstructor(selection)) { }
        public EnumeratedValueCollection(Func<T, string> format)
            : base(ListConstructor(format)) { }
        public EnumeratedValueCollection(Func<T, bool> selection, Func<T, string> format)
            : base(ListConstructor(selection, format)) { }
        internal EnumeratedValueCollection(IList<EnumeratedValue<T>> data)
            : base(data) { }

        internal static List<EnumeratedValue<T>> ListConstructor()
        {
            return ListConstructor(null, null);
        }

        internal static List<EnumeratedValue<T>> ListConstructor(Func<T, string> format)
        {
            return ListConstructor(null, format);
        }

        internal static List<EnumeratedValue<T>> ListConstructor(Func<T, bool> selection)
        {
            return ListConstructor(selection, null);
        }

        internal static List<EnumeratedValue<T>> ListConstructor(Func<T, bool> selection, Func<T, string> format)
        {
            if (null == selection) selection = (x => true);
            if (null == format) format = (x => GetDescription<T>(x));
            var result = new List<EnumeratedValue<T>>();
            foreach (T value in System.Enum.GetValues(typeof(T)))
            {
                if (selection(value))
                {
                    string description = format(value);
                    result.Add(new EnumeratedValue<T>(value, description));
                }
            }
            return result;
        }

        public bool Contains(T value)
        {
            return (Items.FirstOrDefault(item => item.Value.Equals(value)) != null);
        }

        public EnumeratedValue<T> this[T value]
        {
            get
            {
                return Items.First(item => item.Value.Equals(value));
            }
        }

        public string Describe(T value)
        {
            return this[value].Description;
        }
    }

    [System.Diagnostics.DebuggerDisplay("{Value} ({Description})")]
    public class EnumeratedValue<T>
    {
        private T value;
        private string description;
        internal EnumeratedValue(T value, string description) {
            this.value = value;
            this.description = description;
        }
        public T Value { get { return this.value; } }
        public string Description { get { return this.description; } }
    }

}

ご覧のとおり、このコレクションはラムダを使用して簡単にカスタマイズでき、列挙子のサブセットを選択したり、言及stringしたGetDescription<T>(x)関数を使用する代わりにカスタム書式を実装したりできます。


すばらしいですが、コードでさらに少ない作業が必要なものを探しています。
シャロームクレイマー2009

すべての列挙子のこの種のものに同じジェネリックコレクションを使用できるとしても、もちろん、列挙型ごとにそのようなコレクションをカスタムで作成することはお勧めしませんでした。
peSHIr 2009

1

PostSharpを使用してEnum.ToStringをターゲットにし、必要なaditionallコードを追加できます。これはコードの変更を必要としません。


1

必要なのは、列挙型をReadonlyCollectionに変換し、コレクションをコンボボックス(またはその問題のキーと値のペアが有効なコントロール)にバインドすることです。

まず、リストの項目を含むクラスが必要です。必要なのはint / stringペアだけなので、必要なオブジェクトに機能を実装できるように、インターフェイスと基本クラスのコンボを使用することをお勧めします。

public interface IValueDescritionItem
{
    int Value { get; set;}
    string Description { get; set;}
}

public class MyItem : IValueDescritionItem
{
    HowNice _howNice;
    string _description;

    public MyItem()
    {

    }

    public MyItem(HowNice howNice, string howNice_descr)
    {
        _howNice = howNice;
        _description = howNice_descr;
    }

    public HowNice Niceness { get { return _howNice; } }
    public String NicenessDescription { get { return _description; } }


    #region IValueDescritionItem Members

    int IValueDescritionItem.Value
    {
        get { return (int)_howNice; }
        set { _howNice = (HowNice)value; }
    }

    string IValueDescritionItem.Description
    {
        get { return _description; }
        set { _description = value; }
    }

    #endregion
}

これは、インターフェイスとそれを実装するサンプルクラスです。クラスのKeyがEnumに強く型指定されており、IValueDescritionItemプロパティが明示的に実装されていることに注意してください(クラスには任意のプロパティを設定でき、実装するプロパティを選択できます)キーと値のペア。

EnumToReadOnlyCollectionクラス:

public class EnumToReadOnlyCollection<T,TEnum> : ReadOnlyCollection<T> where T: IValueDescritionItem,new() where TEnum : struct
{
    Type _type;

    public EnumToReadOnlyCollection() : base(new List<T>())
    {
        _type = typeof(TEnum);
        if (_type.IsEnum)
        {
            FieldInfo[] fields = _type.GetFields();

            foreach (FieldInfo enum_item in fields)
            {
                if (!enum_item.IsSpecialName)
                {
                    T item = new T();
                    item.Value = (int)enum_item.GetValue(null);
                    item.Description = ((ItemDescription)enum_item.GetCustomAttributes(false)[0]).Description;
                    //above line should be replaced with proper code that gets the description attribute
                    Items.Add(item);
                }
            }
        }
        else
            throw new Exception("Only enum types are supported.");
    }

    public T this[TEnum key]
    {
        get 
        {
            return Items[Convert.ToInt32(key)];
        }
    }

}

したがって、コードに必要なのは次のとおりです。

private EnumToReadOnlyCollection<MyItem, HowNice> enumcol;
enumcol = new EnumToReadOnlyCollection<MyItem, HowNice>();
comboBox1.ValueMember = "Niceness";
comboBox1.DisplayMember = "NicenessDescription";
comboBox1.DataSource = enumcol;

コレクションはMyItemで型付けされるため、適切なプロパティにバインドした場合、コンボボックスの値は列挙値を返す必要があることに注意してください。

T this [Enum t]プロパティを追加して、コレクションを単純なコンボの消耗品よりもさらに便利にしました。たとえば、textBox1.Text = enumcol [HowNice.ReallyNice] .NicenessDescription;

もちろん、MyItemをKey / Valueクラスに変換して、このPuproseにのみ使用することを選択できます。EnumToReadnlyCollectionの型引数のMyItemを完全にスキップしますが、キーのintを使用する必要があります(combobox1.SelectedValueを取得することを意味します)。列挙型ではなくintを返します)。MyValueなどを置き換えるKeyValueItemクラスを作成する場合は、この問題を回避します。


1

この古いスレッドを取得してごめんね。

この例では、ドロップダウンリストのテキストフィールドを通じて、説明だけでなく、意味のあるローカライズされた値をユーザーに表示できるため、次の方法で列挙型をローカライズします。

まず、OwToStringByCultureというシンプルなメソッドを作成して、グローバルリソースファイルからローカライズされた文字列を取得します。この例では、App_GlobalResourcesフォルダーのBiBongNet.resxです。このリソースファイル内で、すべての文字列が列挙型の値と同じであることを確認してください(ReallyNice、SortOfNice、NotNice)。このメソッドでは、パラメーター:resourceClassNameを渡します。これは通常、リソースファイルの名前です。

次に、OwFillDataWithEnumと呼ばれるデータソースとしてenumをドロップダウンリストに入力する静的メソッドを作成します。このメソッドは、後で任意の列挙型で使用できます。

次に、DropDownList1というドロップダウンリストがあるページで、Page_Loadに次の1行のコードを設定して、列挙型をドロップダウンリストに入力します。

 BiBongNet.OwFillDataWithEnum<HowNice>(DropDownList1, "BiBongNet");

それでおしまい。私はこれらのようないくつかの単純なメソッドを使用すると、リストコントロールを列挙型で埋めることができます。説明的な値だけでなく、ローカライズされたテキストを表示することもできます。これらすべてのメソッドを拡張メソッドとして作成して、より使いやすくすることができます。

この助けを願っています。共有して共有しましょう!

メソッドは次のとおりです。

public class BiBongNet
{

        enum HowNice
        {
            ReallyNice,
            SortOfNice,
            NotNice
        }

        /// <summary>
        /// This method is for filling a listcontrol,
        /// such as dropdownlist, listbox... 
        /// with an enum as the datasource.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ctrl"></param>
        /// <param name="resourceClassName"></param>
        public static void OwFillDataWithEnum<T>(ListControl ctrl, string resourceClassName)
        {
            var owType = typeof(T);
            var values = Enum.GetValues(owType);
            for (var i = 0; i < values.Length; i++)
            {
                //Localize this for displaying listcontrol's text field.
                var text = OwToStringByCulture(resourceClassName, Enum.Parse(owType, values.GetValue(i).ToString()).ToString());
                //This is for listcontrol's value field
                var key = (Enum.Parse(owType, values.GetValue(i).ToString()));
                //add values of enum to listcontrol.
                ctrl.Items.Add(new ListItem(text, key.ToString()));
            }
        }

        /// <summary>
        /// Get localized strings.
        /// </summary>
        /// <param name="resourceClassName"></param>
        /// <param name="resourceKey"></param>
        /// <returns></returns>
        public static string OwToStringByCulture(string resourceClassName, string resourceKey)
        {
                return (string)HttpContext.GetGlobalResourceObject(resourceClassName, resourceKey);
        }
}

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

これを解決するには、拡張メソッドと文字列の配列を次のように使用する必要があります。

Enum HowNice {
  ReallyNice  = 0,
  SortOfNice  = 1,
  NotNice     = 2
}

internal static class HowNiceIsThis
{
 const String[] strings = { "Really Nice", "Kinda Nice", "Not Nice At All" }

 public static String DecodeToString(this HowNice howNice)
 {
   return strings[(int)howNice];
 }
}

シンプルなコードと高速なデコード。


文字列変数は静的であるのでように宣言すべきである:静的な文字列は、[]の文字列=新しい[] {...}
セルジオ

これの唯一の問題は、すべての列挙に対して関数が必要になることであり、説明は列挙自体の一部になります...
Avi Turner

1

私はこのアプローチを試しましたが、うまくいきました。

enumのラッパークラスを作成し、それをenum変数に割り当てることができるように暗黙の演算子をオーバーロードしました(この場合、オブジェクトをComboBox値にバインドする必要がありました)。

リフレクションを使用して、希望する方法で列挙値をフォーマットできます。私の場合は、 DisplayAttribute、列挙値(存在する。

お役に立てれば。

public sealed class EnumItem<T>
{
    T value;

    public override string ToString()
    {
        return Display;
    }

    public string Display { get; private set; }
    public T Value { get; set; }

    public EnumItem(T val)
    {
        value = val;
        Type en = val.GetType();
        MemberInfo res = en.GetMember(val.ToString())?.FirstOrDefault();
        DisplayAttribute display = res.GetCustomAttribute<DisplayAttribute>();
        Display = display != null ? String.Format(display.Name, val) : val.ToString();
    }

    public static implicit operator T(EnumItem<T> val)
    {
        return val.Value;
    }

    public static implicit operator EnumItem<T>(T val)
    {
        return new EnumItem<T>(val);
    }
}

編集:

念のため、次の関数enumを使用してDataSourceComboBox

public static class Utils
{
    public static IEnumerable<EnumItem<T>> GetEnumValues<T>()
    {
        List<EnumItem<T>> result = new List<EnumItem<T>>();
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            result.Add(item);
        }
        return result;
    }
}

0

GetDescriptionメソッドを取得したら(グローバルスタティックである必要があります)、これを拡張メソッドを通じて使用できます。

public static string ToString(this HowNice self)
{
    return GetDescription<HowNice>(self);
}

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

Status = ReallyNice.GetDescription()

3
Stackoverflowへようこそ!投稿の精度を向上させるために、サンプルコードの短い説明を提供することは常に優れています:)
Picrofoソフトウェア

-1

Enumは次のように定義できます。

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

次にを使用しますHowNice.GetStringValue()


2
これはコンパイルされません(.NET 3.5を使用しています)。「StringValue」はどこで宣言されていますか?
畏敬の念の

1
@scraimerからの答えは同じですが、彼がフレームワーク外の属性を使用しているのに対して、ある種の自己定義属性を使用しています。
オリバー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.