... Anyone starting to see a problem here? Maybe not, its actually a little subtle. Basically, the problem is surrounding the use of static resources, and in particular trying to change a static resource at runtime.
The short and curlys are that it's not an easy thing to do. As the resource is static, its loaded once at the start (during the InitializeComponent() call in the App.xaml.cs contructor) and if you want to change it there after, you have to find the particual resource from the application resource dictionaries (see Application.Current.Resources) and changes its value manually. I've found that even clearing the resource dictionary and re-loading didn't work either... go figure!
So my solution was to not even bother to load the generic style (containing static resources) if there was an external one we wanted to use instead. A bit like the code below:
public partial class App : Application { private static IDisplayPage m_currentPage = null; private IDictionary m_initParams = null; public App() { this.Startup += this.Application_Startup; this.Exit += this.Application_Exit; this.UnhandledException += this.Application_UnhandledException; } ////// Occurs when the application is started/// /// /// private void Application_Startup(object sender, StartupEventArgs e) { // Set the init params and intialise the application m_initParams = e.InitParams; Initialise(); } private void Initialise() { // We need to load the new theme before InitaliseComponent so that the right static resources are loaded first time! // Otherwise the defaults will be loaded and the newer ones are ignored. string themeLocation = ConfigurationManager.Settings["ThemeLocation"]; if (string.IsNullOrEmpty(themeLocation) == false) { // We have a theme location present, so try to load it! The application will be // loaded after the theme is downloaded and applyed to the application resources. LoadTheme(themeLocation); } else { // Skip loading the theme as we haven't supplied one. Go straight for the good stuff! LoadApplication(); } } ////// Loads the given theme into the application resources /// /// private void LoadTheme(string themeLocation) { // Create the client to load the file WebClient client = new WebClient(); // When the download is complete, apply it! client.DownloadStringCompleted += ThemeDownloaded; // Start the download of the theme off. The location should be local as the screen will be blank while downloading! client.DownloadStringAsync(new Uri(themeLocation, UriKind.RelativeOrAbsolute)); } ////// Occurs when an external theme has finished downloading /// /// /// private void ThemeDownloaded(object sender, DownloadStringCompletedEventArgs e) { string themeLocation = ConfigurationManager.Settings["ThemeLocation"]; if (!e.Cancelled && (e.Error == null)) { try { // Read in the external style and store it as a resource dictionary ResourceDictionary dictionary = XamlReader.Load(e.Result) as ResourceDictionary; if (dictionary != null) { // If the reading was successful, store it as our current dictionary Application.Current.Resources.MergedDictionaries.Add(dictionary); } } catch (XamlParseException ex) { ServiceClient webService = new ServiceClient(); webService.LogErrorAsync("Problem parsing theme (" + themeLocation + "): " + ex.Message, ex.StackTrace, SLUtilities.GetClientVersion()); } } else { if (e.Error != null) { ServiceClient webService = new ServiceClient(); webService.LogErrorAsync("Problem loading theme (" + themeLocation + "): " + e.Error.Message, e.Error.StackTrace, SLUtilities.GetClientVersion()); } } // After our attempt at loading an external theme, load the rest of the application LoadApplication(); } ////// Loads the rest of the application, initialising the components if an external theme hasn't already been loaded. /// private void LoadApplication() { // If we haven't been able to reach an external theme for the app, use the internal default if (Application.Current.Resources.MergedDictionaries.Count == 0) { // Initialise the main app.xaml page, including resources and styles InitializeComponent(); } // Do the rest of our application specific setup } }
So as you can see, the application when first loaded, checks a settings file in the .xap and a location to an external theme. If it can find it, the theme is downloaded and instead of calling InitializeComponents(), we just load the resources in the theme instead. If there is no location for an external theme, we just load up the generic resources and styles using InitializeComponents() as normal.
I have to say, not the most satisfying work around, but it does work!
Hope this helps someone!
No comments:
Post a Comment