改进枚举到组合框的绑定(使用MarkupExtension)

3

我希望尽可能地简化将枚举绑定到ComboBox的过程。

在多种解决方案(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="Display"。

是否有可能用类操作符(EnumMember内部)或类似的东西来替换SelectedValuePath="Value"属性?

谢谢!

2个回答

4

有很多方法可以实现这个:

方法一

一个标记扩展的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();
}

方法二

如果你的应用程序中所有的下拉框都将绑定到包装类EnumMember,你可以在Application.Resources的全局样式下定义它,这样你就不必为每个下拉框都复制一份。

即使您没有将ComboBox绑定到枚举值,也可以针对每个实例覆盖SelectedValuePath。

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

第三种方法

您可以对 ComboBox 进行子类化,并在 OnItemsSourceChanged 中设置 SelectedValuePathValue,以防 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
无法这样做,因为SelectedValueSelectedItem中提取特定属性的值,基于在SelectedValuePath中提到的属性。也就是说,SelectedValue和SelectedValuePath配合使用。但是,如果您想不提及SlectedValuePath而完成它,则使用Binding的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}"/>

感谢您的解释,尽管我没有成功使用SelectedItem绑定。您如何指定确切的路径? - Profet
我更新了答案。请查看更新,我尝试过并且对我有效。您可以通过 ViewModel 中的 SelectedItem.Value 简单地获取该值。 - yo chauhan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接