Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Dynamically change resource file and theme

Tags:

mvvm

wpf

c#-4.0

My project uses a ProjectTheme.xaml file for all WPF windows through out the project. The ProjectTheme.xaml file references a style theme as follows

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ResourceDictionary.MergedDictionaries>
        <!-- In order to modify the project's theme, change this line -->
        <ResourceDictionary Source="/MyProject;component/Themes/WPFThemes/Customized.xaml" />
    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

All WPF Windows references WindowBase.xaml

<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/MyProject;component/View/WindowBase.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Window.Resources>

WindowBase.xaml references customized titlebar Bar1.xaml

<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/MyProject;component/Themes/WPFThemes/Bar1.xaml" />
</ResourceDictionary.MergedDictionaries>

Bar1.xaml references ProjectTheme.xaml

<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/MyProject;component/ProjectTheme.xaml"/>
</ResourceDictionary.MergedDictionaries>

So the heriarchy is

  • Window1 references WindowBase.xaml
  • WindowBase references Bar1.xaml
  • Bar1 references ProjectTheme.xaml
  • ProjectTheme.xaml reference the real theme resource file.

This works fine. Now I want to dynamically change the project theme at run time without quitting the app. Assuming that I have several theme style files

  • Customized.xaml
  • Customized1.xaml
  • Customized2.xaml

My question is if it possible to dynamically update ProjectTheme.xaml file at run time to change the line from

<ResourceDictionary Source="/MyProject;component/Themes/WPFThemes/Customized.xaml" />

to

<ResourceDictionary Source="/MyProject;component/Themes/WPFThemes/Customized1.xaml" />

to achieve my objective? If yes, how do I do it? If no, what is the reason and what is the best (other) way to achieve my purpose?

I have tried the following but none of them work: the style does not change.

way 1

Application.Current.Resources.MergedDictionaries.Clear();
Uri NewTheme = new Uri(@"/MyProject;component/Themes/WPFThemes/Customized2.xaml", UriKind.Relative);
ResourceDictionary dictionary = (ResourceDictionary)Application.LoadComponent(NewTheme);
Application.Current.Resources.MergedDictionaries.Add(dictionary);

way 2

Application.Current.Resources.MergedDictionaries.RemoveAt(0);
Uri NewTheme = new Uri(@"/MyProject;component/Themes/WPFThemes/Customized2.xaml", UriKind.Relative);
ResourceDictionary dictionary = (ResourceDictionary)Application.LoadComponent(NewTheme);
Application.Current.Resources.MergedDictionaries.Insert(0, dictionary);

Note: In my real theme style files (Customized.xaml...) I used a combination of dynamic resource and static resource. Does that matters?

Thanks in advance.

like image 924
Shawn Avatar asked Jul 29 '13 15:07

Shawn


1 Answers

There are a few things to consider here.

First, anything defined with StaticResource will not get updated on a change. If you want a control to support changing the theme at runtime, you need to use DynamicResource so it knows to look for changes.

Your overall approach to changing the theme is correct. The easiest way to accomplish this is using Application-scoped resource dictionaries, making sure your ResourceDictionary is defined in your App.xaml. For adding a new resource, I've used snippets similar to the following:

ResourceDictionary dict = new ResourceDictionary();
dict.Source = new Uri("MyResourceDictionary.xaml", UriKind.Relative);

Application.Current.Resources.MergedDictionaries.Add(dict);

The part you may be confusing yourself over is when using resources within base classes. When you define a resource in a class, the resource will be local to an instance of that type. Think of the XAML compiling into it's own InitializeComponent() method on classes, meaning you can't change the original XAML and expect the changes to go to all instances. On the same note, changing the resources on a class instance doesn't effect other instances.

Since your question really contains two separate concerns (application theming and changing control resources), I would focus on ensuring your application resources are updating properly and using DynamicResource, and hopefully the information I've provided would be sufficient for understanding why certain other resources may not be updating yet.

like image 150
Will Eddins Avatar answered Sep 18 '22 08:09

Will Eddins