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

41

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

我希望在我的应用程序中使用自定义主题。根据我的了解,我可以通过使用资源字典并在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属性?或者有更好的方法来解决这个问题吗?


我在这里找到了一个类似的问题和答案:https://dev59.com/inE95IYBdhLWcg3wSb7P - labm0nkey
我刚刚重新阅读了你的问题,我想知道你是否使用过资源字典?听起来你想定义一个样式,并将该样式应用于你的XAML元素,对吗?如果你设置一个资源字典,你可以有一个基础样式,将所有标签设置为绿色前景色,然后有另一个基于此的样式,设置字体大小,这些都包含在一个代码文件中,作为字典使用。然后在每个XAML元素中,你将样式分配为“Style =”{StaticResource LargeGreen}“或任何你称之为的名称。我会尝试编辑我的答案并提供一个示例。 - Zack
2个回答

41

我曾经遇到同样的问题。我采用了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>

15

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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接