覆盖已覆盖的WPF主题

11

我正在WinXP上编写一个WPF应用程序,我已经使用Vista主题覆盖了默认主题,方法如下:

protected override void OnStartup(StartupEventArgs e)
{
  base.OnStartup(e);

  var themerd = new ResourceDictionary();
  themerd.Source = new Uri(@"PresentationFramework.Aero;V3.0.0.0;31bf3856ad364e35;component\themes/aero.normalcolor.xaml", UriKind.Relative);

  Resources.MergedDictionaries.Add(themerd);
}

它基本上可以正常工作。当我使用诸如按钮之类的控件时:

<Button />

样式看起来没问题,但如果我使用一个不同样式的按钮,像这样:

<Button>
  <Button.Style>
    <Style TargetType="Button">
      <Setter Property="Width" Value="80" />
    </Style>
  </Button.Style>
</Button>

该样式将使用标准的WinXP样式覆盖指定主题的样式,而不是在其基础上构建。这对我来说非常限制。有没有办法避免这个问题?

1个回答

11

为什么会发生这种情况

对于一个样式来说,其默认的BasedOn属性只会从当前主题的资源字典中生成。你所展示的覆盖主题的方法实际上并没有改变正在使用的主题字典,而只是将主题资源字典添加到应用程序的资源字典中。因为当前主题没有改变,所以默认的BasedOn属性也没有改变。

如何解决

选项1:通过截取Win32级别的uxtheme.dll!GetCurrentThemeName调用来本地覆盖主题。虽然这很复杂,但它适用于所有样式,并且不需要更改XAML。

选项2:使用自定义MarkupExtension设置BasedOn属性。它看起来像这样:

<Style TargetType="Button" BasedOn="{DefaultAeroStyle Button}"> ...

您的自定义MarkupExtension会在第一次使用时加载Aero主题字典并将其存储在静态字段中。其构造函数将接受一个类型,并且其ProvideValue将在字典中查找类型以查找样式。

选项3:将BasedOn设置为一个中间的已命名样式。它看起来像这样:

<Application ...>
  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="... theme path ..." />
      </ResourceDictionary.MergedDictionaries>

      <Style x:Key="ThemeButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}" />
      <Style x:Key="ThemeListBoxStyle" TargetType="ListBox" BasedOn="{StaticResource {x:Type ListBox}}" />
      ...
    </ResourceDictionary>
  </Application.Resources>
</Application>

现在你可以在你的低层字典中这样说:

<Style TargetType="Button" BasedOn="{StaticResource ThemeButtonStyle}" />

选项4:使用静态属性和x:Static标记扩展设置BasedOn


1
谢谢,你的回答真的很有帮助。这个领域似乎在WPF中缺乏。你通常使用哪个选项来解决这个问题? - vanja.
选项2非常酷,某种程度上甚至可以解决不能基于DynamicResources的问题。 - vanja.
我通常通过向客户解释,坚持使用当前的操作系统主题会使窗口对最终用户看起来更“正常”,从而解决问题。但是,在需要重新设计 UI 的某个部分但不需要重新设计 UI 的内部部分(例如弹出窗口)的情况下,我已经使用了选项2和选项3。 - Ray Burns
我明白不覆盖Windows主题是更好的选择,但如果我使用完全自定义的主题呢?那将遇到相同的问题。 - vanja.
如果您正在使用完全自定义的主题,我可能会选择选项2,因为在自定义主题中查找东西会很容易。 - Ray Burns
只需在本地样式定义中添加BasedOn = "{StaticResource {x:Type Button}}"即可让它对我起作用-感谢您解决这个问题,它让我发疯了! - devios1

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