在帖子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()
,但这对每个枚举都有很多工作,我宁愿避免这样做。
有什么想法吗?
哎呀,我甚至会把hug作为赏金: - )
答案 0 :(得分:85)
ComboBox
拥有您需要的一切:FormattingEnabled
属性,您应设置为true
,以及Format
事件,您需要放置所需的格式逻辑。因此,
myComboBox.FormattingEnabled = true;
myComboBox.Format += delegate(object sender, ListControlConvertEventArgs e)
{
e.Value = GetDescription<HowNice>((HowNice)e.Value);
}
答案 1 :(得分:44)
不要!枚举是原始的而不是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";
答案 2 :(得分:41)
的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()
。我想我会同时满足于此......
答案 3 :(得分:41)
您可以编写一个TypeConverter来读取指定的属性,以便在资源中查找它们。因此,您可以毫不费力地获得显示名称的多语言支持。
查看TypeConverter的ConvertFrom / ConvertTo方法,并使用反射来读取枚举字段上的属性。
答案 4 :(得分:31)
使用枚举示例:
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的解决方案
答案 5 :(得分: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"
答案 6 :(得分: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,并且没有人可以再创建您的类的实例,这实际上使它成为枚举。
P.S。可能需要一些轻微的语法修复,我对C#不是很好。 (Java家伙)
答案 7 :(得分:5)
我不认为你可以做到这一点而不仅仅是绑定到另一种类型 - 至少,不方便。通常情况下,即使您无法控制ToString()
,也可以使用TypeConverter
进行自定义格式设置 - 但{II} System.ComponentModel
内容不会因枚举而受到尊重。
你可以绑定到string[]
描述,或者一个基本上像键/值对的东西? (desription / value) - 类似于:
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()
答案 8 :(得分: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语句和所有 - 但它应该工作,并希望whitout到许多重写......
答案 9 :(得分: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;
}
答案 10 :(得分: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;
}
答案 11 :(得分: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方法几乎相同)。这将节省您必须为每个枚举类型实现一个实体,并为“GetDescriptions”帮助器方法提供一个好的/逻辑位置(我个人称之为FromEnum(T obj)...
答案 12 :(得分:1)
创建一个包含所需内容的集合(例如包含Value
属性的简单对象,其中包含HowNice
枚举值和包含Description
的{{1}}属性,并对数组进行数据绑定那个系列。
像这样:
GetDescription<HowNice>(Value)
当你有这样的集合类时:
Combo.DataSource = new EnumeratedValueCollection<HowNice>();
Combo.ValueMember = "Value";
Combo.DisplayMember = "Description";
正如您所看到的,此集合可以使用lambda轻松自定义,以选择枚举器的子集和/或实现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
函数。
答案 13 :(得分:1)
您可以使用PostSharp定位Enum.ToString并添加所需的aditionall代码。 这不需要任何代码更改。
答案 14 :(得分: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键入的,因此如果绑定到适当的proprtie,组合框值应返回枚举值。
我添加了T [Enum t]属性,使集合比简单的组合耗材更有用,例如textBox1.Text = enumcol [HowNice.ReallyNice] .NicenessDescription;
你当然可以选择将MyItem转换为仅用于此puprose的Key / Value类,在EnumToReadnlyCollection的类型参数中有效地跳过MyItem,但是你将被迫使用int来获取键(意味着获取) combobox1.SelectedValue将返回int而不是枚举类型)。如果你创建一个KeyValueItem类来替换MyItem等等,那么你可以解决这个问题......等等...
答案 15 :(得分:1)
很抱歉让这个老帖子搞定。
我会采用以下方式来本地化枚举,因为它可以通过此示例中的下拉列表文本字段向用户显示有意义的本地化值,而不仅仅是描述。
首先,我创建了一个名为OwToStringByCulture的简单方法,以从全局资源文件中获取本地化字符串,在此示例中,它是App_GlobalResources文件夹中的BiBongNet.resx。在此资源文件中,确保所有字符串与枚举值(ReallyNice,SortOfNice,NotNice)相同。在这个方法中,我传入参数:resourceClassName,它通常是资源文件的名称。
接下来,我创建一个静态方法来填充下拉列表,其中枚举为enum作为其数据源,称为OwFillDataWithEnum。此方法可以在以后的任何枚举中使用。
然后在带有名为DropDownList1的下拉列表的页面中,我在Page_Load中设置了以下一行简单的代码来将枚举填充到下拉列表中。
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);
}
}
答案 16 :(得分: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];
}
}
简单的代码和快速解码。
答案 17 :(得分:1)
我尝试了这种方法,它对我有用。
我为枚举创建了一个包装类并重载了隐式运算符,以便我可以将它分配给枚举变量(在我的例子中,我必须将对象绑定到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
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;
}
}
值
addUser
答案 18 :(得分:0)
一旦你有GetDescription
方法(它需要是全局静态),你可以通过扩展方法使用它:
public static string ToString(this HowNice self)
{
return GetDescription<HowNice>(self);
}
答案 19 :(得分:0)
Enum HowNice {
[StringValue("Really Nice")]
ReallyNice,
[StringValue("Kinda Nice")]
SortOfNice,
[StringValue("Not Nice At All")]
NotNice
}
Status = ReallyNice.GetDescription()
答案 20 :(得分:-1)
您可以将Enum定义为
Enum HowNice {
[StringValue("Really Nice")]
ReallyNice,
[StringValue("Kinda Nice")]
SortOfNice,
[StringValue("Not Nice At All")]
NotNice
}
然后使用HowNice.GetStringValue()
。