改进Enum-to-Combobox绑定(使用MarkupExtension)

时间:2014-01-03 19:16:47

标签: c# wpf binding member

我正在努力简化Enums与ComboBoxes的绑定。

在多个解决方案(ObjectDataProvider,Converter ...)中,我选择了以下MarkupExtension:

public class EnumSource : MarkupExtension
{

    public class EnumMember
    {
        public string Display { get; set; }
        public object Value { get; set; }

        public override string ToString()
        {
            return Display;
        }
    }


    private readonly Type EnumType;

    public EnumSource(Type type)
    {
        EnumType = type;
    }


    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var enumValues = Enum.GetValues(EnumType);
        return (
          from object enumValue in enumValues
          select new EnumMember
          {
              Value = enumValue,
              Display = GetDescription(enumValue)
          }).ToArray();
    }


    private string GetDescription(object enumValue)
    {
        var descriptionAttribute = EnumType
                                      .GetField(enumValue.ToString())
                                      .GetCustomAttributes(typeof(DescriptionAttribute), false)
                                      .FirstOrDefault() as DescriptionAttribute;

        return (descriptionAttribute != null) ? descriptionAttribute.Description : enumValue.ToString();
    }

}


<ComboBox ItemsSource="{Binding Source={my:EnumSource {x:Type my:Options}}}" SelectedValue="{Binding Path=CurrentOption}" SelectedValuePath="Value" />

您可以注意到我设法通过在EnumMember类中添加ToString()方法来摆脱 DisplayMemberPath =“显示”

是否可以用类操作符(在EnumMember中)或类似的东西替换 SelectedValuePath =“Value”属性?

谢谢!

2 个答案:

答案 0 :(得分:4)

有很多方法可以实现这一目标:

方法1

标记扩展的ProvideValue方法采用IServiceProvider类型的参数,该参数提供IProvideValueTarget服务等。此接口公开属性TargetObject,允许检索目标对象(在您的情况下为组合框)。

您可以像这样设置SelectedValuePath

public override object ProvideValue(IServiceProvider serviceProvider)
{
    IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
    if (target != null && target.TargetObject is ComboBox)
    {
        ((ComboBox)target.TargetObject).SelectedValuePath = "Value";
    }

    var enumValues = Enum.GetValues(EnumType);
    return (
       from object enumValue in enumValues
       select new EnumMember
       {
          Value = enumValue,
          Display = GetDescription(enumValue)
       }).ToArray();
}

方法2

如果你的应用中的所有组合框都被绑定到包装类EnumMember,你可以在global style of ComboBox under Application.Resources下定义它,这样你就不必为每个组合框复制它。

即使以防万一,你没有将ComboBox绑定到枚举值,也可以为每个实例覆盖SelectedValuePath。

<Application.Resources>
   <Style TargetType="ComboBox">
      <Setter Property="SelectedValuePath" Value="Value"/>
   </Style>
</Application.Resources>

方法3

你可以继承ComboBox和OnItemsSourceChanged,设置SelectedValuePath to Value,以防 ItemsSource是EnumMember类的数组

public class MyComboBox : ComboBox
{
    protected override void OnItemsSourceChanged(IEnumerable oldValue,
                                                 IEnumerable newValue)
    {
        if (newValue != null && 
            newValue.GetType().Equals(typeof(EnumSource.EnumMember[])))
        {
            SelectedValuePath = "Value";
        }
        base.OnItemsSourceChanged(oldValue, newValue);
    }
}

在XAML中的用法:

<my:MyComboBox ItemsSource="{my:EnumSource {x:Type my:Options}}"
               SelectedValue="{Binding Path=CurrentOption}"/>

在旁注中,您创建了自己的标记扩展,您根本不需要Binding。您可以像这样绑定ItemsSource

    <ComboBox ItemsSource="{my:EnumSource {x:Type my:Options}}"
              SelectedValue="{Binding CurrentOption}"/>

答案 1 :(得分:1)

不可能这样做是因为 SelectedValue 根据 SelectedValuePath 中提到的属性从 SelectedItem 中提取特定属性的值.Means SelectedValue和SelectedValuePath一起工作。但是,如果您想在不提及SlectedValuePath的情况下执行此操作,请使用 SelectedItem 属性进行绑定。

  

更新这是ViewModel中的属性

    EnumSource.EnumMember selectedItem;

    public  EnumSource.EnumMember SelectedItem
    { 
        get{return  selectedItem;}
        set { selectedItem = value; OnPropertyChanged("SelectedItem");}
    }
  

XAML

<ComboBox SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding Source={local:EnumSource {x:Type local:DataType}}}" SelectedValue="{Binding Path=CurrentOption}"/>