I have several ComboBox controls, with DropDownStyle set to DropDownList. The items that are selected are enum values, but I want "friendly" descriptions of these to be displayed, i.e. with spaces and not camel case.
I want the combo box to be "two-way" bound to the data object: if the data object changes its property it alters the combo box, and vice versa.
I can do this easily with strings but the problem is I am binding the controls to enum properties in the objects, so I want the Items collection of the Combo Box to contain actual enums. This obviously isn't going to work.
Solution 1: textual properties
I can create extra properties in my object which are textual, and map those to the enum values. This is a bit messy though as I am including things in my business logic layer which really belong in the UI. In that business logic layer they should be enums and not strings.
Solution 2: event handlers
Another alternative is to use event handlers so that when the user changes the option it gets the selected item text and finds the appropriate enum value, then sets this in the object. This is only one way binding though.
Attempted Solution 3
public class BusinessObject
{
private NumberCategory category;
public NumberCategory Category
{
get
{
return category;
}
set
{
category = value;
}
}
}
public enum NumberCategory
{
[Description("Negative Number")]
NegativeNumber,
[Description("Zero")]
Zero,
[Description("One")]
One,
[Description("Prime Number")]
PrimeNumber,
[Description("Composite Number")]
CompositeNumber,
}
public class EnumDescriptionAdapter
{
private readonly BusinessObject businessObject;
public EnumDescriptionAdapter(BusinessObject businessObject)
{
this.businessObject = businessObject;
}
public string CategoryValue
{
get
{
//get the enum from businessObject and convert to a string
return EnumUtils.GetDescription(businessObject.Category);
}
set
{
//get the string, convert to an enum and set it in BusinessObject
businessObject.Category = EnumUtils.GetValueFromDescription<NumberCategory>(value);
}
}
}
public static class EnumUtils
{
public static T GetValueFromDescription<T>(string description)
{
var type = typeof(T);
if (!type.IsEnum) throw new InvalidOperationException();
foreach (var field in type.GetFields())
{
var attribute = Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attribute != null)
{
if (attribute.Description == description)
return (T)field.GetValue(null);
}
else
{
if (field.Name == description)
return (T)field.GetValue(null);
}
}
throw new ArgumentException("Not found.", "description");
// or return default(T);
}
public static string GetDescription(Enum value)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
if (name != null)
{
FieldInfo field = type.GetField(name);
if (field != null)
{
DescriptionAttribute attr =
Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr != null)
{
return attr.Description;
}
}
}
return null;
}
}
public partial class Form1 : Form
{
public BusinessObject businessObject;
public Form1()
{
InitializeComponent();
string[] descriptions = Enum.GetValues(typeof(NumberCategory)).Cast<NumberCategory>().Select(e => EnumUtils.GetDescription(e)).ToArray();
comboBox1.DataSource = descriptions;
businessObject = new BusinessObject();
EnumDescriptionAdapter adapter = new EnumDescriptionAdapter(businessObject);
comboBox1.DataBindings.Add(new Binding("SelectedItem", adapter, "CategoryValue"));
}
private void button1_Click(object sender, EventArgs e)
{
businessObject.Category = NumberCategory.PrimeNumber;
}
}
I put a button (button1
) and a combo box (comboBox1
) on my form. When I change the combo box selected item, it does fire the setter in EnumDescriptionAdapter.CategoryValue
and change the businessObject
. However, the reverse is not true: if I press the button it changes the businessObject
but doesn't alter the selected item in comboBox1
.