WPF ControlTemplate打破了风格

时间:2013-01-24 14:02:17

标签: wpf xaml controltemplate nested

有用的东西

我需要设置某种类型的控件,这些控件是StackPanel的子控件。我正在使用:

<StackPanel>
    <StackPanel.Resources>
        <Style TargetType="{x:Type TextBlock}">...</Style>
    </StackPanel.Resources>
    <TextBlock ...>
    ...
</StackPanel>

这很好用!每个TextBlock都会查看它的父级(StackPanel)的资源,以了解它应该如何设置样式。将TextBlock嵌套到StackPanel上的距离无关紧要...如果它在其直接父级中找不到样式,它将查看其父级的父级,依此类推,直到找到某些东西(在这种情况下) ,()中定义的风格。

不起作用的东西

当我在具有模板的ContentControl中嵌套TextBlock时遇到了问题(请参阅下面的代码)。 ControlTemplate似乎破坏了TextBlock从父母,祖父母那里检索其风格的方式......

ControlTemplate的使用有效地似乎消除了TextBlock找到其合法风格的手段(StackPanel.Resources中的风格)。遇到ControlTemplate时,它会停止在树中的资源中查找其样式,而是默认使用Application本身的MergedDictionaries中的样式。

<StackPanel Orientation="Vertical" Background="LightGray">
    <StackPanel.Resources>
        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="Foreground" Value="Green" />
        </Style>
    </StackPanel.Resources>

    <TextBlock Text="plain and simple in stackpanel, green" />
    <ContentControl>
        <TextBlock Text="inside ContentControl, still green" />
    </ContentControl>
    <ContentControl>
        <ContentControl.Template>
            <ControlTemplate TargetType="{x:Type ContentControl}">
                <StackPanel Orientation="Vertical">
                    <ContentPresenter />
                    <TextBlock Text="how come this one - placed in the template - is not green?" />
                </StackPanel>
            </ControlTemplate>
        </ContentControl.Template>
        <TextBlock Text="inside ContentControl with a template, this one is green as well" />
    </ContentControl>

</StackPanel>

有没有办法 - 除了将StackPanel.Resources中的Style复制到ControlTemplate.Resources之外 - 使ControlTemplate中的TextBlock找到定义的样式?

...谢谢

1 个答案:

答案 0 :(得分:22)

WPF认为ControlTemplates是边界,并且不会在模板中应用隐式样式(没有x:Key的样式)。

但是这条规则有一个例外:从Control继承的任何内容都将应用隐式样式。

因此,您可以使用Label而不是TextBlock,它会应用在XAML层次结构中进一步定义的隐式样式,但是TextBlock继承自FrameworkElement而不是Control,它不会自动应用隐式样式,您必须手动添加它。

我最常见的解决方法是在ControlTemplate.Resources中添加一个隐式样式BasedOn现有的隐式TextBlock样式

    <ControlTemplate.Resources>
        <Style TargetType="{x:Type TextBlock}" 
               BasedOn="{StaticResource {x:Type TextBlock}}" />
    <ControlTemplate.Resources>

解决这个问题的其他常见方法是:

  • 将隐式样式放在<Application.Resources>中。无论模板边界如何,此处放置的样式都将适用于整个应用程序。但要小心,因为它会将样式应用于其他控件中的TextBlocks,如Buttons或ComboBoxes

    <Application.Resources>
        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="Foreground" Value="Green" />
        </Style>
    </Application.Resources>
    
  • 使用Label代替TextBlock,因为它是从Control继承的,因此将应用在ControlTemplate

    之外定义的隐式样式
  • 为基本样式指定x:Key,并将其用作TextBlock内隐式ControlTemplate样式的基本样式。它与顶级解决方案几乎相同,但它用于具有x:Key属性的基本样式

    <Style x:Key="BaseTextBlockStyle" TargetType="{x:Type TextBlock}">
        <Setter Property="Foreground" Value="Green" />
    </Style>
    
    ...
    
    <ControlTemplate.Resources>
        <Style TargetType="{x:Type TextBlock}" 
            BasedOn="{StaticResource BaseTextBlockStyle}" />
    <ControlTemplate.Resources>