Reputation: 1730
I have two themes: Aero.NormalColor.xaml and Classic.xaml. I have button on my window.
<Grid>
<Button Grid.Row="0"
Name="_first"
Content="Fisrt">
</Button>
</Grid>
I defined styles for a Button
in the bouth themes.
In Aero.NormalColor.xaml:
<Style TargetType="{x:Type ButtonBase}">
<Setter Property="Background" Value="Red"/>
</Style>
In Classic.xaml:
<Style TargetType="{x:Type ButtonBase}">
<Setter Property="Background" Value="Violet"/>
</Style>
And set assembly attribute like this.
[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly,
ResourceDictionaryLocation.SourceAssembly)]
I expect when Aero theme is picked - button will be Red
, and when Classic theme is picked - button will be Violet
. But nothing happens. Button has default Gray
color whatever theme was picked. What am I doing wrong?
Upvotes: 1
Views: 534
Reputation: 1730
Solution. It turned out a litle more complicated than I supposed, but now it works.
First create a derived button:
public partial class ImageButton : Button
{
// Very important!
static ImageButton()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(ImageButton),
new FrameworkPropertyMetadata(typeof(ImageButton)));
}
public ImageButton()
{
InitializeComponent();
}
}
<Button x:Class="TestProjectTheme.ImageButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Button>
Second, set the assembly property correctly:
[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly,
ResourceDictionaryLocation.SourceAssembly)]
Third, define resource dictionaries for windows themes:
Aero.NormalColor.xaml
<Style TargetType="{x:Type Local:ImageButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Local:ImageButton}">
<Border Background="Aquamarine">
<ContentPresenter
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="True"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Classic.xaml
<Style TargetType="{x:Type Local:ImageButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Local:ImageButton}">
<Border Background="Red">
<ContentPresenter
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="True"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Fourth, define an ImageButton in your MainWindow:
<Local:ImageButton Content="look here!"
Width="100"
Height="100">
</Local:ImageButton>
Fifth, run application and switch themes from Classic to Aero and vise-versa in Windows->Personalizaton Sixth - Enjoy :)
Upvotes: 1
Reputation: 22702
Unfortunately, the mechanism of switching themes in WPF is more complicated than it seems. Here
you can see info and solution about this, some quote:
The more I thought about this, the more I realized that all of these problems were being caused by the fact that I was applying a system theme (aero.normalcolor) as a style on top of the actual system theme, rather than actually changing it. So I set off on a journey in Reflector to find out how WPF picks the current theme. This sounds hard, and it’s actually a lot harder than it sounds. After a dozen or so hours (spread out over a few weeks) and a some guidance by a blog that got really close (unfortunately, my link is now dead), I found out that however WPF calls a native method in uxtheme.dll to get the actual system theme, then stores the result in
MS.Win32.UxThemeWrapper
, an internal static class (of course). Furthermore, the properties on the class are read only (and also marked internal), so the best way to change it was by directly manipulating the private fields.
Usually, the switching themes done by "hand".
Example:
App.xaml
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Resources/Themes/DefaultSkin.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
App.xaml.cs
public partial class App : Application
{
public void ChangeTheme(Uri uri)
{
var resourceDict = Application.LoadComponent(uri) as ResourceDictionary;
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(resourceDict);
}
}
MainWindow.xaml
<StackPanel>
<Button Width="100" Height="100" Content="Test" />
<Button Width="100" Height="100" Content="Click Me" Click="Button_Click" />
</StackPanel>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var app = App.Current as App;
app.ChangeTheme(new Uri(@"/Resources/Themes/BlueSkin.xaml", UriKind.Relative));
}
}
DefaultSkin.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Red" />
</Style>
</ResourceDictionary>
BlueSkin.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Blue" />
</Style>
</ResourceDictionary>
Upvotes: 0