使用绑定来为DataTrigger条件的Value属性进行绑定

40

我正在开发一个WPF应用程序,遇到了数据触发器的问题。 我想将触发器条件的值绑定到一个对象上:

<DataTrigger Binding="{Binding Foo}" 
             Value="{Binding ElementName=AnotherElement, Path=Bar}">..

然而,由于似乎无法对Value属性使用绑定,我无法这么做。是吗?有什么办法可以实现吗?我会收到以下错误提示: “Binding”不能在类型为“DataTrigger”的“Value”属性上设置。“Binding”只能设置为DependencyObject的DependencyProperty的属性。

进一步阐述答案:这里有一个示例,展示了如何实现。 - BlazingFrog
4个回答

48

不可能。就像错误信息所说的那样,只有依赖属性才能成为WPF绑定的目标,而DataTrigger.Value不是依赖属性。因此,您需要分配一个实际值。

解决方法是使用MultiBinding,其子绑定是要比较的两个绑定,使用IMultiValueConverter,如果两个输入相等则返回true,否则返回false。然后,DataTrigger可以使用该MultiBinding,并设置Value为True。


16

这是一个使用 IMultiValueConverter 转换器的示例。

这个 XAML 中有三个复选框。前两个绑定到 Foo 和 Bar 属性(都是布尔类型)。第三个使用多重绑定与 IMultiValueConverter。当 Foo 和 Bar 具有相同的值时,它将被选中。

<!-- It's expected that the DataContext of this StackPanel has boolean Bar and Foo properties.. -->
<StackPanel Orientation="Vertical">
    <StackPanel.Resources>
        <!-- local contains the MultiValueEqualityConverter class implementation -->
        <local:MultiValueEqualityConverter x:Key="multiValueEqualityConverter"/>
    </StackPanel.Resources>

    <CheckBox IsChecked="{Binding Foo}">Foo</CheckBox>
    <CheckBox IsChecked="{Binding Bar}">Bar</CheckBox>

    <CheckBox IsEnabled="False" Content="Are the same">
        <CheckBox.Style>
            <Style TargetType="CheckBox">
                <Style.Setters>
                    <Setter Property="IsChecked" Value="False"/>
                </Style.Setters>
                <Style.Triggers>
                    <DataTrigger Value="True">
                        <DataTrigger.Binding>
                            <MultiBinding Converter="{StaticResource multiValueEqualityConverter}">
                                <Binding RelativeSource="{RelativeSource self}" Path="DataContext.Foo" Mode="OneWay" />
                                <Binding RelativeSource="{RelativeSource self}" Path="DataContext.Bar" Mode="OneWay"/>
                            </MultiBinding>
                        </DataTrigger.Binding>
                        <Setter Property="IsChecked" Value="True" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </CheckBox.Style>
    </CheckBox>
</StackPanel>

一个用于单向绑定的简单IMultiValueConverter实现:

public class MultiValueEqualityConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        return values?.All(o => o?.Equals(values[0]) == true) == true || values?.All(o => o == null) == true;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

4
我使用MVVM。由于我已经多次回到这个答案,值得一提的是,每次我最终都得到相同的结果:
对于每个项目都要制作一个模型,而不是进行比较。
例如,我通常在ItemsControl.DataTemplate中有一堆行。我尝试使用DataTrigger通过与动态Value={Binding}进行比较来设置IsEnabled(或其他东西)。
就在那时,我的代码失败了,我去了SO,最终来到了这里。
几乎毫无例外的是,我随后决定在ViewModel中维护一个行模型列表,它们处理自己的IsEnabled并相应地通知UI。
我将它们用于ItemsControl.Source,然后想知道为什么我一开始就没这样做。

4
DataTrigger的Value属性不是可绑定的DependencyProperty。因此,我们要RegisterAttached一个可以绑定并设置DataTrigger的Value属性值的依赖属性,每当附加的依赖属性值被设置时,它都会被更新。
以下是一个名为DataTriggerAssists的示例类。
public class DataTriggerAssists
{
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.RegisterAttached(
            "Value",
            typeof(object),
            typeof(DataTriggerAssists),
            new FrameworkPropertyMetadata(null, OnValueChanged));

    public static object GetValue(DependencyObject d)
    {
        return d.GetValue(ValueProperty);
    }

    public static void SetValue(DependencyObject d, object value)
    {
        d.SetValue(ValueProperty, value);
    }

    public static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
    {
        if (d is DataTrigger trigger)
            trigger.Value = args.NewValue;
    }
}

声明一个名为as的前缀,其命名空间为DataTriggerAssists类。

然后你可以像这样使用。

<DataTrigger Binding="{Binding Foo}" 
             as:DataTriggerAssists.Value="{Binding ElementName=AnotherElement, Path=Bar}">..

最好解释一下为什么这样做可以解决问题。 - Coder

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