使用组合框MVVM绑定枚举描述

时间:2013-09-02 08:31:20

标签: c# wpf mvvm dependency-properties

我喜欢使用依赖对象将我的组合框与enum绑定。我搜索并发现这个解决方案非常适合MVVM,

我的xaml是

   <ComboBox SelectedItem="{Binding Color,Mode=TwoWay}" 
                 l:EnumHelper.Enum="{x:Type l:MyEnum }"></ComboBox>

我的依赖对象是

  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;
            }
        }
    }


}

我想知道我是否必须阅读枚举描述并使用依赖对象将其转换回来,我该如何扩展这个助手类。

2 个答案:

答案 0 :(得分:1)

我使用MarkupExtension而不是附加属性的方法略有不同:

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);
    }
}

我发现它更优雅,因为它更短,我可以写ItemsSource="{l:EnumValues {l:MyEnum}}"

对于枚举值描述,我使用转换器:

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
}

所以最后你可以在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>

如果您需要多次,可以在应用程序级别将此DataTemplate定义为资源。

答案 1 :(得分:1)

为了减少重复代码,我使用自定义控件:

以下是自定义EnumComboBox控件的用法:

<myCustomControls:EnumComboBox 
EnumType="{x:Type myEnums:MyEnumType}" 
SelectedEnumValue="{Binding MyBindingProperty}"/>

EnumComboBox只是从ComboBox继承而来:(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;
    }
}

这只是我的首选方式,并希望分享它以防其他人想要使用它。 特别是因为花了一段时间才弄明白:)