Thanks to Abe for the help, although in the end I went with a 'resolver' class that is included as a merged dictionary and resolves to the appropriate theme dictionary. The reasons are beyond the scope of he original question:
- You can load the appropriate dictionary at initialisation stage in
App.xaml.cs
without later having to overwrite the resources programatically based on user choice (before you would have to merge in a default theme, say Light at initialisation, and if the user had selected Dark then merge in the Dark theme later)
- Most importantly, If you do later overwrite the theme resources then other resources defined at initialisation that reference the overwritten resources will not change!
The code is based on an idea by this blogger.
ThemeResourceDictionaryResolver.cs
using System;
using System.IO;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Resources;
using System.IO.IsolatedStorage;
namespace YourAppName.View
{
public enum ApplicationTheme
{
Dark,
Light
}
public class ThemeResourceDictionaryResolver : ResourceDictionary
{
public ThemeResourceDictionaryResolver() : base()
{
ApplicationTheme theme;
if (System.ComponentModel.DesignerProperties.IsInDesignTool)
{
// Set here which dictionary you want to be loaded in Blend
theme = ApplicationTheme.Light;
}
else
{
ApplicationTheme theme;
if (!IsolatedStorage.ApplicationSettings.TryGetValue("ApplicationTheme", out theme))
{
// The 'standard' way to get the global phone theme
Visibility darkBGVisibility = (Visibility)Application.Current.Resources["PhoneDarkThemeVisibility"];
theme = (darkBGVisibility == Visibility.Visible) ? ApplicationTheme.Dark : ApplicationTheme.Light;
}
}
// Change the URI string as appropriate - this way refers to the Dictionaries
// which are set to 'Build: Page'. I couldn't get to work when set to Build as
// Content and using the simpler URI scheme.
Uri uri = new Uri(string.Format("YouAppName;component/View/Themes/{0}ThemeResourceDictionary.xaml", theme), UriKind.RelativeOrAbsolute);
ResourceDictionary res = this.LoadXaml<ResourceDictionary>(uri);
this.MergedDictionaries.Add(res);
}
// For some reason a simple ResourceDictionary() { Source = uri }; does not work,
// need to use this instead
T LoadXaml<T>(Uri uri)
{
StreamResourceInfo info = Application.GetResourceStream(uri);
if (info != null)
{
using (StreamReader reader = new StreamReader(info.Stream))
{
return (T)XamlReader.Load(reader.ReadToEnd());
}
}
return default(T);
}
}
}
MainResourceDictionary.xaml
...
<ResourceDictionary.MergedDictionaries>
<views:ThemeResourceDictionaryResolver />
</ResourceDictionary.MergedDictionaries>
<!-- StaticResources that refer to the theme StaticResources can go here -->
...
App.xaml
...
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="View/Themes/MainResourceDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
...