Binding an enum to a control (such as a ComboBox or DataGridView) with a custom string for each enum value


/ Published in: C#
Save to your folder(s)

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)


Copy this code and paste it in your HTML
  1. //Best way so far - Generic TypeConverter with possibility of specific values - suffers from boxing, but hasn't slowed my apps down
  2. using System;
  3. using System.ComponentModel;
  4.  
  5. namespace X
  6. {
  7. [TypeConverter(typeof(EnumTypeConverter<IPKind>))]
  8. public enum IPKind
  9. {
  10. Static,
  11. Dhcp,
  12. }
  13.  
  14. public class EnumTypeConverter<TEnum> : TypeConverter
  15. {
  16. public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
  17. {
  18. if (destinationType == typeof(string) && value is TEnum)
  19. {
  20. var e = (TEnum)value;
  21. return e.DisplayValue(); // culture?
  22. }
  23. return base.ConvertTo(context, culture, value, destinationType);
  24. }
  25. }
  26.  
  27. public static class IPKindExtensions
  28. {
  29. public static string DisplayValue(this IPKind value)
  30. {
  31. // Localization?
  32. switch (value)
  33. {
  34. case IPKind.Static:
  35. return "Static";
  36. case IPKind.Dhcp:
  37. return "Dynamic (DHCP)";
  38. default:
  39. return value.ToString();
  40. }
  41. }
  42. }
  43.  
  44.  
  45.  
  46. public static class EnumExtensions
  47. {
  48. public static string DisplayValue<TEnum>(this TEnum value)
  49. {
  50. if (value is IPKind)
  51. {
  52. return ((IPKind)(object)value).DisplayValue();
  53. }
  54. return value.ToString();
  55. }
  56. }
  57. }
  58.  
  59.  
  60. // Use:
  61. // Set up combobox to display enum
  62. var enumValues = Enum.GetValues(typeof(WifiSecurity));
  63.  
  64. // Bind to a combo box
  65. w_securityIO.DisplayMember = "Key";
  66. w_securityIO.ValueMember = "Value";
  67. w_securityIO.DataSource = enumValues; // Might throw an ArgumentNullExc which can be ignored - not sure why
  68.  
  69.  
  70. using System;
  71. using System.ComponentModel;
  72. using Hawk.Properties;
  73.  
  74. namespace Hawk
  75. {
  76. [TypeConverter(typeof(WifiSecurityTypeConverter))]
  77. public enum WifiSecurity : byte
  78. {
  79. None,
  80. Wep64,
  81. Wep128,
  82. WpaTkip,
  83. Wpa2Aes,
  84. }
  85.  
  86. public class WifiSecurityTypeConverter : TypeConverter
  87. {
  88. public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
  89. {
  90. // 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.)
  91. if (destinationType == typeof(string) && value is WifiSecurity)
  92. {
  93. var e = (WifiSecurity) value;
  94. return e.DisplayValue(); // culture?
  95. }
  96. return base.ConvertTo(context, culture, value, destinationType);
  97. }
  98. }
  99.  
  100. public static class WifiSecurityExtensions
  101. {
  102. public static string DisplayValue(this WifiSecurity value)
  103. {
  104. // Localization?
  105. switch (value)
  106. {
  107. case WifiSecurity.None:
  108. return "None";
  109. case WifiSecurity.Wep64:
  110. return Resources.Wifi_Security_Wep64; // "WEP 64";
  111. case WifiSecurity.Wep128:
  112. return Resources.Wifi_Security_Wep128; // "WEP 128";
  113. case WifiSecurity.WpaTkip:
  114. return Resources.Wifi_Security_WPA; // "WPA/TKIP";
  115. case WifiSecurity.Wpa2Aes:
  116. return Resources.Wifi_Security_WPA2; // "WPA2/AES";
  117. default:
  118. return value.ToString();
  119. }
  120. }
  121. }
  122. }
  123. //End of TypeConverter example
  124.  
  125. public class Wifi
  126. {
  127. public enum Security
  128. {
  129. [Description("WPA2-PSK (AES)")]
  130. Wpa2,
  131. [Description("WPA-PSK (TKIP)")]
  132. Wpa,
  133. [Description("WEP 64")]
  134. Wep64,
  135. [Description("WEP 32")]
  136. Wep32,
  137. }
  138. }
  139.  
  140.  
  141. private void InitWifiControls()
  142. {
  143. var enumValues = Enum.GetValues(typeof (Wifi.Security));
  144.  
  145. var items = enumValues.Cast<Wifi.Security>().ToDictionary(value => value.GetDescription());
  146.  
  147. // Bind to a combo box
  148. w_wifiSecurityIO.DisplayMember = "Key";
  149. w_wifiSecurityIO.ValueMember = "Value";
  150. w_wifiSecurityIO.DataSource = new BindingSource(items, null);
  151. }
  152.  
  153. private void InitWifiControls()
  154. {
  155. var map = new Dictionary<string, Security>() {
  156. {"WPA2-PSK (AES)",Wpa2},
  157. {"WPA-PSK (TKIP)",Wpa},
  158. {"WEP 64",Wep64},
  159. {"WEP 32",Wep32},
  160. };
  161. w_wifiSecurityIO.DataSource = new BindingSource(modeMap, null);
  162. w_wifiSecurityIO.DisplayMember = "Key";
  163. w_wifiSecurityIO.ValueMember = "Value";
  164. }
  165.  
  166. private void GetWifiControlsSelection()
  167. {
  168. Security selectedSecurity = w_wifiSecurityIO.SelectedValue;
  169. // w_wifiSecurityIO.SelectedText is blank (don't know why)
  170. // w_wifiSecurityIO.SelectedItem is the dictionary (but boxed into an object)
  171. }
  172.  
  173.  
  174. public static class Extensions
  175. {
  176. public static string GetDescription(this Enum value)
  177. {
  178. if (value == null)
  179. throw new ArgumentNullException("value");
  180.  
  181. FieldInfo fi = value.GetType().GetField(value.ToString());
  182.  
  183. var da = (DescriptionAttribute)Attribute.GetCustomAttribute(fi, typeof(DescriptionAttribute));
  184.  
  185. string description = da != null ? da.Description : value.ToString();
  186.  
  187. return description;
  188. }
  189. }
  190.  
  191.  
  192. // Third method:
  193. private void InitWifiControls()
  194. {
  195. var enumValues = Enum.GetValues(typeof(WifiSecurity));
  196. var items = enumValues.Cast<WifiSecurity>().ToDictionary(value => value.DisplayValue());
  197. w_wifiSecurityIO.DataSource = new BindingSource(items, null);
  198. w_wifiSecurityIO.DisplayMember = "Key";
  199. w_wifiSecurityIO.ValueMember = "Value";
  200. }
  201.  
  202. public enum WifiSecurity : byte
  203. {
  204. None,
  205. Wep64,
  206. Wep128,
  207. WpaTkip,
  208. //Resources.Wifi_Security_WPA
  209. Wpa2Aes,
  210. }
  211.  
  212. public static class WifiSecurityExtensions
  213. {
  214. public static string DisplayValue(this WifiSecurity value)
  215. {
  216. switch (value)
  217. {
  218. case WifiSecurity.None:
  219. return "None";
  220. case WifiSecurity.Wep64:
  221. return "WEP 64";
  222. case WifiSecurity.Wep128:
  223. return "WEP 128";
  224. case WifiSecurity.WpaTkip:
  225. return "WPA/TKIP";
  226. case WifiSecurity.Wpa2Aes:
  227. return "WPA2/AES";
  228. default:
  229. throw new ArgumentOutOfRangeException("value");
  230. }
  231. }
  232. }

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.