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

时间:2016-04-27 17:33:47

标签: c# wpf xaml controltemplate wpf-style

考虑这个(已编辑过的)Style,专为Button Content String设计的<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>

TemplatedParent

此示例中的意图是将按钮文本变为红色,如果它等于单词&#34; Test&#34; 1 。但它不起作用,因为触发器的Button绑定解析为null而不是Style TextBlock应用于Text。但是,TemplatedParent名为&#34; demo&#34;将DataTrigger设置为&#34; System.Windows.Controls.Button:[ButtonText]&#34;正如预期的那样,这意味着{{1}}在该级别上正常工作。为什么它在{{1}}内无效?

<小时/> 1 我知道还有其他方法可以实现这一点,但我试图理解为什么绑定不会像我期望的那样工作。

3 个答案:

答案 0 :(得分:6)

TemplatedParent中的{p> ControlTemplate.Triggers不符合您的预期。内部触发器实际上引用了Button.TemplatedParent。因此,如果您在模板中创建按钮,它将仅为非null。您没有在模板中创建按钮,因此在您的情况下它是null。现在考虑这个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,在模板内部,我使用按钮和模板。如果您运行此代码,您将看到&#34; dummy&#34;文字为红色,因为Button.TemplatedParent现在为ContentControl,并且它有Content等于&#34;测试&#34;,这证实了我上面所说的内容。

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

答案 1 :(得分:0)

我不太确定,但我认为触发器等于referenc,因为Content返回一个Object。因此,在触发器中定义的字符串永远不会成为现实。

答案 2 :(得分:0)

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

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

所以我的最终代码如下:

<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>