Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Override Overridden WPF Theme

Tags:

wpf

I am writing a WPF application on WinXP and I have overridden the default theme with the vista theme like this:

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);
}

And it works fine mostly. When I use control such as a button:

<Button />

The style looks fine, but if I use a Button with a different style like this:

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

The style will override the specified theme style with the standard WinXP style instead of building on top of it. This is extremely limiting for me. Is there a way to avoid this issue?

like image 500
vanja. Avatar asked Mar 04 '10 05:03

vanja.


1 Answers

Why this is happening

The default BasedOn= for a style is generated using only the current theme's resource dictionary. The technique you show for overriding a theme doesn't actually change the theme dictionary in use: It simply adds the resources from the theme's resource dictionary to the application's resource dictionary. Since the current theme is unchanged, the default BasedOn is also unchanged.

How to solve it

Option 1: Locally override the theme by intercepting calls to uxtheme.dll!GetCurrentThemeName at the Win32 level. This is quite complex but works for all your styles with no changes to your XAML.

Option 2: Set BasedOn using a custom MarkupExtension. It would look like this:

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

Your custom MarkupExtension would load the Aero theme dictionary on first use and store it in a static field. Its constructor would take a Type, and its ProvideValue would look up the type in the dictionary to find the style.

Option 3: Set BasedOn to an intermediate named style. It would look like this:

<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>

Now in your lower-level dictionary you can say:

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

Option 4: Set BasedOn using a static property and the x:Static markup extension

like image 76
Ray Burns Avatar answered Nov 18 '22 18:11

Ray Burns