如何扩展而不是覆盖 WPF 样式

Posted

技术标签:

【中文标题】如何扩展而不是覆盖 WPF 样式【英文标题】:How to extend instead of overriding WPF Styles 【发布时间】:2013-03-12 16:16:52 【问题描述】:

我想在我的应用程序中使用自定义主题,据我所知,我可以通过使用资源字典并在 App.xaml 中引用它来完成此操作。样式会像这样覆盖默认值:

<Style TargetType="x:Type Label">
    <Setter Property="Foreground" Value="Green" />
</Style>

现在我猜默认标签样式被相同的值覆盖,但我所有的标签字体都是绿色的。当我想再次在某个地方设置一个标签的样式时,问题就开始了。当我想像这样更改网格中的其他属性时

<Grid.Resources>
    <Style TargetType="x:Type Label">
        <Setter Property="FontSize" Value="28" />
    </Style>
</Grid.Resources>

我的网格内的所有标签都失去了前景色并再次具有默认值(我不是在上一步中覆盖了默认值吗?)。经过一些尝试,我发现要正确执行此操作,我必须将另一个属性添加到 Style 声明 BasedOn=StaticResource x:Type Label" 并且它可以工作。这对我来说有点奇怪,因为现在我将不得不在整个应用程序中重复相同的 BasedOn 代码,这不是样式的工作方式 - 这应该自动完成!例如在 html + CSS 样式被继承和合并,在 WPF 中它们被替换......

请注意,当我不使用任何样式控件时,它们的外观仍然来自某个(系统主题?)。我如何告诉他们在其他地方寻找默认值,这样如果没有任何额外的样式代码,他们就会认为默认情况下它们应该是绿色的?

有什么方法可以自动设置 BasedOn 属性?或者也许有更好的方法来全面做到这一点?

【问题讨论】:

我在这里找到了类似问题的答案***.com/questions/2377055/… 我刚刚重新阅读了您的问题,我想知道您是否使用过资源字典?听起来您希望能够定义一种样式,然后将该样式应用于您的 xaml 元素,对吗?好吧,如果你设置了一个资源字典,你可以让你的基本样式将所有标签设置为具有绿色前景,然后有另一种基于设置字体大小的样式,所有这些都包含在一个代码文件中,用作字典。然后在每个 xaml 元素中,将样式指定为 'Style="StaticResource LargeGreen"' 或任何您称之为的样式。我会尝试用一个例子来编辑我的答案。 【参考方案1】:

我遇到了同样的问题。我使用了 Zack 的答案并对其进行了如下改进,因此如果您不指定样式,仍会考虑覆盖的默认值。这基本上是你会做的,但只在 ResourceDictionary 中做一次。

<Window x:Class="TestWpf.RandomStuffWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Random Stuff Window">
  <Window.Resources>
    <ResourceDictionary>
      <!-- Default Label style definition -->
      <Style TargetType="x:Type Label">
        <Setter Property="Foreground" Value="Green" />
      </Style>
      <!-- Extending default style -->
      <Style TargetType="x:Type Label" 
             x:Key="LargeGreenForegroundLabel" 
             BasedOn="StaticResource x:Type Label">
        <Setter Property="FontSize" Value="28" />
      </Style>
    </ResourceDictionary>
  </Window.Resources>
  <StackPanel>
    <Button Click="Button_Click">Click</Button>
    <Label Content="GreenForegroundLabel" /> <!-- Uses default style -->
    <Label Style="StaticResource LargeGreenForegroundLabel" 
           Content="LargeGreenForegroundLabel" />
  </StackPanel>
</Window>

【讨论】:

【参考方案2】:

Wpf 有不同级别的样式,按全局 > 本地的顺序应用。直接在控件上设置的样式将覆盖全局样式集,就像在您的示例中一样。我试图找到一个控件查找其样式的所有不同位置的列表,但目前我找不到。据我所知,您必须使用 BasedOn 属性来继承样式,而不是用您在本地设置的样式完全覆盖该样式的属性。

这是一个资源字典的示例,其样式基于另一种样式,因此您不必一遍又一遍地重复 BasedOn 绑定,您只需在您想要的特定元素上设置样式有这种风格。

<Window x:Class="TestWpf.RandomStuffWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Random Stuff Window">
  <Window.Resources>
    <ResourceDictionary>
      <Style TargetType="x:Type Label" 
             x:Key="GreenForegroundLabel">
        <Setter Property="Foreground" Value="Green" />
      </Style>
      <Style TargetType="x:Type Label" 
             x:Key="LargeGreenForegroundLabel" 
             BasedOn="StaticResource GreenForegroundLabel">
        <Setter Property="FontSize" Value="28" />
      </Style>
    </ResourceDictionary>
  </Window.Resources>
  <StackPanel>
    <Button Click="Button_Click">Click</Button>
    <Label Style="StaticResource GreenForegroundLabel" 
           Content="GreenForegroundLabel" />
    <Label Style="StaticResource LargeGreenForegroundLabel" 
           Content="LargeGreenForegroundLabel" />
  </StackPanel>
</Window>

【讨论】:

以上是关于如何扩展而不是覆盖 WPF 样式的主要内容,如果未能解决你的问题,请参考以下文章

如何在不覆盖现有样式的情况下向 WPF 自定义控件添加触发器?

如何使用扩展运算符向对象添加属性,而不是覆盖它?

WPF:按可用宽度而不是按其内容扩展 TextBox?

如何访问网页 DOM 而不是扩展页面 DOM?

WPF自定义控件与样式-TextBox & RichTextBox & PasswordBox样式水印Label标签功能扩展

如何通过拖动扩展的窗口框架使 WPF 窗口可移动?