WPF ControlTemplate разбивает стиль

Материал, который работает

Мне нужно стилизовать элементы управления определенного типа, которые являются дочерними элементами StackPanel. Я использую:

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

И это отлично работает! Каждый TextBlock ищет ресурсы этого родителя (StackPanel), чтобы узнать, как его следует стилизовать. Не имеет значения, как далеко вы вложите TextBlock в StackPanel... если он не найдет стиль в своем прямом родителе, он будет смотреть на родительский родитель и так далее, пока не найдет что-то (в этом случае, стиль, который был определен в).

Материал, который не работает

У меня возникла проблема, когда я вложил TextBlock внутри ContentControl, у которого был шаблон (см. код ниже). ControlTemplate, похоже, нарушает способ, которым TextBlock получает свой стиль от своих родителей, дедушек и бабушек,...

Использование ControlTemplate эффективно, похоже, выбивает холодное средство TextBlock для поиска его законного стиля (тот, что находится в StackPanel.Resources). Когда он сталкивается с ControlTemplate, он перестает искать свой стиль в ресурсах до дерева и вместо этого по умолчанию использует стиль в 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 для ControlTemplate.Resources - чтобы заставить TextBlock внутри этого элемента управления ControlTemplate найти определенный стиль?

Спасибо...

Ответ 1

WPF считает, что ControlTemplates является границей и не будет применять неявные стили (стили без x:Key) внутри шаблонов.

Но есть одно исключение из этого правила: все, что наследует от Control, будет применять неявные стили.

Таким образом, вы можете использовать Label вместо TextBlock, и он будет применять неявный стиль, определенный далее в иерархии XAML, однако, поскольку TextBlock наследует от FrameworkElement вместо Control, он выиграл 't применять неявный стиль автоматически, и вы должны добавить его вручную.

Самый распространенный способ обойти это - добавить неявный стиль в ControlTemplate.Resources, который BasedOn существующий неявный стиль TextBlock

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

Другие распространенные способы обойти это:

  • Поместите неявный стиль в <Application.Resources>. Стили, размещенные здесь, будут применяться ко всему вашему приложению независимо от границ шаблона. Будьте осторожны с этим, так как он будет применять стиль к TextBlocks внутри других элементов управления, например, кнопки или 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>