I am new to WPF. I was trying to skin my UI with light and dark color themes. So I defined two ResourceDictionaries as following:
<!-- Light.xaml-->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="Background">#DDDDDD</Color>
</ResourceDictionary>
and
<!-- Dark.xaml-->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="Background">#222222</Color>
</ResourceDictionary>
Then I created a skin ResourceDictionary as following:
<!-- Skin.xaml-->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="BackgroundBrush" Color="{DynamicResource Background}"/>
</ResourceDictionary>
Finally, I loaded them in App.xaml as MergedDictionaries as following:
<!-- App.xaml-->
<Application x:Class="Example.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Light.xaml"/>
<ResourceDictionary Source="Skin.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
with Application code as following:
//App.xaml.cs
using System.Windows;
namespace Example
{
public partial class App : Application
{
bool dark = false;
public void SwitchSkin()
{
if (dark)
{
Resources.MergedDictionaries[0].Source = new Uri("Light.xaml", UriKind.Relative);
}
else
{
Resources.MergedDictionaries[0].Source = new Uri("Dark.xaml", UriKind.Relative);
}
dark = !dark;
}
}
My example window code looks like:
<!-- MainWindow.xaml-->
<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Background="{DynamicResource BackgroundBrush}">
<Button Click="SwitchSkin" Width="200" Height="24">Switch Skin</Button>
</Grid>
</Window>
along with
//MainWindow.xaml.cs
namespace Example
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void SwitchSkin(object sender, RoutedEventArgs e)
{
App app = (App)Application.Current;
app.SwitchSkin();
}
}
}
The switching code does run and App.Resources["Background"] updates however App.Resources["BackgroundBrush"] does not updates its color. Consequently, the background of the Grid is unchanged. To clarify, Grid background does not update even if I use DynamicResource. I just experimented and moved the skin resources and SwitchSkin methhod to MainWindow. Oops, it works there. MainWindow.Resources["Background"] and MainWindow.Resources["BackgroundBrush"] have same color. Though, MainWindow.Resources["Background"] is newer object, MainWindow.Resources["BackgroundBrush"] is same old object as verified from debugger. I interpret that as DynamicResource does not work as expected in the first case however it does work as expected in the second case.
My question is why the behaviors defer in two cases. I found numerous questions about skinning WPF apps but I was unable to meet one that could explain this to my understanding. Here is the modified code (which does work as expected) for shake of completeness:
<!-- App.xaml-->
<Application x:Class="Example.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
</Application>
with Application code as following:
//App.xaml.cs
using System.Windows;
namespace Example
{
public partial class App : Application
{
}
My example window code looks like:
<!-- MainWindow.xaml-->
<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Example"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Light.xaml"/>
<ResourceDictionary Source="Skin.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{DynamicResource BackgroundBrush}">
<Button Click="SwitchSkin" Width="200" Height="24">Switch Skin</Button>
</Grid>
</Window>
along with
//MainWindow.xaml.cs
namespace Example
{
public partial class MainWindow : Window
{
bool dark = false;
public MainWindow()
{
InitializeComponent();
}
private void SwitchSkin(object sender, RoutedEventArgs e)
{
if (dark)
{
Resources.MergedDictionaries[0].Source = new Uri("Light.xaml", UriKind.Relative);
}
else
{
Resources.MergedDictionaries[0].Source = new Uri("Dark.xaml", UriKind.Relative);
}
dark = !dark;
}
}
}
I am sorry if it reads too long. I could not make it any more compacter.
It should work as expected if you replace the resource dictionaries with new ones each time you switch themes: