在 ControlTemplate 的 DataTrigger 中使用时,TemplatedParent 为空

TemplatedParent is null when used inside a ControlTemplate's DataTrigger

考虑这个(编辑过的)Style,专为 Button 设计,其 ContentString:

<Style x:Key="Test" TargetType="Button">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
               <StackPanel>
                   <TextBlock x:Name="text" Text="{TemplateBinding Content}" />
                   <TextBlock x:Name="demo" Text="{Binding RelativeSource={RelativeSource TemplatedParent}}" />
                </StackPanel>
                <ControlTemplate.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}">
                        <DataTrigger.Value>
                            <system:String>Test</system:String>
                        </DataTrigger.Value>
                        <Setter TargetName="test" Property="Foreground" Value="Red" />
                    </DataTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

此示例的目的是在按钮文本等于单词 "Test"1 时将其变为红色。但它不起作用,因为触发器的 TemplatedParent 绑定解析为 null 而不是应用 StyleButton。但是,名为 "demo" 的 TextBlock 将按预期将其 Text 设置为 "System.Windows.Controls.Button: [ButtonText]",这意味着 TemplatedParent 在该级别可以正常工作。为什么它在 DataTrigger 中不起作用?


1 我知道还有其他方法可以实现这一点,但我想了解为什么绑定无法按我预期的方式工作。

我不太确定,但我认为触发器等于引用,因为内容 returns 是一个对象。因此,您在触发器中定义的字符串永远不会成立。

TemplatedParent 在您的 ControlTemplate.Triggers 中不是您所期望的。在触发器内部,它实际上引用了 Button.TemplatedParent。因此,如果您在模板中创建 button ,它只会是非空的。您不会在模板内创建按钮,因此在您的情况下它为空。现在考虑这个 xaml:

<Window.Resources>
    <Style x:Key="Test"
           TargetType="Button">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <StackPanel>
                        <TextBlock x:Name="text"
                                   Text="dummy" />
                        <TextBlock x:Name="demo"
                                   Text="{Binding RelativeSource={RelativeSource TemplatedParent}}" />
                    </StackPanel>
                    <ControlTemplate.Triggers>
                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}">
                            <DataTrigger.Value>
                                <system:String>Test</system:String>
                            </DataTrigger.Value>
                            <Setter TargetName="text"
                                    Property="Foreground"
                                    Value="Red" />
                        </DataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style x:Key="Test2" TargetType="ContentControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ContentControl">
                    <Button Style="{StaticResource Test}"></Button>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<Grid>
    <!--<Button Content="Test" Style="{StaticResource Test}"/>-->
    <ContentControl Style="{StaticResource Test2}" Content="Test" />
</Grid>

我在这里重新模板化 ContentControl 并在模板内部使用 按钮和您的模板 。如果您 运行 此代码,您将看到 "dummy" 红色文本,因为 Button.TemplatedParent 现在是 ContentControl,并且 Content 等于 "Test",这证实了我上面说的。

现在回到您的问题:只需将 RelativeSource TemplatedParent 更改为 RelativeSource Self(无需将 DataTrigger 更改为 Trigger)- 这个将引用您的 Button。

我认为这可能是 .NET Core WPF 中的类似问题。 我的 DataTrigger 没有用 {Binding MyProp, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource Convert}} 触发,而是当我将 RelativeSource 更改为 Self 时绑定开始工作。我不确定这是黑客攻击还是解决方案,但它确实有效。

也许值得一提的是,我的模板基于 MyView(见下文)并且我绑定到 MyView 上的 DependencyProperty

所以我的最终代码如下所示:

<ControlTemplate x:Key="Template" TargetType="{x:Type ns:MyView}">
    <!-- Some template -->

    <ControlTemplate.Triggers>
        <DataTrigger Binding="{Binding MyProp, RelativeSource={RelativeSource Self}, Converter={StaticResource Convert}}" Value="True">
            <Setter Property="Foreground" Value="Red"/>
        </DataTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>