24

I have a WPF application with a theme (ShinyRed.xaml) and I want to have a button that when clicked changes the theme to ShinyBlue.xaml

I load in the theme initially in App.xaml:

<Application.Resources>
    <ResourceDictionary Source="/Themes/ShinyBlue.xaml"/>
</Application.Resources>

How might I do this?

H.B.
  • 166,899
  • 29
  • 327
  • 400
Entity
  • 7,972
  • 21
  • 79
  • 122

5 Answers5

36

How you could do it:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary x:Name="ThemeDictionary">
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="/Themes/ShinyRed.xaml"/>
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </ResourceDictionary.MergedDictionaries>
    <!-- ... -->
public partial class App : Application
{
    public ResourceDictionary ThemeDictionary
    {
        // You could probably get it via its name with some query logic as well.
        get { return Resources.MergedDictionaries[0]; }
    }

    public void ChangeTheme(Uri uri)
    {
        ThemeDictionary.MergedDictionaries.Clear();
        ThemeDictionary.MergedDictionaries.Add(new ResourceDictionary() { Source = uri });
    }

    //...
}

In your change method:

var app = (App)Application.Current;
app.ChangeTheme(new Uri("New Uri here"));
H.B.
  • 166,899
  • 29
  • 327
  • 400
  • @grv_9098: I don't know. – H.B. Sep 27 '13 at 06:57
  • what if I have only the name of the theme and not the URI? – Piero Alberto Dec 02 '14 at 11:09
  • 1
    @PieroAlberto: The URI can directly be constructed from name and location of the theme. See [MSDN](http://msdn.microsoft.com/en-us/library/aa970069.aspx). – H.B. Dec 02 '14 at 14:24
  • @H.B. I've done it in this way: ThemeManager.ApplicationThemeName = this.p_PRF_TEMA; but it is very slow... is there something reasons to slow down this action?? it takes something like 15 20 seconds to change the theme! – Piero Alberto Dec 02 '14 at 15:20
  • @PieroAlberto: Well, it has to recreate the entire UI, depending on its complexity that may take quite a while. – H.B. Dec 02 '14 at 15:38
  • @PieroAlberto: I don't think you can do anything, except using less fancy themes. – H.B. Dec 02 '14 at 16:36
7

Im using the following command to set the theme at runtime:

Application.Current.Resources.Source = new Uri("/Themes/ShinyRed.xaml", UriKind.RelativeOrAbsolute);
j0k
  • 22,600
  • 28
  • 79
  • 90
ShadowKras
  • 333
  • 4
  • 14
7

App.xaml

    <Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Themes/Font.xaml" />
            <ResourceDictionary Source="Themes/Light.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

In your code:

> Application.Current.Resources.MergedDictionaries[1].Source = new Uri("Themes/Dark.xaml", UriKind.RelativeOrAbsolute);

you can check with this to be sure nothing grow

Application.Current.Resources.MergedDictionaries.Count.ToString();

Thanh Bao
  • 67
  • 1
  • 3
7

Here is an article that will walk you through it:

http://svetoslavsavov.blogspot.com/2009/07/switching-wpf-interface-themes-at.html

Basically you need to remove the "old" theme from the resource dictionary and then merge in the new one. The above article shows you how to make this change very simple.

IAmTimCorey
  • 16,412
  • 5
  • 39
  • 75
  • 1
    I did something similar visible [here](https://github.com/Orace/SO/tree/main/SO_6229724), I think it's more MVVM friendly, moreover, it automatically feed the list of available themes. – Orace Apr 26 '22 at 17:15
2

H.B.'s answer did not run for me, I had to do this (works, tested):

Uri dictUri = new Uri(@"/Resources/Themes/MyTheme.xaml", UriKind.Relative);
ResourceDictionary resourceDict = Application.LoadComponent(dictUri) as ResourceDictionary;
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(resourceDict);

To pretty it up:

// Place in App.xaml.cs
public void ChangeTheme(Uri uri)
{
    ResourceDictionary resourceDict = Application.LoadComponent(uri) as ResourceDictionary;
    Application.Current.Resources.MergedDictionaries.Clear();
    Application.Current.Resources.MergedDictionaries.Add(resourceDict);
}

// Example Usage (anywhere in app)
private void ThemeRed_Click(object sender, RoutedEventArgs e)
{
    var app = App.Current as App;
    app.ChangeTheme(new Uri(@"/Resources/Themes/RedTheme.xaml", UriKind.Relative));      
}
Brock Hensley
  • 3,617
  • 2
  • 29
  • 47