Databinding an enum property to a ComboBox in WPF

2019-01-01 04:27发布

As an example take the following code:

public enum ExampleEnum { FooBar, BarFoo }

public class ExampleClass : INotifyPropertyChanged
{
    private ExampleEnum example;

    public ExampleEnum ExampleProperty 
    { get { return example; } { /* set and notify */; } }
}

I want a to databind the property ExampleProperty to a ComboBox, so that it shows the options "FooBar" and "BarFoo" and works in mode TwoWay. Optimally I want my ComboBox definition to look something like this:

<ComboBox ItemsSource="What goes here?" SelectedItem="{Binding Path=ExampleProperty}" />

Currently I have handlers for the ComboBox.SelectionChanged and ExampleClass.PropertyChanged events installed in my Window where I do the binding manually.

Is there a better or some kind of canonical way? Would you usually use Converters and how would you populate the ComboBox with the right values? I don't even want to get started with i18n right now.

Edit

So one question was answered: How do I populate the ComboBox with the right values.

Retrieve Enum values as a list of strings via an ObjectDataProvider from the static Enum.GetValues method:

<Window.Resources>
    <ObjectDataProvider MethodName="GetValues"
        ObjectType="{x:Type sys:Enum}"
        x:Key="ExampleEnumValues">
        <ObjectDataProvider.MethodParameters>
            <x:Type TypeName="ExampleEnum" />
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</Window.Resources>

This I can use as an ItemsSource for my ComboBox:

<ComboBox ItemsSource="{Binding Source={StaticResource ExampleEnumValues}}"/>

标签: .net wpf
13条回答
柔情千种
2楼-- · 2019-01-01 05:28

My favorite way to do this is with a ValueConverter so that the ItemsSource and SelectedValue both bind to the same property. This requires no additional properties to keep your ViewModel nice and clean.

<ComboBox ItemsSource="{Binding Path=ExampleProperty, Converter={x:EnumToCollectionConverter}, Mode=OneTime}"
          SelectedValuePath="Value"
          DisplayMemberPath="Description"
          SelectedValue="{Binding Path=ExampleProperty}" />

And the definition of the Converter:

public static class EnumHelper
{
  public static string Description(this Enum e)
  {
    return (e.GetType()
             .GetField(e.ToString())
             .GetCustomAttributes(typeof(DescriptionAttribute), false)
             .FirstOrDefault() as DescriptionAttribute)?.Description ?? e.ToString();
  }
}

[ValueConversion(typeof(Enum), typeof(IEnumerable<ValueDescription>))]
public class EnumToCollectionConverter : MarkupExtension, IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    return Enum.GetValues(value.GetType())
               .Cast<Enum>()
               .Select(e => new ValueDescription() { Value = e, Description = e.Description()})
               .ToList();
  }
  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    return null;
  }
  public override object ProvideValue(IServiceProvider serviceProvider)
  {
    return this;
  }
}

This converter will work with any enum. ValueDescription is just a simple class with a Value property and a Description property. You could just as easily use a Tuple with Item1 and Item2, or a KeyValuePair with Key and Value instead of Value and Description or any other class of your choice as long as it has can hold an enum value and string description of that enum value.

查看更多
登录 后发表回答