/ Published in: C#
When you want to put the values of an enumeration into a ComboBox or similar control, there is no obvious or easy way to customize the text displayed.
[UPDATE] The best method I've found is to use a custom TypeConverter. I haven't worked out all the intricacies, but the code below works for combo/listboxes and datagridviews, and I imagine that it would work with all IComponents.
One method below uses the System.ComponentModel.DescriptionAttribute to create a nice description of the enum value and then reads those values using reflection and an extension method (requires .NET 3.5 - see the references for alternatives). Once those values are read, they are put in a Dictionary in order to act as the DataSource for a ComboBox (named w_wifiSecurityIO in the example).
Unfortunately, attributes (to my knowledge) cannot access resources, so the first method does not allow for localization. To overcome this challenge, I have used a Dictionary as the control's Tag and then use the SelectedItem.ToString as the lookup for the enum value selected (works with multiple selections and Flags enums as well, e.g. in a CheckedListBox). The second method uses a Dictionary for the DataSource, sets the DisplayMember and ValueMember properties, then retrieves using the SelectedValue property.
(Since the initialization code is only called once, and there are only a few enum values, the performance hit for using reflection is negligible. Even if it were noticeable, it might still be warranted when compared to other methods of customizing the displayed text.)
The third method comes from the answer to a [StackOverflow question about the subject](http://stackoverflow.com/questions/1415140/c-enums-can-my-enums-have-friendly-names/1415460#1415460) and uses a simple extension method that allows for localization.
Another method is [enumeration classes](http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/08/12/enumeration-classes.aspx).
(Used in RedBird::Hawk.)
References:
[msmvps.com](http://msmvps.com/blogs/deborahk/archive/2009/07/10/enum-binding-to-the-description-attribute.aspx)
[codeproject](http://www.codeproject.com/KB/cs/enumdatabinding.aspx)
[blogs.msdn](http://blogs.msdn.com/abhinaba/archive/2005/10/21/483337.aspx)
[local ms help](ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/fxref_system/html/7f153948-1284-313b-1f33-a6ed0d817322.htm) (DescriptionAttribute Class)
[UPDATE] The best method I've found is to use a custom TypeConverter. I haven't worked out all the intricacies, but the code below works for combo/listboxes and datagridviews, and I imagine that it would work with all IComponents.
One method below uses the System.ComponentModel.DescriptionAttribute to create a nice description of the enum value and then reads those values using reflection and an extension method (requires .NET 3.5 - see the references for alternatives). Once those values are read, they are put in a Dictionary in order to act as the DataSource for a ComboBox (named w_wifiSecurityIO in the example).
Unfortunately, attributes (to my knowledge) cannot access resources, so the first method does not allow for localization. To overcome this challenge, I have used a Dictionary as the control's Tag and then use the SelectedItem.ToString as the lookup for the enum value selected (works with multiple selections and Flags enums as well, e.g. in a CheckedListBox). The second method uses a Dictionary for the DataSource, sets the DisplayMember and ValueMember properties, then retrieves using the SelectedValue property.
(Since the initialization code is only called once, and there are only a few enum values, the performance hit for using reflection is negligible. Even if it were noticeable, it might still be warranted when compared to other methods of customizing the displayed text.)
The third method comes from the answer to a [StackOverflow question about the subject](http://stackoverflow.com/questions/1415140/c-enums-can-my-enums-have-friendly-names/1415460#1415460) and uses a simple extension method that allows for localization.
Another method is [enumeration classes](http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/08/12/enumeration-classes.aspx).
(Used in RedBird::Hawk.)
References:
[msmvps.com](http://msmvps.com/blogs/deborahk/archive/2009/07/10/enum-binding-to-the-description-attribute.aspx)
[codeproject](http://www.codeproject.com/KB/cs/enumdatabinding.aspx)
[blogs.msdn](http://blogs.msdn.com/abhinaba/archive/2005/10/21/483337.aspx)
[local ms help](ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/fxref_system/html/7f153948-1284-313b-1f33-a6ed0d817322.htm) (DescriptionAttribute Class)
Expand |
Embed | Plain Text
Copy this code and paste it in your HTML
//Best way so far - Generic TypeConverter with possibility of specific values - suffers from boxing, but hasn't slowed my apps down using System; using System.ComponentModel; namespace X { public enum IPKind { Static, Dhcp, } public class EnumTypeConverter<TEnum> : TypeConverter { public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { { var e = (TEnum)value; return e.DisplayValue(); // culture? } return base.ConvertTo(context, culture, value, destinationType); } } public static class IPKindExtensions { public static string DisplayValue(this IPKind value) { // Localization? switch (value) { case IPKind.Static: return "Static"; case IPKind.Dhcp: return "Dynamic (DHCP)"; default: return value.ToString(); } } } public static class EnumExtensions { public static string DisplayValue<TEnum>(this TEnum value) { { return ((IPKind)(object)value).DisplayValue(); } return value.ToString(); } } } // Use: // Set up combobox to display enum // Bind to a combo box w_securityIO.DisplayMember = "Key"; w_securityIO.ValueMember = "Value"; w_securityIO.DataSource = enumValues; // Might throw an ArgumentNullExc which can be ignored - not sure why using System; using System.ComponentModel; using Hawk.Properties; namespace Hawk { public enum WifiSecurity : byte { None, Wep64, Wep128, WpaTkip, Wpa2Aes, } public class WifiSecurityTypeConverter : TypeConverter { public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { // This code actually works for a DGV (and a combobox)! It's called a lot! (Like, every time a user adjusts the width of a column.) { var e = (WifiSecurity) value; return e.DisplayValue(); // culture? } return base.ConvertTo(context, culture, value, destinationType); } } public static class WifiSecurityExtensions { public static string DisplayValue(this WifiSecurity value) { // Localization? switch (value) { case WifiSecurity.None: return "None"; case WifiSecurity.Wep64: return Resources.Wifi_Security_Wep64; // "WEP 64"; case WifiSecurity.Wep128: return Resources.Wifi_Security_Wep128; // "WEP 128"; case WifiSecurity.WpaTkip: return Resources.Wifi_Security_WPA; // "WPA/TKIP"; case WifiSecurity.Wpa2Aes: return Resources.Wifi_Security_WPA2; // "WPA2/AES"; default: return value.ToString(); } } } } //End of TypeConverter example public class Wifi { public enum Security { [Description("WPA2-PSK (AES)")] Wpa2, [Description("WPA-PSK (TKIP)")] Wpa, [Description("WEP 64")] Wep64, [Description("WEP 32")] Wep32, } } private void InitWifiControls() { var items = enumValues.Cast<Wifi.Security>().ToDictionary(value => value.GetDescription()); // Bind to a combo box w_wifiSecurityIO.DisplayMember = "Key"; w_wifiSecurityIO.ValueMember = "Value"; } private void InitWifiControls() { {"WPA2-PSK (AES)",Wpa2}, {"WPA-PSK (TKIP)",Wpa}, {"WEP 64",Wep64}, {"WEP 32",Wep32}, }; w_wifiSecurityIO.DisplayMember = "Key"; w_wifiSecurityIO.ValueMember = "Value"; } private void GetWifiControlsSelection() { Security selectedSecurity = w_wifiSecurityIO.SelectedValue; // w_wifiSecurityIO.SelectedText is blank (don't know why) // w_wifiSecurityIO.SelectedItem is the dictionary (but boxed into an object) } public static class Extensions { public static string GetDescription(this Enum value) { if (value == null) FieldInfo fi = value.GetType().GetField(value.ToString()); string description = da != null ? da.Description : value.ToString(); return description; } } // Third method: private void InitWifiControls() { var items = enumValues.Cast<WifiSecurity>().ToDictionary(value => value.DisplayValue()); w_wifiSecurityIO.DisplayMember = "Key"; w_wifiSecurityIO.ValueMember = "Value"; } public enum WifiSecurity : byte { None, Wep64, Wep128, WpaTkip, //Resources.Wifi_Security_WPA Wpa2Aes, } public static class WifiSecurityExtensions { public static string DisplayValue(this WifiSecurity value) { switch (value) { case WifiSecurity.None: return "None"; case WifiSecurity.Wep64: return "WEP 64"; case WifiSecurity.Wep128: return "WEP 128"; case WifiSecurity.WpaTkip: return "WPA/TKIP"; case WifiSecurity.Wpa2Aes: return "WPA2/AES"; default: } } }