WPF binding ComboBox to enum (with a twist)
I would suggest a DataTemplate and a ValueConverter. That will let you customize the way it's displayed, but you would still be able to read the combobox's SelectedItem property and get the actual enum value.
ValueConverters require a lot of boilerplate code, but there's nothing too complicated here. First you create the ValueConverter class:
public class ModeConverter : IValueConverter{ public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return ((Mode) value).GetDescription(); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotSupportedException(); }}
Since you're only converting enum values to strings (for display), you don't need ConvertBack -- that's just for two-way binding scenarios.
Then you put an instance of the ValueConverter into your resources, with something like this:
<Window ... xmlns:WpfApplication1="clr-namespace:WpfApplication1"> <Window.Resources> <WpfApplication1:ModeConverter x:Key="modeConverter"/> </Window.Resources> ....</Window>
Then you're ready to give the ComboBox a DisplayTemplate that formats its items using the ModeConverter:
<ComboBox Name="comboBox" ...> <ComboBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Converter={StaticResource modeConverter}}"/> </DataTemplate> </ComboBox.ItemTemplate></ComboBox>
To test this, I threw in a Label too, that would show me the actual SelectedItem value, and it did indeed show that SelectedItem is the enum instead of the display text, which is what I would want:
<Label Content="{Binding ElementName=comboBox, Path=SelectedItem}"/>
This is how I am doing it with MVVM. On my model I would have defined my enum:
public enum VelocityUnitOfMeasure { [Description("Miles per Hour")] MilesPerHour, [Description("Kilometers per Hour")] KilometersPerHour }
On my ViewModel I expose a property that provides possible selections as string as well as a property to get/set the model's value. This is useful if we don't want to use every enum value in the type:
//UI Helper public IEnumerable<string> VelocityUnitOfMeasureSelections { get { var units = new [] { VelocityUnitOfMeasure.MilesPerHour.Description(), VelocityUnitOfMeasure.KilometersPerHour.Description() }; return units; } } //VM property public VelocityUnitOfMeasure UnitOfMeasure { get { return model.UnitOfMeasure; } set { model.UnitOfMeasure = value; } }
Furthermore, I use a generic EnumDescriptionCoverter:
public class EnumDescriptionConverter : IValueConverter{ //From Binding Source public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (!(value is Enum)) throw new ArgumentException("Value is not an Enum"); return (value as Enum).Description(); } //From Binding Target public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { if (!(value is string)) throw new ArgumentException("Value is not a string"); foreach(var item in Enum.GetValues(targetType)) { var asString = (item as Enum).Description(); if (asString == (string) value) { return item; } } throw new ArgumentException("Unable to match string to Enum description"); }}
And finally, with the view I can do the following:
<Window.Resources> <ValueConverters:EnumDescriptionConverter x:Key="enumDescriptionConverter" /></Window.Resources>...<ComboBox SelectedItem="{Binding UnitOfMeasure, Converter={StaticResource enumDescriptionConverter}}" ItemsSource="{Binding VelocityUnitOfMeasureSelections, Mode=OneWay}" />
I like the way you think. But GetCustomAttributes
uses reflection
. What is that going to do to your performance?
Check out this post:WPF - Displaying enums in ComboBox controlhttp://www.infosysblogs.com/microsoft/2008/09/wpf_displaying_enums_in_combob.html