【问题标题】:WPF ControlTemplate breaks styleWPF ControlTemplate 打破风格
【发布时间】:2013-01-08 07:34:12
【问题描述】:

有效的东西

我需要对作为 StackPanel 子级的某种类型的控件进行样式设置。我正在使用:

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

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

不起作用的东西

我在 ContentControl 中嵌套了一个 TextBlock 时遇到了问题,该 ContentControl 有一个模板(参见下面的代码)。 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 找到定义的样式?

谢谢...

【问题讨论】:

    标签: wpf xaml controltemplate nested


    【解决方案1】:

    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>
    

    其他常见的解决方法是:

    • 将隐式样式放在&lt;Application.Resources&gt; 中。无论模板边界如何,此处放置的样式都将应用于您的整个应用程序。不过要小心这一点,因为它会将样式应用于其他控件内部的TextBlocks,例如按钮或组合框

      <Application.Resources>
          <Style TargetType="{x:Type TextBlock}">
              <Setter Property="Foreground" Value="Green" />
          </Style>
      </Application.Resources>
      
    • 使用Label 而不是TextBlock,因为它继承自Control,因此将应用在ControlTemplate 之外定义的隐式样式

    • 给基本样式一个x:Key 并将其用作ControlTemplate 内隐式TextBlock 样式的基本样式。它与顶级解决方案几乎相同,但它用于具有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>
      

    【讨论】:

    • 很好的解释!我也喜欢您使用 BasedOn="{StaticResource {x:Type TextBlock}}" 的方式。有趣的。现在我知道 WPF 认为 ControlTemplates 是一个边界,我可以尝试解决它:)
    • 谢谢,我终于明白了如何将样式应用于我的 ControlTemplate。第三个示例很有启发性,允许我仅将样式应用于我需要的 ControlTemplate(s)。
    猜你喜欢
    • 2013-05-05
    • 2019-11-30
    • 2012-11-09
    • 1970-01-01
    • 2014-02-15
    • 2013-05-20
    • 2010-10-27
    • 2016-04-14
    • 1970-01-01
    相关资源
    最近更新 更多