Click here to Skip to main content
15,949,686 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am developing a WPF MVVM-based application using c#.
I am trying to create 2 themes light & dark.
There will be a button on all windows in either the header or the footer to switch between light & dark themes.
I have created a folder hierarchy like this
AppName/
>Resources/
Light.xaml
Dark.xaml

all appropriate styles & solid colour brushes are defined in each XAML file.
I want to know how to switch the theme with a button click.

What I have tried:

ResourceDictionary resX = new ResourceDictionary();
resX.Source = new Uri("/Resources/Light.xaml", UriKind.RelativeOrAbsolute);
this.Resources.MergedDictionaries.Clear();
this.Resources.MergedDictionaries.Add(resX);
This code executes on button click.
Posted
Comments
Richard Deeming 3-May-24 6:54am    
Aside from wrapping the update in a BeginInit/EndInit block, that code looks like it should work.
this.Resources.BeginInit();
try
{
    this.Resources.MergedDictionaries.Clear();
    this.Resources.MergedDictionaries.Add(resX);
}
finally
{
    this.Resources.EndInit();
}

So what's the problem?

The ability to switch a theme is largely going to depend on whether or not you have declared a particular resource as a StaticResource or a DynamicResource. Suppose you have defined a brush colour as a StaticResource. When the application starts up, that resource is going to be baked in for the duration of the application lifetime, and can't be switched. If it's a DynamicResource, then reloading it results in the brush colour changing.

While this[^] is an old article, it should still hold true today.
 
Share this answer
 
Here is my solution
I created 4 resource files namely Light.xaml, Dark.xaml, Black.xaml, Red.xaml. Each resource file contains the same named style setters for each element of the app with different values. I am using Style="{DynamicResource ResourceKey=name of style}" to assign the style in the XAML pages & windows.

Also in the App.config file create an appsettings key named AppTheme and assign it a default value.
In App.xaml.cs
private void Application_Startup(object sender, StartupEventArgs e)
{
    string theme = ConfigurationManager.AppSettings["StashTheme"];

    if (theme != null || theme != "")
    {
        AppThemeManager.ApplyTheme(new System.Uri("/Resources/" + theme + ".xaml", UriKind.Relative));
    }
    else
    {
        AppThemeManager.ApplyTheme(new System.Uri("Resources/Light.xaml", UriKind.Relative));
    }
    Dashboard dashboard = new dashboard();
    dashboard.Show();
}


In AppThemeManager class

internal class AppThemeManager
{
    public AppThemeManager() { }

    public static void ApplyTheme(Uri themeURI)
    {
        ResourceDictionary theme_resx = new ResourceDictionary() { Source = themeURI };

        App.Current.Resources.Clear();
        App.Current.Resources.MergedDictionaries.Clear();
        App.Current.Resources.MergedDictionaries.Add(theme_resx);

        string[] parts = themeURI.ToString().Split('/');
        string theme = parts.Last().Split('.')[0];

        if (theme != "")
        {
            Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            config.AppSettings.Settings["AppTheme"].Value = theme;
            config.Save(ConfigurationSaveMode.Modified);
        }
    }
}


You can also call the ApplyTheme method on the button click or comboboxitem changed event.
Be sure to add any required resource files to the merged dictionary after clearing them out in the ApplyTheme method.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900