将属性绑定到依赖项属性时触发器不起作用

本文关键字:属性 触发器 不起作用 依赖 绑定 | 更新日期: 2023-09-27 17:55:32

我正在尝试使用触发器设置矩形 (WPF) 的颜色,具体取决于布尔依赖项属性,我将其绑定到矩形的 Tag 属性。

我有以下代码:

 public partial class MainWindow : Window
 {
    public Boolean isAutoStart
    {
        get { return (Boolean)GetValue(isAutoStartProperty); }
        set { SetValue(isAutoStartProperty, value); }
    }
    public static readonly DependencyProperty isAutoStartProperty =
        DependencyProperty.Register("isAutoStart", typeof(Boolean),
        typeof(MainWindow), new PropertyMetadata(true));
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
       isAutoStart = false;
    }
 }

在 XAML 中:

<Window.Resources>
    <Style x:Key="TriggerDark" TargetType="Rectangle">
        <Setter Property="Fill" Value="Green" />
        <Style.Triggers>
            <Trigger Property="Tag" Value="False">
                <Setter Property="Fill" Value="Red" />
            </Trigger>
            <Trigger Property="Tag" Value="True">
                <Setter Property="Fill" Value="Green" />
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>
<Rectangle Style="{StaticResource ResourceKey=TriggerDark}" Tag="{Binding Path=isAutoStart, UpdateSourceTrigger=PropertyChanged}">

如果我将"True"或"False"硬编码到矩形的标签属性中,触发器将正常工作。如果我在运行时将 tag 属性的值打印到控制台,绑定有效,但触发器不会触发。

知道我做错了什么吗?

谢谢!

将属性绑定到依赖项属性时触发器不起作用

触发器尝试将布尔值 true 与字符串"True"进行比较,因为 Tag 是 Object 属性,因此将存储布尔值,而触发器的值是字符串。PHP想要它,而不是WPF。;)

如果要保留触发器而不是数据触发器,可以创建一个静态类:

public static class BooleanHelper {
    public static bool False {
        get { return false; }
    }
    public static bool True {
        get { return true; }
    }
}

然后样式将写成:

<Style x:Key="TriggerDark" TargetType="Rectangle">
    <Setter Property="Fill" Value="Green"/>
    <Style.Triggers>
        <Trigger Property="Tag" Value="{x:Static local:BooleanHelper.True}">
            <Setter Property="Fill" Value="Red" />
        </Trigger>
    </Style.Triggers>
</Style>

感谢Michael Mairegger的想法。

您需要设置绑定的DataContext。现在,您的绑定指向 Rectangle.DataContext.isAutoStart ,但是Rectangle.DataContext为 null,因此您的绑定不会解析为任何内容。有关DataContext的更多详细信息,请参阅此答案。

因为您在此处提到您不想使用 DataTrigger 进行硬编码,所以您可能希望手动设置绑定Source以将可视化树搜索到第一个Window对象,并绑定到该对象的 isAutoStart 属性。

<Rectangle Tag="{Binding Path=isAutoStart, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" .. />

正如此答案所指出的那样,您还遇到了额外的问题,您将布尔值与字符串进行比较,因此它始终评估为 False。有很多方法可以解决这个问题,例如IValueConverter,但我发现最简单的方法是导入系统命名空间并为布尔 true 创建一个静态值,如下所示:

<s:Boolean x:Key="TrueValue">True</s:Boolean>
...
<Trigger Property="Tag" Value="{StaticResource TrueValue}">

其中s命名空间定义为

xmlns:s="clr-namespace:System;assembly=mscorlib"

用户 nkoniishvt 已经给出了解释,触发器将Tag属性值与字符串文本"True"和"False"进行比较,而不是bool值。

此问题的另一种解决方法可能是不使用 Tag 属性,而是使用正确类型的附加属性,例如声明

public static class StyleHelper
{
    public static readonly DependencyProperty StateProperty =
        DependencyProperty.RegisterAttached(
            "State", typeof(bool), typeof(StyleHelper));
    public static bool GetState(DependencyObject obj)
    {
        return (bool)obj.GetValue(StateProperty);
    }
    public static void SetState(DependencyObject obj, bool value)
    {
        obj.SetValue(StateProperty, value);
    }
}

你将在 XAML 中使用它,如下所示:

<Window.Resources>
    <Style TargetType="Rectangle">
        <Setter Property="Fill" Value="Green" />
        <Style.Triggers>
            <Trigger Property="local:StyleHelper.State" Value="False">
                <Setter Property="Fill" Value="Red" />
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>
<Rectangle local:StyleHelper.State="{Binding isAutoStart}}" />

您可以尝试使用 DataTrigger:

(请注意,您也可以通过删除触发器来举例说明您的风格)

       <Style x:Key="TriggerDark" TargetType="Rectangle">
            <Setter Property="Fill" Value="Green" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=isAutoStart}" Value="False">
                    <Setter Property="Fill" Value="Red" />
                </DataTrigger>
            </Style.Triggers>
        </Style>

同时将数据上下文设置为窗口:

public MainWindow()
{
    DataContext = this;
}

编辑:如果你愿意,你可以使用IValueConverter:

public class BoolToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
    {
        if (targetType != typeof(bool))
            throw new InvalidOperationException("The target must be a boolean");
        return value.ToString();
    }
    public object ConvertBack(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

并将绑定更改为:

    <Window.Resources>
        <local:BoolToStringConverter x:Key="BtSConv"/>
        <Style x:Key="TriggerDark" TargetType="Rectangle">
            <Setter Property="Fill" Value="Green" />
            <Style.Triggers>
                <Trigger Property="Tag" Value="False">
                    <Setter Property="Fill" Value="Red" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Rectangle Style="{StaticResource TriggerDark}" Tag="{Binding isAutoStart, Converter={StaticResource BtSConv}}" />

甚至有很多来自其他人的美丽解决方案。