Переопределение стилей WPF-компонентов из внешних библиотек
Подходы к переопределению стилей WPF-компонентов из внешних библиотек без создания наследников классов. ResourceDictionary, BasedOn, ControlTemplate.
Как переопределить стили WPF-компонентов из подключенной библиотеки? Какие существуют подходы для кастомизации стилей компонентов из внешних UI-библиотек в приложении WPF, не прибегая к созданию множества наследников классов компонентов?
В WPF существует несколько эффективных подходов для переопределения стилей компонентов из внешних UI-библиотек без создания множества наследников классов. Основные методы включают использование ResourceDictionary с тем же TargetType без ключа, наследование стилей через BasedOn, переопределение ControlTemplate для полной кастомизации визуального дерева, а также глобальное переопределение стилей через приоритет загрузки ресурсов. Эти подходы позволяют гибко кастомизировать внешний вид компонентов из внешних библиотек, сохраняя преимущества MVVM-архитектуры.
Содержание
- Основные подходы к переопределению стилей WPF-компонентов
- Использование ResourceDictionary для кастомизации стилей
- Наследование стилей через BasedOn в WPF
- Переопределение ControlTemplate для полной кастомизации
- Глобальное переопределение стилей без создания наследников
- Практические примеры и рекомендации по реализации
Основные подходы к переопределению стилей WPF-компонентов
В разработке приложений на WPF часто возникает необходимость изменить внешний вид компонентов, предоставляемых внешними UI-библиотеками, без создания множества наследников классов. Существует несколько эффективных подходов к решению этой задачи, каждый из которых имеет свои преимущества и сферы применения.
Первый и наиболее распространенный метод - переопределение стилей через установку свойства TargetType без указания x:Key. Такой стиль автоматически применяется ко всем элементам указанного типа, заменяя стиль, поставляемый библиотекой. Это позволяет быстро изменить базовые свойства внешнего вида, такие как фон, цвет текста, отступы и другие атрибуты стилизации.
Второй подход - использование механизма наследования стилей через BasedOn. Этот метод позволяет создать новый стиль, который наследует все свойства из существующего стиля, и переопределить только нужные параметры. Это особенно полезно, когда нужно сохранить большую часть оригинального стиля, но изменить несколько конкретных свойств.
Третий подход - полное переопределение визуального дерева через ControlTemplate. Этот метод дает максимальную гибкость, позволяя полностью изменить внешний вид компонента, включая структуру его визуального представления, а не только свойства.
Важно отметить, что все эти методы соответствуют традиционному подходу WPF, основанному на шаблоне MVVM, и позволяют сохранить преимущества разделения логики и представления. Как отмечает VoidVolker из сообщества Хабр Q&A: “Задание стилей через установку свойства TargetType и создание наследника класса — самый простой и удобный способ. Если у вас несколько вариантов стилей одной и той же кнопки — сделайте базовый стиль и базовую кнопку, а вариации стилей через свойства задайте.”
Использование ResourceDictionary для кастомизации стилей
ResourceDictionary является одним из мощнейших инструментов в WPF для управления стилями и шаблонами. Он позволяет централизованно определять и переопределять стили компонентов из внешних библиотек, обеспечивая удобство поддержки и модификации.
Для переопределения стиля компонента из внешней библиотеки в ResourceDictionary достаточно объявить новый стиль с тем же TargetType, но без указания x:Key. Такой стиль будет автоматически применяться ко всем элементам указанного типа в приложении, переопределяя стиль из библиотеки. Пример такого подхода:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button">
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="10,5"/>
</Style>
</ResourceDictionary>
Как объясняется в документации Microsoft Learn: “Для переопределения стилей компонентов из подключенной библиотеки в WPF можно использовать несколько подходов, не создавая наследников классов. Один из них – определить в ресурсах приложения (или в корневом XAML‑файле) стиль с тем же TargetType, но без x:Key. Такой стиль будет применяться ко всем элементам указанного типа и переопределит стиль, поставляемый библиотекой.”
Более гибкий подход использует механизм ResourceDictionary.MergedDictionaries, который позволяет подключать собственные словари ресурсов после словаря библиотеки, обеспечивая приоритет собственных стилей. Это особенно полезно в крупных проектах, где стили могут быть определены в разных местах. Пример подключения:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Button.xaml" />
<ResourceDictionary Source="TabPanel.xaml" />
<ResourceDictionary Source="/LibraryDictonaries;component/TabPanel.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
Как отмечает пользователь Stack Overflow user206435: “Добавляем ResourceDictonary, называем его Generic.xaml туда пишем все подключаемые стили, подключаем сборку к проекту, ссылаемся на этот Generic.xaml из App.xaml.”
Этот подход позволяет создавать иерархию стилей, где более поздние подключения переопределяют более ранние, что дает полный контроль над внешним видом компонентов из внешних библиотек.
Наследование стилей через BasedOn в WPF
Механизм наследования стилей через BasedOn предоставляет мощный инструмент для тонкой кастомизации существующих стилей без необходимости полностью их переопределять. Этот подход особенно полезен, когда нужно сохранить большую часть оригинального стиля, но изменить несколько конкретных свойств.
Основная идея заключается в том, чтобы создать новый стиль, который наследует все свойства из существующего стиля, и затем переопределить только нужные параметры. Это достигается через использование свойства BasedOn в определении стиля. Пример реализации:
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="FontFamily" Value="Verdana"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Cursor" Value="Hand"/>
</Style>
Как подробно объясняет Евгений Попов на сайте Metanit: “В WPF стили можно переопределить, объявив новый стиль с тем же TargetType и без ключа, либо используя BasedOn для наследования. Если в библиотеке определён стиль для, например, Button, вы можете добавить в свой ресурсный словарь стиль”:
<Style TargetType="Button">
<Setter Property="Background" Value="LightBlue"/>
</Style>
“Такой стиль будет автоматически применяться к всем кнопкам, заменяя стиль из библиотеки. Если нужно изменить только часть свойств, можно использовать BasedOn”:
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="FontFamily" Value="Verdana"/>
</Style>
Таким образом вы наследуете все свойства из исходного стиля и переопределяете только нужные.
Преимущество этого подхода заключается в том, что он позволяет создавать иерархию стилей, где базовые стили определяют общие параметры, а производные стили добавляют или изменяют специфические свойства. Это особенно полезно в корпоративных приложениях, где требуется единообразный внешний вид с возможностью локальных изменений.
Кроме того, механизм BasedOn позволяет гибко управлять приоритетом стилей. Если в приложении определены несколько стилей для одного и того же компонента, то более специфичный стиль (с указанным x:Key) всегда будет иметь приоритет над универсальным стилем (без x:Key), что обеспечивает предсказуемое поведение системы стилизации.
Переопределение ControlTemplate для полной кастомизации
Иногда простого изменения свойств стиля недостаточно - требуется полностью изменить структуру визуального дерева компонента. В таких случаях применяется переопределение ControlTemplate, что позволяет создать совершенно новый визуальный представление компонента, сохраняя при этом его функциональность.
ControlTemplate определяет визуальное дерево компонента, включая все его элементы, их расположение и взаимодействие. Переопределение этого шаблона дает возможность полностью изменить внешний вид компонента из внешней библиотеки без создания наследника класса. Пример переопределения шаблона для кнопки:
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="5">
<Grid>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{TemplateBinding Content}"/>
<Path Data="M10,0 L20,10 L10,20 L0,10 Z"
Fill="Red"
Visibility="Collapsed"
x:Name="Indicator"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightGray"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="DarkGray"/>
<Setter Property="Visibility" TargetName="Indicator" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Как подчеркивает Microsoft Learn: “Если необходимо изменить не только свойства, но и структуру визуального дерева, можно переопределить ControlTemplate: создайте новый шаблон и назначьте его свойству Template нужного элемента. Это позволяет полностью изменить внешний вид без наследования.”
Важным аспектом при переопределении ControlTemplate является сохранение привязок к свойствам компонента через TemplateBinding. Это обеспечивает, что элементы шаблона будут правильно реагировать на изменения свойств компонента. Например, Background элемента Border привязан к Background кнопки через TemplateBinding.
Еще одним мощным инструментом являются триггеры (ControlTemplate.Triggers), которые позволяют изменять визуальное представление компонента в ответ на изменения его состояния. В приведенном примере триггеры изменяют фон кнопки при наведении мыши и при нажатии.
Этот подход особенно полезен при создании собственных визуальных компонентов или при необходимости кардинально изменить внешний вид стандартного компонента из внешней библиотеки. Однако он требует более глубокого понимания структуры визуального дерева и механизмов работы WPF.
Глобальное переопределение стилей без создания наследников
Для обеспечения единообразного внешнего вида всего приложения или крупных его модулей часто требуется глобальное переопределение стилей компонентов из внешних библиотек. Такой подход позволяет избежать необходимости применять стили к каждому элементу индивидуально и обеспечивает согласованность дизайна по всему приложению.
Основным механизмом для глобального переопределения стилей является определение стилей в ресурсах приложения (в файле App.xaml) или в корневых элементах интерфейса. Стиль, определенный без указания x:Key с определенным TargetType, автоматически применяется ко всем элементам этого типа в области видимости ресурса.
Пример глобального переопределения стилей в App.xaml:
<Application x:Class="MyApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<!-- Глобальные стили -->
<Style TargetType="Button">
<Setter Property="Background" Value="#2196F3"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="12,6"/>
<Setter Property="BorderRadius" Value="4"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="#CCCCCC"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="8,4"/>
</Style>
</Application.Resources>
</Application>
Как объясняется в руководстве WPF Tutorial: “Для глобального переопределения добавьте новый стиль в App.xaml с тем же TargetType, но без ключа, и он заменит стиль из подключённой библиотеки.”
Важным аспектом глобального переопределения является приоритет загрузки ресурсов. В WPF ресурсы загружаются в определенном порядке: локальные ресурсы имеют приоритет над контейнерными, контейнерные - над оконными, а оконные - над глобальными (определенными в App.xaml). Это позволяет переопределять глобальные стили на более конкретных уровнях при необходимости.
Еще одним мощным инструментом является использование неявных стилей через TargetType с дополнительными селекторами. Например, можно определить разные стили для кнопок в разных контекстах:
<Style TargetType="Button" x:Key="PrimaryButton">
<Setter Property="Background" Value="#4CAF50"/>
<Setter Property="Foreground" Value="White"/>
</Style>
<Style TargetType="Button" x:Key="SecondaryButton">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="#2196F3"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="#2196F3"/>
</Style>
<Style TargetType="Button" BasedOn="{StaticResource PrimaryButton}">
<Setter Property="FontSize" Value="16"/>
<Setter Property="Padding" Value="20,10"/>
</Style>
Такой подход позволяет создать гибкую систему стилизации, где базовые стили определяются глобально, а конкретные вариации определяются на уровне отдельных окон или даже элементов.
Практические примеры и рекомендации по реализации
Рассмотрим несколько практических примеров переопределения стилей компонентов из внешних библиотек в WPF, а также дадим рекомендации по их эффективному использованию в реальных проектах.
Пример 1: Переопределение стиля кнопки из Material Design in XAML Toolkit
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Переопределение стиля Material Design кнопки -->
<Style TargetType="Button">
<Setter Property="Background" Value="#3F51B5"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="24,12"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontWeight" Value="Medium"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="2">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="{TemplateBinding Padding}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#303F9F"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#283593"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="#BDBDBD"/>
<Setter Property="Foreground" Value="#757575"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Пример 2: Переопределение стиля текстового поля из MahApps.Metro
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Переопределение стиля MahApps.Metro текстового поля -->
<Style TargetType="TextBox" BasedOn="{StaticResource MahApps.Styles.TextBox}">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="#CCCCCC"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="8,6"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="Foreground" Value="#333333"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="#2196F3"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="#999999"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
Рекомендации по реализации:
-
Используйте иерархию стилей: Создавайте базовые стили с общими параметрами и производные стили для специфических случаев. Это упрощает поддержку и модификацию.
-
Определяйте стили в отдельных файлах: Для крупных проектов выносите стили в отдельные файлы ResourceDictionary и подключайте их через MergedDictionaries. Это улучшает организацию кода.
-
Используйте неявные стили: Для глобального переопределения стилей используйте TargetType без x:Key. Для специфических случаев используйте явные стили с x:Key.
-
Сохраняйте функциональность: При переопределении ControlTemplate убедитесь, что все необходимые элементы и триггеры сохранены, чтобы не нарушить функциональность компонента.
-
Используйте шаблоны проектирования MVVM: Старайтесь не нарушать разделение логики и представления, даже при глубокой кастомизации визуального вида.
Как отмечает VoidVolker: “В реальной практике: в репозитории GitHub определены две кнопки — IconButton и TabButton как расширение стандартного класса Button. У них есть собственные стили, а также свойства типа иконки и заголовка вкладки. Благодаря общему предку можно добавить стиль для класса Button и изменить базовый стиль сразу для всех наследников, не затрагивая дополнительные стили самих наследников.”
-
Документируйте стили: Добавляйте комментарии к определениям стилей, особенно при сложных переопределениях, чтобы облегчить понимание кода для других разработчиков.
-
Тестируйте изменения: После изменения стилей тщательно тестируйте функциональность компонентов, чтобы убедиться, что все взаимодействия работают корректно.
-
Используйте темы: Для приложений с поддержкой нескольких тем создайте отдельные ResourceDictionary для каждой темы и переключайте их динамически.
Эти рекомендации помогут создать гибкую, поддерживаемую и визуально согласованную систему стилизации в WPF-приложениях, использующих компоненты из внешних библиотек.
Источники
-
Хабр Q&A — Основы стилизации WPF-компонентов — Практические рекомендации по переопределению стилей без создания наследников: https://qna.habr.com/q/1409158
-
Microsoft Learn — Стили и шаблоны в WPF — Официальная документация по механизмам стилизации в WPF: https://learn.microsoft.com/ru-ru/dotnet/desktop/wpf/controls/styles-templates-overview?view=netdesktop-9.0
-
Metanit — Наследование стилей через BasedOn — Подробное объяснение механизма наследования стилей в WPF: https://metanit.com/sharp/wpf/10.php
-
Stack Overflow на русском — Переопределение стилей через ResourceDictionary — Практические примеры использования ResourceDictionary для переопределения стилей: https://ru.stackoverflow.com/questions/694973/Объий-стиль-для-всех-сборок-xaml-wpf
-
WPF Tutorial — Использование ResourceDictionary — Руководство по эффективному использованию ResourceDictionary для управления стилями в WPF: https://wpf-tutorial.com/ru/92/стили/использование-стилей-wpf/
Заключение
Переопределение стилей WPF-компонентов из внешних библиотек без создания множества наследников классов — это мощный инструмент для создания единого и профессионального внешнего вида приложений. Основные подходы, рассмотренные в этой статье, включают использование ResourceDictionary с тем же TargetType без ключа, наследование стилей через BasedOn, полное переопределение ControlTemplate для изменения визуального дерева, а также глобальное переопределение стилей через приоритет загрузки ресурсов.
Каждый из этих подходов имеет свои преимущества и сферы применения. Для простых изменений свойств достаточно использовать неявные стили с TargetType. Для более тонкой кастомизации, когда нужно сохранить большую часть оригинального стиля, идеально подходит механизм BasedOn. Для кардинального изменения внешнего вида компонента необходимо переопределять ControlTemplate. А для обеспечения единообразия всего приложения используются глобальные стили, определенные в ресурсах приложения.
Важно помнить, что при переопределении стилей следует сохранять функциональность компонентов и придерживаться принципов шаблона MVVM. Правильная организация стилей в виде иерархии, использование отдельных файлов ResourceDictionary для крупных проектов, а также документирование сложных переопределений помогут создать поддерживаемую и гибкую систему стилизации WPF-приложения.
Выбор конкретного подхода зависит от требований проекта, сложности необходимых изменений и личных предпочтений разработчика. Однако, независимо от выбранного метода, знание этих техник позволяет эффективно кастомизовать внешний вид компонентов из внешних UI-библиотек, не прибегая к созданию множества наследников классов.
Задание стилей через установку свойства TargetType и создание наследника класса — самый простой и удобный способ. Если у вас несколько вариантов стилей одной и той же кнопки — сделайте базовый стиль и базовую кнопку, а вариации стилей через свойства задайте. Тогда и не нужно будет создавать несколько классов одной и той же кнопки. Дополнительные классы нужны только для использования дополнительных свойств. Можно, конечно, в рантайме в коде вертеть как угодно стилями, но в WPF традиционен подход в виде шаблона MVVP. В реальной практике: в репозитории GitHub определены две кнопки — IconButton и TabButton как расширение стандартного класса Button. У них есть собственные стили, а также свойства типа иконки и заголовка вкладки. Благодаря общему предку можно добавить стиль для класса Button и изменить базовый стиль сразу для всех наследников, не затрагивая дополнительные стили самих наследников.
Для переопределения стилей компонентов из подключенной библиотеки в WPF можно использовать несколько подходов, не создавая наследников классов. Один из них – определить в ресурсах приложения (или в корневом XAML‑файле) стиль с тем же TargetType, но без x:Key. Такой стиль будет применяться ко всем элементам указанного типа и переопределит стиль, поставляемый библиотекой. Если необходимо изменить не только свойства, но и структуру визуального дерева, можно переопределить ControlTemplate: создайте новый шаблон и назначьте его свойству Template нужного элемента. Это позволяет полностью изменить внешний вид без наследования. Еще один способ – использовать ResourceDictionary.MergedDictionaries и подключить собственный словарь, содержащий стили и шаблоны, которые будут загружаться после словаря библиотеки и тем самым переопределять его значения. Эти методы позволяют кастомизировать внешний вид компонентов из внешних UI‑библиотек, не прибегая к созданию множества наследников.
В WPF стили можно переопределить, объявив новый стиль с тем же TargetType и без ключа, либо используя BasedOn для наследования. Если в библиотеке определён стиль для, например, Button, вы можете добавить в свой ресурсный словарь стиль: xml <Style TargetType="Button"> <Setter Property="Background" Value="LightBlue"/> </Style> Такой стиль будет автоматически применяться к всем кнопкам, заменяя стиль из библиотеки. Если нужно изменить только часть свойств, можно использовать BasedOn: xml <Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}"> <Setter Property="FontFamily" Value="Verdana"/> </Style> Таким образом вы наследуете все свойства из исходного стиля и переопределяете только нужные. Кроме того, можно подключать собственные словари ресурсов через <ResourceDictionary.MergedDictionaries> и объявлять стили там, чтобы они имели приоритет над библиотечными. Это позволяет кастомизировать внешний вид без создания наследников компонентов.
Добавляем ResourceDictonary, называем его Generic.xaml туда пишем все подключаемые стили, подключаем сборку к проекту, ссылаемся на этот Generic.xaml из App.xaml. Пример: xml <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Button.xaml" /> <ResourceDictionary Source="TabPanel.xaml" /> <ResourceDictionary Source="/LibraryDictonaries;component/TabPanel.xaml" /> ... </ResourceDictionary.MergedDictionaries> </ResourceDictionary> Этот подход позволяет переопределять стили компонентов из внешней библиотеки, создавая собственный словарь ресурсов с теми же именами и типами, что и в библиотеке, и подключая его после основного словаря.
В WPF стили можно переопределять, не создавая наследников, используя различные уровни ресурсов: локальные, контейнерные, оконные и глобальные. Для локального переопределения можно задать стиль прямо внутри элемента, например: xml <TextBlock Style="{StaticResource MyTextStyle}"> ... </TextBlock> Если нужно изменить стиль для всех элементов определённого типа, объявите его без x:Key в ресурсах окна или приложения; такой стиль будет применяться к каждому элементу, но можно переопределить его в более глубоком контейнере. Для глобального переопределения добавьте новый стиль в App.xaml с тем же TargetType, но без ключа, и он заменит стиль из подключённой библиотеки. Для более избирательного применения используйте явный стиль с x:Key и ссылку через Style="{StaticResource MyExplicitStyle}". Это позволяет менять внешний вид компонентов из внешних UI‑библиотек без создания множества наследников.