Reputation: 2097
Hi Iam binding my combobox with enum using a dependency object.I searched and found this solution very elegant for MVVM,
my xaml is
<ComboBox SelectedItem="{Binding Color,Mode=TwoWay}"
l:EnumHelper.Enum="{x:Type l:MyEnum }"></ComboBox>
and my dependency object is
public class EnumHelper : DependencyObject
{
public static Type GetEnum(DependencyObject obj)
{
return (Type)obj.GetValue(EnumProperty);
}
public static void SetEnum(DependencyObject obj, string value)
{
obj.SetValue(EnumProperty, value);
}
// Using a DependencyProperty as the backing store for Enum. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EnumProperty =
DependencyProperty.RegisterAttached("Enum", typeof(Type), typeof(EnumHelper), new PropertyMetadata(null, OnEnumChanged));
private static void OnEnumChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var control = sender as ItemsControl;
if (control != null)
{
if (e.NewValue != null)
{
var _enum = Enum.GetValues(e.NewValue as Type);
control.ItemsSource = _enum;
}
}
}
}
I want to know if I have to read enum descriptions and convert them back using dependency object,how can I extend this helper class.
Upvotes: 1
Views: 5258
Reputation: 23
To reduce repetitive code I use a custom control:
Below is the usage of the custom EnumComboBox control:
<myCustomControls:EnumComboBox
EnumType="{x:Type myEnums:MyEnumType}"
SelectedEnumValue="{Binding MyBindingProperty}"/>
EnumComboBox just inherits from a ComboBox like so: (EnumComboBox.xaml)
<ComboBox x:Class="MyProject.MyCustomControls.EnumComboBox" />
EnumComboBox.xaml.cs:
public partial class EnumComboBox
{
public EnumComboBox()
{
InitializeComponent();
}
public Type EnumType
{
get { return (Type)GetValue(EnumTypeProperty); }
set { SetValue(EnumTypeProperty, value); }
}
public Enum SelectedEnumValue
{
get { return (Enum)GetValue(SelectedEnumValueProperty); }
set { SetValue(SelectedEnumValueProperty, value); }
}
public static readonly DependencyProperty EnumTypeProperty =
DependencyProperty.Register("EnumType", typeof(Type), typeof(EnumComboBox), new UIPropertyMetadata(null));
public static readonly DependencyProperty SelectedEnumValueProperty =
DependencyProperty.Register("SelectedEnumValue", typeof(Enum), typeof(EnumComboBox), new UIPropertyMetadata(null));
private readonly Dictionary<string, Enum> _conversionDictionary = new Dictionary<string, Enum>();
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if (e.Property == EnumTypeProperty)
{
foreach (var en in Enum.GetValues(EnumType))
{
var descr = Description((Enum)en);
_conversionDictionary.Add(descr, (Enum)en);
}
ItemsSource = _conversionDictionary.Keys.OrderBy(x => x);
}
else if (e.Property == SelectedItemProperty)
{
SelectedEnumValue = _conversionDictionary[e.NewValue.ToString()];
}
else if (e.Property == SelectedEnumValueProperty)
{
SetValue(SelectedItemProperty, Description((Enum)e.NewValue));
}
base.OnPropertyChanged(e);
}
public static string Description(Enum value)
{
if (value == null)
return null;
var enumType = value.GetType();
var field = enumType.GetField(value.ToString());
if (field == null)
return null;
var attr = field.GetCustomAttributes(typeof(DescriptionAttribute), true)
.Cast<DescriptionAttribute>()
.FirstOrDefault();
return attr == null ? value.ToString() : attr.Description;
}
}
This is just my preferred way and wanted to share it in case anyone else wants to use it. Especially since it took a while to figure out :)
Upvotes: 1
Reputation: 34407
I use slightly different approach with MarkupExtension
instead of attached property:
public sealed class EnumValues : MarkupExtension
{
private readonly Type _enumType;
public EnumValues(Type enumType)
{
_enumType = enumType;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Enum.GetValues(_enumType);
}
}
I find this more elegant as it is shorter and I can write ItemsSource="{l:EnumValues {l:MyEnum}}"
.
As for enum value descriptions, i use a converter:
public sealed class EnumValueToDecriptionConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(value == null)
{
return null;
}
var type = value.GetType();
if(!type.IsEnum)
{
return null;
}
var field = type.GetField(value.ToString());
var attr = field.GetCustomAttributes(typeof(DescriptionAttribute), true)
.Cast<DescriptionAttribute>()
.FirstOrDefault();
if(attr != null)
{
return attr.Description;
}
else
{
return field.Name;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
So finally you can write this in XAML:
<ComboBox SelectedItem="{Binding Color, Mode=TwoWay}"
ItemsSource="{l:EnumValues {x:Type l:MyEnum}}">
<FrameworkElement.Resources>
<l:EnumValueToDecriptionConverter x:Key="EnumValueToDecriptionConverter" />
</FrameworkElement.Resources>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Mode=OneTime,
Converter={StaticResource EnumValueToDecriptionConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
You can define this DataTemplate
as a resource at application level if you need it more than once.
Upvotes: 4