ポスト 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にGetDescription
を呼び出すことを強制できないので、単に列挙の値をComboBoxに入力したい場合。
私が欲しいものには次の要件があります:
(HowNice)myComboBox.selectedItem
_を読み取ると、選択した値が列挙値として返されます。NotNice
」ではなく「_Not Nice At All
_」が表示されます。明らかに、作成する各列挙型に新しいクラスを実装し、そのToString()
をオーバーライドできますが、それは各列挙型の多くの作業であり、むしろそれを避けたいです。
何か案は?
ええと、私は ハグ を賞金として投げ入れさえします:-)
指定された属性を読み取り、リソースで検索するTypeConverterを作成できます。したがって、表示名の多言語サポートはそれほど面倒なことはありません。
TypeConverterのConvertFrom/ConvertToメソッドを調べ、リフレクションを使用して、enum fieldsの属性を読み取ります。
ComboBox
には必要なものがすべて揃っています。FormattingEnabled
プロパティ(true
に設定する必要があります)、およびFormat
イベント(必要な書式を設定する必要がある場所)論理。副<文>この[前述の事実の]結果として、それ故に、従って、だから◆【同】consequently; therefore <文>このような方法で、このようにして、こんなふうに、上に述べたように◆【同】in this manner <文>そのような程度まで<文> AひいてはB◆【用法】A and thus B <文>例えば◆【同】for example; as an example、
myComboBox.FormattingEnabled = true;
myComboBox.Format += delegate(object sender, ListControlConvertEventArgs e)
{
e.Value = GetDescription<HowNice>((HowNice)e.Value);
}
しないでください!列挙型は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";
TypeConverter。これが私が探していたものだと思います。すべてあられ Simon Svensson !
_[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()
をオーバーライドするわけではありません。私はこの間に落ち着くと思います...
列挙例を使用して:
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にあります
説明を含むすべての列挙型に使用できる汎用構造体を作成できます。クラスとの間の暗黙的な変換では、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"
これを行う最良の方法は、クラスを作成することです。
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が表示されます。クラスのインスタンスをこれ以上作成できないので、基本的には列挙型になります。
追伸若干の構文修正が必要になる場合がありますが、C#にはあまり向いていません。 (Java男)
単純に別の型にバインドすることなく、それができるとは思わない-少なくとも、便利ではない。通常、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()
にバインドします
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ステートメントとすべてを備えたニースのソリューションではありません。
列挙型ごとにクラスを作成するのではなく、列挙型の値/表示テキストの辞書を作成し、代わりにバインドすることをお勧めします。
これは、元の投稿の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;
}
@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;
}
この古いスレッドを作成して申し訳ありません。
この例のドロップダウンリストテキストフィールドを介して、説明だけでなく、意味のあるローカライズされた値をユーザーに表示できるため、enumをローカライズするには次の方法を使用します。
まず、OwToStringByCultureと呼ばれる単純なメソッドを作成して、グローバルリソースファイルからローカライズされた文字列を取得します。この例では、App_GlobalResourcesフォルダーのBiBongNet.resxです。このリソースファイル内で、すべての文字列が列挙値(ReallyNice、SortOfNice、NotNice)と同じであることを確認してください。このメソッドでは、パラメータとしてresourceClassNameを渡します。これは通常、リソースファイルの名前です。
次に、静的メソッドを作成して、ドロップダウンリストにデータソースとしてOwFillDataWithEnumと呼ばれる列挙型を入力します。このメソッドは、後で列挙型で使用できます。
次に、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);
}
}
任意の型で使用する汎用クラスを作成します。私は過去にこのようなものを使用しました:
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)と呼びます...
私はこのアプローチを試みましたが、うまくいきました。
列挙型のラッパークラスを作成し、暗黙の演算子をオーバーロードして列挙型変数に割り当てることができます(この場合、オブジェクトをComboBox
値にバインドする必要がありました)。
リフレクションを使用して、enum値を必要な方法でフォーマットできます。私の場合、enum値から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
のDataSource
に使用するComboBox
値を取得します。
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;
}
}
必要なのは、列挙型を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で入力されるため、適切なプロパティにバインドする場合、combobox値は列挙値を返す必要があることに注意してください。
T this [Enum t]プロパティを追加して、コレクションを単純なコンボ消耗品よりもさらに便利にしました。たとえば、textBox1.Text = enumcol [HowNice.ReallyNice] .NicenessDescription;
もちろん、MyItemをEnumToReadnlyCollectionの型引数でMyItemを完全にスキップするこのpuproseのみに使用されるKey/Valueクラスに変更することを選択できますが、キーのintを使用することを強制されます(combobox1.SelectedValueを取得することを意味します)列挙型ではなくintを返します)。 MyValueなどを置き換えるKeyValueItemクラスを作成すると、この問題を回避できます...
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];
}
}
シンプルなコードと高速デコード。
PostSharpを使用してEnum.ToStringをターゲットにし、必要なコードを追加できます。これはコードの変更を必要としません。
必要なものを含むコレクションを作成します(Value
enum値を含むHowNice
プロパティと、GetDescription<HowNice>(Value)
およびdatabindを含むDescription
プロパティを含む単純なオブジェクトのようにそのコレクションへのコンボ。
このようなビット:
_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; } }
}
}
_
ご覧のとおり、このコレクションはラムダで簡単にカスタマイズでき、列挙子のサブセットを選択したり、言及したGetDescription<T>(x)
関数を使用する代わりにstring
にカスタム書式を実装したりできます。
Enum HowNice {
[StringValue("Really Nice")]
ReallyNice,
[StringValue("Kinda Nice")]
SortOfNice,
[StringValue("Not Nice At All")]
NotNice
}
Status = ReallyNice.GetDescription()
GetDescription
メソッドを取得したら(グローバル静的である必要があります)、これを拡張メソッドで使用できます。
public static string ToString(this HowNice self)
{
return GetDescription<HowNice>(self);
}