Question

I have read lots of thing about WPF Theme, Skin, Style etc... But there is still something I cannot achieve.

I have custom controls, which are styled depending on the OS theme, by having a different style in each of the theme file (Aero.NormalColor.xaml, Luna.NormalColor.xaml or Aero2.NormalColor.xaml), this work like a charm.

I don't load/force any theme in my App.xaml, each controls (like buttons) keep there style depending on the OS theme. So I see XP buttons on XP, Win7 buttons on windows 7 and Win8 buttons on Windows 8.

I also have ResourceDictionaries which are loaded in the App.xaml that contains "named" (explicit x:Key) styles for different normal wpf controls. They look like this:

<Style x:Key="BlackComboBox" TargetType="{x:Type ComboBox}"></Style>

and I use them like this

<ComboBox Style="{StaticResource BlackComboBox}"></ComboBox>

So for now, my BlackComboBox is the same on every Windows (XP/7/8).

What I try to achieve is to have a different Style for these normal Controls depending on the OS theme, without having to subclass the Control (I think it will be overkill to have a subclass for each control that will need an OS specific them), so BlackComboBox could be different on each OS.

I have already tried to put a style with the same key in a theme file, but this doesn't seem to work.

I have thought about loading at runtime a different ResourceDictionary containing the style for the desired OS version:

  • But it looks like an ugly solution.
  • I don't like having to check for System.Environment.OSVersion.
  • And it will not be theme dependant, but OS dependent.

For me the best way seems to be able to have "named" style in a Theme file that kind of overrides the one in the ResourceDictionaries.

Thanks for the help!

Était-ce utile?

La solution

I believe the only way to do this would be to create resource dictionaries for each theme the same you would if you created a custom control and wanted to have a different look for each theme. Then you would create a Style in each for the ComboBox and provide a ResourceKey derived class (e.g. ComponentResourceKey) as the x:Key for the Style using the same value for the x:Key in each theme's resource dictionary. Then when you reference the Style you would use a DynamicResource to that ResourceKey.

So a simplified example would be to create a new WpfApplication (e.g. I named its WpfResourceKeys). In this case I'm going to put the theme resource dictionaries in the main assembly so I go into the AssemblyInfo.cs and set the ThemeInfo's 1st parameter (i.e. the themeDictionaryLocation) to SourceAssembly.

Then create a folder named "themes" and in it create a resource dictionary for each theme you want to support.E.g. aero.normalcolor.xaml, aero2.normalcolor.xaml, luna.normalcolor.xaml, classic.xaml, etc..

In each ResourceDictionary define a Style for ComboBox or whatever control you want and give it an x:Key of the same ResourceKey. The easiest thing to use is ComponentResourceKey. In my case I'll use a TextBox since I'll just set the Background and that will be honored regardless of the template defined for each theme. E.g.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:local="clr-namespace:WpfResourceKeys"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style TargetType="TextBox" 
        x:Key="{ComponentResourceKey 
            ResourceId=Foo, 
            TypeInTargetAssembly={x:Type local:MainWindow}}">
        <Setter Property="Background" Value="Purple" />
    </Style>
</ResourceDictionary>

In my case I just put this into each theme xaml file but with a different value for the Background setter to test it out. So in my aero2.normalcolor.xaml the setter value was Purple and in the classic.xaml the setter value was Orange. When I run my test in Windows 8 with the default theme the TextBox is purple but if I switch to one of the high contrast themes the TextBox is Orange.

Then in the place you are going to reference it you would use a DynamicResource instead of a StaticResource since you won't be defining the Style within the resources of the window or app.xaml (because you want the framework to locate it considering the OS theme).

<Window x:Class="WpfResourceKeys.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfResourceKeys"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBox Style="{DynamicResource ResourceKey={ComponentResourceKey 
            ResourceId=Foo, 
            TypeInTargetAssembly={x:Type local:MainWindow}}}" Text="ABC" />
    </Grid>

You just need to make sure you use an equivalent resource key to how you define it in the theme dictionaries. In the case of ComponentResourceKey that means the ResourceId and TypeInTargetAssembly are equivalent.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top