Introduction
Over course of my professional life as the GUI programmer, countless times I needed to populate combo box with values mapped to some Enum
values defined in the program. Then after user selection, I needed to translate selected item back to the corresponding Enum
value. I always ended up with ugly and difficult to maintain code, where Enum
values were manually mapped to the ComboBox
instance using ComboBox
selected item index or string
value.
I knew it could be done better but lack of time, laziness, "I'll fix it later" approach prevented me from coding proper solution. But enough is enough.
Extension methods introduced with C# 3.0 provide a very convenient way to extend existing types with additional methods. It's simple and elegant and what is most important is that it is very easy to integrate with targeted types. Only thing you have to do is add the reference to the class containing extensions and add using
directive to the extension namespace in the code.
Code Explained
To cover the needed functionality, the following three methods are sufficient:
- Method to bind any
Enum
type with ComboBox
instance. After binding, ComboBox
instance should behave as GUI representation of the underlying Enum
type. In another words, ComboBox
items should be tightly related to Enum
fields.
public static void BindWithEnum<T>(this ComboBox comboBox, T selectedValue)
selectedValue
parameter determines initially selected ComboBox
item.
- Method to retrieve currently selected
ComboBox
item.
public static T GetSelectedValue<T>(this ComboBox comboBox)
- And finally, method to change
ComboBox
selection.
public static void SetSelectedValue<T>(this ComboBox comboBox,T selectedValue)
selectedValue
parameter determines new selected ComboBox
item.
BindWithEnum Method
This method uses reflection to enumerate all fields of given Enum
type. For each field, it retrieves Description
attribute in order to provide human friendly text in the ComboBox
item. Next, the method adds Description
text and associated with it Enum
value to the ComboBox
item list. ComboBox
becomes tightly coupled visual representation of underlying Enum
.
public static void BindWithEnum<T>(this ComboBox comboBox, T selectedValue)
{
Type enumType = typeof(T);
if (!enumType.IsEnum)
throw new Exception("Only Enum types are allowed.");
List<KeyValuePair<string,
T>> comboBoxItems = new List<KeyValuePair<string, T>>();
KeyValuePair<string, T>? selectedItem = null;
foreach (T enumValue in Enum.GetValues(enumType))
{
string name = Enum.GetName(enumType, enumValue);
FieldInfo fi = enumType.GetField(name);
string descriptiveName = fi.GetDescriptionAttributeOrName();
KeyValuePair<string,T> item =
new KeyValuePair<string,T>(descriptiveName,enumValue);
comboBoxItems.Add(item);
if (enumValue.Equals(selectedValue))
selectedItem = item;
}
comboBox.DisplayMember = "Key";
comboBox.ValueMember = "Value";
comboBox.DataSource = comboBoxItems;
if (selectedItem != null)
comboBox.SelectedItem = selectedItem.Value;
}
Example
The following code binds Enum
type OptionsToSelectFrom
with comboBox1
and makes OptionsToSelectForm.Opt3
selected item in the comboBox1
.
comboBox1.BindWithEnum<OptionsToSelectFrom>(OptionsToSelectFrom.opt3);
GetSelectedValue Method
Method retrieves Enum
value associated with currently selected ComboBox
item.
public static T GetSelectedValue<T>(this ComboBox comboBox)
{
KeyValuePair<string, T>
selectedItem = (KeyValuePair<string, T>)comboBox.SelectedItem;
return (T)Convert.ChangeType(selectedItem.Value, typeof(T));
}
Example
OptionsToSelectFrom selectedValue =
comboBox1.GetSelectedValue<OptionsToSelectFrom>();
SetSelectedValue Method
This method is used to set selected ComboBox
item to provided Enum
value.
public static void SetSelectedValue<T>(this ComboBox comboBox,T selectedVaue)
{
string name = Enum.GetName(typeof(T), selectedOption);
FieldInfo fi = typeof(T).GetField(name);
string descriptiveName = fi.GetDescriptionAttributeOrName();
KeyValuePair<string, T> selectedItem =
new KeyValuePair<string, T>(descriptiveName, selectedOption);
comboBox.SelectedItem = selectedItem;
}
Example
comboBox1.SetSelectedValue<OptionsToSelectFrom>(OptionsToSelectFrom.opt2);
Some Extras
As the "side effect" of coding ComboBoxExtensions
I got a small set of Type
related extensions. Initially, I defined them as private
helper methods used internally for ComboboxExtensions
but after giving it some thought, I came to the conclusion that they can have some value by themselves, so I decided to expose them as public
extension methods as well.
Below, you can find their names and usage examples.
TryGetDefaultValueAttribute Method
This method simplifies retrieval of DefaultValue
attribute for the given type. If DefaultValue
exists for given type, its value is returned int
out
parameter and method return value is true
. If DefaultValue
attribute does not exist for the given type, the method returns false
and value of output parameter is indeterminate.
public static bool TryGetDefaultValueAttribute<T>(this Type type, out T defaultValue)
{
DefaultValueAttribute[] defaultValueAttribute =
(DefaultValueAttribute[])type.GetCustomAttributes(typeof(DefaultValueAttribute),false);
if (defaultValueAttribute.Length > 0)
{
defaultValue = (T)Convert.ChangeType(defaultValueAttribute[0].Value, typeof(T));
return true;
}
else
{
defaultValue = Activator.CreateInstance<T>();
return false;
}
}
Example
if (!typeof(OptionsToSelectFrom).
TryGetDefaultValueAttribute<OptionsToSelectFrom>(out defValue))
{
MessageBox.Show("Type does not have DefaultValue attribute");
}
GetDefaultValueAttribute
This method retrieves DefaultValue
attribute in a similar way as TryGetDefaultValueAttribute
. If attribute does not exist, the method raises an exception.
public static T GetDefaultValueAttribute<T>(this Type type)
{
T retValue;
if (type.TryGetDefaultValueAttribute<T>(out retValue))
return retValue;
else
throw new Exception"No DefaultValue associated with the type.", type.ToString()));
}
GetDescriptionAttributeOrName
This method retrieves value of Description
attribute for given enum
value. If Description
attribute does not exist, the method returns field name.
public static string GetDescriptionAttributeOrName(this Enum value)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
FieldInfo fi = type.GetField(name);
return fi.GetDescriptionAttributeOrName();
}
Using the Code
Very simple.
- In your project, add reference to ComboBoxExttensions.dll
- Add the following
using
directive to your code: using Proxoft.WinForms;
- And you are ready to go. The below snippet shows an example of how to bind
Enum
with ComboBox
and how to set and retrieve current selection. You can also download the source code and demo program and get yourself familiar with extensions functionality.
The following code snippets show most of the use cases:
using Proxoft.WinForms;
(..)
[DefaultValue(OptionsToSelectFrom.opt3)]
public enum OptionsToSelectFrom
{
[Description("Option One")]
opt1,
[Description("Option Two")]
opt2,
[Description("Option Three")]
opt3,
[Description("Option Four")]
opt4
}
(...)
comboBox1.BindWithEnum<OptionsToSelectFrom>(OptionsToSelectFrom.opt3);
(...)
<span style="font-size: 9pt;">comboBox1.SetSelectedValue
<OptionsToSelectFrom>(OptionsToSelectFrom.opt2);
</span>
<pre lang="cs">(...)
//The following line shows how to examine currently selected value
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
label1.Text = comboBox1.GetSelectedValue<OptionsToSelectFrom>().ToString();
}
History
- November 7th, 2013 - Initial article release