刷新与绑定到其他转换器的绑定 属性

Refresh binding with converter bound to other property

我目前正在为 Windows 10 构建一个 WinRT 应用程序,我遇到了一个我似乎找不到答案的问题。

在我的主页中,我有一个绑定到 ViewModel 中的 ObservableCollection 的地图标记列表。对于这些标记中的每一个,我需要根据我的 ViewModel 的另一个 属性(我们称之为 PropertySelector)的值显示一个文本,该文本可以是我的 MapMarker class 中的 Property1 或 Property2。

我找到的最佳解决方案是在 MapMarker class 中创建一个包含 Property1 和 Property2 的结构,将其绑定到标记的文本字段并使用转换器选择要显示的结构。

由于您无法将 属性 绑定到 ConverterParameter,因此我在 Converter 中实现了一个 DependencyProperty 以使其能够访问 PropertySelector。 DP 工作正常,转换器中的 属性 得到更新,但标记永远不会更新。我知道这是因为我没有触发任何实际告诉标记更新的事件,但我没有通过添加 PropertyChanged("MarkerList") 来实现它到 PropertySelector setter 或者当我用 GetBinding(Text).UpdateSource() 更改 属性 时尝试以编程方式刷新绑定方式似乎与 WPF 有不同的实现。

我这样做对吗?我该怎么做才能强制刷新绑定?

这是我的相关代码:

MainPage.xaml

<Page.Resources>
        <local:PropertySelectorConverter x:Key="propertySelectorConverter" 
                                   PropertySelector="{Binding PropertySelector}" />
</Page.Resources>

...

<Maps:MapControl>
    <Maps:MapItemsControl ItemsSource="{Binding MarkerList}">
        <Maps:MapItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Properties, Converter={StaticResource propertySelectorConverter}}" />
            </DataTemplate>
        </Maps:MapItemsControl.ItemTemplate>
    </Maps:MapItemsControl>
</Maps:MapControl>
<Button Text="Switch Data" Click="SwitchButton_Click" />

MainPage.xaml.cs

public void SwitchButton_Click(object sender, EventArgs e)
{
    viewModel.PropertySelector= !viewModel.PropertySelector
}

ViewModel.cs

class ViewModel : INotifyPropertyChanged
{
    private ObservableCollection<Marker> markerList = new ObservableCollection<Marker>();
    public ObservableCollection<Marker> MarkerList
    {
        get { return markerList; }
        set { markerList = value; OnPropertyChanged("MarkerList"); }
    }

    private bool propertySelector = false;
    public bool PropertySelector
    {
        get { return propertySelector; }
        set { propertySelector = value; OnPropertyChanged("PropertySelector"); }
    }
}

Marker.cs

public class Marker
{
    public Tuple<double, double> Properties { get; set; } = Tuple.Create(10, 7);
}

Converter.cs

public class PropertySelectorConverter : DependencyObject, IValueConverter
{
    public bool PropertySelector
    {
        get { return (bool)GetValue(PropertySelectorProperty); }
        set { SetValue(PropertySelectorProperty, value); }
    }

    public static readonly DependencyProperty PropertySelectorProperty =
        DependencyProperty.Register("PropertySelector", typeof(bool), typeof(PropertySelectorConverter), new PropertyMetadata(null, CurrentItemChangedCallback));

    private static void CurrentItemChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
    {

    }

    public object Convert(object value, Type targetType, object parameter, string language)
    {
        var properties = (Tuple<double, double>)value;
        return PropertySelector ? properties.Item1 : properties.Item2;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

感谢您的宝贵时间。

由于 a good, minimal, complete code example 无法清楚地说明您的问题,因此很难甚至不可能提供具体建议。但是有一些一般的想法要分享……

首先,根据我的经验,ConverterParameter 对于向转换器提供静态(即编译时)信息更有用。例如。当您编写了一个通用转换器,该转换器需要给定绑定的一些特定数据,但该数据值在编译时已知。

在您的场景中,您实际上有多个在 运行 时间变化的转换器输入值。对于这种情况,恕我直言,使用 MultiBinding 更合适。这允许您提供两个或更多绑定源,如果其中任何一个源发生更改,WPF 将重新计算绑定值。不幸的是,这是一个 WPF 功能,并且与许多非常有用的 WPF 功能一样,已从 Windows Store/Winrt API.

中省略

但是,您可以构建一个简单的中间视图模型 class 来完成相同的任务。例如:

class MultiBindingViewModel : DependencyObject
{
    public static readonly DependencyProperty PropertiesProperty = DependencyProperty.Register(
        "Properties", typeof(Tuple<double, double>), typeof(MultiBindingViewModel), new PropertyMetadata(null, OnPropertyChanged);
    public static readonly DependencyProperty PropertySelectorProperty = DependencyProperty.Register(
        "PropertySelector", typeof(bool), typeof(MultiBindingViewModel), new PropertyMetadata(null, OnPropertyChanged);
    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
        "Value", typeof(double), typeof(MultiBindingViewModel), null);

    public Tuple<double, double> Properties
    {
        get { return (Tuple<double, double>)GetValue(PropertiesProperty); }
        set { SetValue(PropertiesProperty, value); }
    }

    public bool PropertySelector
    {
        get { return (bool)GetValue(PropertySelectorProperty); }
        set { SetValue(PropertySelectorProperty, value); }
    }

    public double Value
    {
        get { return (double)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        MultiBindingViewModel model = (MultiBindingViewModel)d;

        model.Value = model.PropertySelector ? model.Properties.Item1 : model.Properties.Item2;
    }
}

然后在您的 XAML 中使用它,例如:

<TextBlock>
    <TextBlock.Text>
        <Binding Path="Value">
            <Binding.Source>
                <local:MultiBindingViewModel Properties="{Binding Properties}"
                                             PropertySelector="{Binding PropertySelector}/>
            </Binding.Source>
        </Binding>
    </TextBlock.Text>
</TextBlock>

警告:缺少完整的代码示例,以上只是浏览器编写的代码。可能存在语法错误,或者我什至遗漏了一些 Windows 存储所需的关键代码。当然,确切的绑定源、路径和 XML 命名空间可能需要一些调整,因为我无法确定您是如何设置数据上下文等的。

但希望上面的内容足够清楚地展示了您可以在项目中使用它的基本方法。


为了完整起见,使用 MultiBinding 的 WPF 方法如下所示:

A MultiBinding总是 有一个转换器,实现 IMultiValueConverter。该接口的 Convert() 方法看起来类似于 IValueConverter 的方法,只是它具有 object[] values 参数而不是允许转换单个输入值的 object value 参数.

根据您提供的代码,我希望您的转换器看起来像这样:

public class PropertySelectorConverter : IMultiValueConverter
{    
    public object Convert(object[] values, Type targetType, object parameter, string language)
    {
        var properties = (Tuple<double, double>)values[0];
        bool propertySelector = (bool)values[1];

        return propertySelector ? properties.Item1 : properties.Item2;
    }

    public object ConvertBack(object[] values, Type targetType, object parameter, string language)
    {
        throw new NotSupportedException();
    }
}

然后在你的 XAML 中,你会做这样的事情:

<TextBlock>
    <TextBlock.Text>
        <MultiBinding Converter="{StaticResource propertySelectorConverter}">
            <Binding Source="." Path="Properties"/>
            <Binding Source="." Path="PropertySelector"/>
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>