如果样式已经设置,如何覆盖 WPF 子控件样式?

Posted

技术标签:

【中文标题】如果样式已经设置,如何覆盖 WPF 子控件样式?【英文标题】:Howto override WPF childcontrols style if a style already is set? 【发布时间】:2021-12-23 05:31:27 【问题描述】:

如果我有一个简单的子控件,它已经为子控件中的元素定义了样式。 我可以从父控件更改该元素的样式吗?

如果我没有在子控件中设置样式,我可以从父控件中覆盖它,但是在设置样式时我似乎无法让它工作。

我无法更改子控件中的代码。

示例子控件:

    <UserControl
    x:Class="TestingThemes.ChildControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:TestingThemes"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    d:DesignHeight="450"
    d:DesignWidth="800"
    mc:Ignorable="d">
    <UserControl.Resources>
        <Style x:Key="aw" TargetType="Button">
            <Setter Property="Foreground" Value="Blue" />
        </Style>
    </UserControl.Resources>
    <Grid>
        <Button
            Width="100"
            Height="50"
            Content="Kalle Anka"
            <!-- If I remove the style I can override it from parent control -->
            Style="DynamicResource aw" />
    </Grid>
</UserControl>

父控件:

 <Grid>
    ...            
    <local:ChildControl>
        <local:ChildControl.Resources>
            <!--<Style BasedOn="StaticResource local:ResourceKey=aw" TargetType="Button">-->
            <Style TargetType="Button">
                <Setter Property="Foreground" Value="Red" />
            </Style>
        </local:ChildControl.Resources>
    </local:ChildControl>
   
</Grid>

【问题讨论】:

【参考方案1】:

这是可能的,但您必须更改初始Style 的位置。 XAML 引擎将遍历逻辑树以查找 StaticResource/DynamicResource 资源以查找从本地开始的元素(例如,Button),使用本地 ResourceDictionary

这意味着,您希望在查找路径的最后找到在UserControl 中定义的默认Style - 在任何旨在覆盖默认Style 的潜在自定义样式之后。为此,您必须将 Style 移动到 App.xamlGeneric.xaml。 此外,引用必须是DynamicResource,以便将查找推迟到组成逻辑树的那一刻。覆盖 Style 可以定义在通往树根的路径上的任何位置。

由于 Generic.xaml 提供了不同的名称范围,因此您必须定义一个唯一的静态(就 XAML 名称范围而言是全局的)ComponentResourceKey,您必须将其用作样式的x:Key 值(如果您打算将默认样式移动到 Generic.xaml 文件)。

由于上述 XAML 资源查找行为,当您计划使控件可自定义时(如您的情况),创建自定义 Control 始终优于 UserControl。默认情况下,自定义 Control 的默认 Style 位于 Generic.xaml 中,因此您永远不会遇到当前问题。

ChildControl.xaml

<UserControl>
  <Grid>
    <Button Style="DynamicResource aw" />
  </Grid>
</UserControl>

App.xamlGeneric.xaml 之前的最后一个查找位置。如果尚未找到具有相同密钥的Style,则将应用在App.xaml 中找到的Style

<Style x:Key="aw" TargetType="Button">
  <Setter Property="Foreground" Value="Blue" />
</Style>

ParentControl.xaml

<ChildControl>
  <ChildControl.Resources>
    <Style x:Key="aw" TargetType="Button">
      <Setter Property="Foreground" Value="Red" />
    </Style>
  </ChildControl.Resources>
</ChildControl>

【讨论】:

感谢您的精彩解释。但这意味着如果我不能更改 childcontrol 的代码,我将无能为力。我想重新设置样式的子控件不是以这种方式构建的,其中样式定义为 app/genric.xaml :( 那么控件设计的不好。您可以做的是等待加载应用程序。如果您知道控件的元素树,则可以遍历其可视树以查找 Button(或感兴趣的元素)并通过分配新的本地样式来覆盖样式。如果你不知道元素树,你可以使用像 Snoop 这样的工具来检查它。这不是优雅的,而是您最后的选择——除了从头开始创建改进的控件。当您创建封装功能的控件时,UserControl 绝不是一个好的选择。

以上是关于如果样式已经设置,如何覆盖 WPF 子控件样式?的主要内容,如果未能解决你的问题,请参考以下文章

无法覆盖由 TargetType 在单个特定控件上设置的全局 WPF 样式

WPF宽度100%,我想wpf的子控件的宽度跟父控件的宽度一样,当父控件宽度改变的时候子控件随着改变,请问有啥!

如何按名称定位具有嵌套样式的 WPF 子控件?

WPF 样式不适用于已设置样式的用户控件

在 WPF 中优雅地覆盖 ComboBox 的 ToggleButton 样式

设置 WPF 用户控件的样式