13Aug/113

How to override theme resources in Windows Phone 7.5 (Mango)

Windows Phone 7 standard comes with some predefined themes and accent colors the user can choose of. You can read more about them here. This enables aaplications to automatically get adjusted to the users theme which makes the experience for user a lot better, because applications can adjust to the user's preferences. But sometimes this is not what you as a developer want. When you are for example building a facebook application you probably want the app to use the colors facebook uses, but you can of course have your own reason to not have your app always adjust to the current theme.

Windows Phone Themes

The different themes Windows Phone 7 comes with are included in the SDK and can be found in C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.0\Design on 64-bit windows installations and in C:\Program Files\Microsoft SDKs\Windows Phone\v7.0\Design on 32-bit installations. Each theme consists of a System.Windows.xaml file which defines the styles for the controls and a ThemeResources.xaml file which defines theme colors, brushes and text styles.

Overriding themes on Windows Phone 7.0

On Windows Phone 7.0 overriding the theme on the phone was quite easy and was well explained here and here. The technique comes down to defining a custom ResourceDictionary and add this ResourceDictionary to the Resources section in the app.xaml file.

<Application x:Class="ThemedApplication.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone">

    <!--Application Resources-->
    <Application.Resources>
        <ResourceDictionary x:Key="styles">
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Themes/styles.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>

    <Application.ApplicationLifetimeObjects>
    <!--Required object that handles lifetime events for the application-->
    <shell:PhoneApplicationService
        Launching="Application_Launching" Closing="Application_Closing" 
        Activated="Application_Activated" Deactivated="Application_Deactivated"/>
    </Application.ApplicationLifetimeObjects>

</Application>

This resource dictionary can then be used to override theme resources, for example the PhoneBackgroundColor and PhoneBackgroundBrush can be defined to be always white in your application as shown below.

<ResourceDictionary
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:controls="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls">

	<!-- 100 percent White-->
	<Color x:Key="PhoneBackgroundColor">#FFFFFFFF</Color>

	<SolidColorBrush x:Key="PhoneBackgroundBrush" Color="{StaticResource PhoneBackgroundColor}"/>
</ResourceDictionary>

An example solution is attached at the bottom of this post.

Overriding themes in Windows Phone 7.5 (Mango)

When using the above overriding in a Windows Phone Mango application the technique does not work anymore. The reason behind this probably has to do with the switch between the silverlight 3 runtime and the silverlight 4 runtime which added support for implicit syles and a lot of other xaml changes. In a thread on the App Hub forums, about this technique not working anymore, Peter Torr, Program Manager in the Windows Phone 7 Application Platform team, explained that this was actually a bug in Silverlight on Windows Phone 7.0 and that this is "fixed" in Mango. He also gives two work-arounds you can use in Mango.

The first is the use of implicit styles which is a feature of silverlight 4 that now comes with mango. The problem with this approach is that you have to override the style for all controls to get them use the theme overrides. If you only want to change some colors this option costs a lot more work than before.

The second option is a lot better to implement if you just want to change some theme colors, although it is not as beautiful as simply including an extra resource dictionary. It works by changing the built-in styles instead of overriding them. This changing of styles can be done in the following way.

(App.Current.Resources["PhoneBackgroundBrush"] as SolidColorBrush).Color = Colors.White;

Another advantage of this technique over implicit styles is that this should also works on Windows Phone 7.0 although I did not test that. A disadvantage is that there is no design-time support when using this technique. One question that comes up is were to place these overrides. I found out by experience that the most useful place to put this overrides is after the InitializeComponent() method call in app.xaml.cs as shown below.

public partial class App : Application
{
        /// <summary>
        /// Provides easy access to the root frame of the Phone Application.
        /// </summary>
        /// <returns>The root frame of the Phone Application.</returns>
        public PhoneApplicationFrame RootFrame { get; private set; }

        /// <summary>
        /// Constructor for the Application object.
        /// </summary>
        public App()
        {
            // Global handler for uncaught exceptions. 
            UnhandledException += Application_UnhandledException;

            // Standard Silverlight initialization
            InitializeComponent();

            // Change default styles
            InitializeStyleChanges();

            // Phone-specific initialization
            InitializePhoneApplication();

            // Show graphics profiling information while debugging.
            if (System.Diagnostics.Debugger.IsAttached)
            {
                // Display the current frame rate counters.
                Application.Current.Host.Settings.EnableFrameRateCounter = true;

                // Show the areas of the app that are being redrawn in each frame.
                //Application.Current.Host.Settings.EnableRedrawRegions = true;

                // Enable non-production analysis visualization mode, 
                // which shows areas of a page that are handed off to GPU with a colored overlay.
                //Application.Current.Host.Settings.EnableCacheVisualization = true;

                // Disable the application idle detection by setting the UserIdleDetectionMode property of the
                // application's PhoneApplicationService object to Disabled.
                // Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run
                // and consume battery power when the user is not using the phone.
                PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
            }

        }

        // Code to execute when the application is launching (eg, from Start)
        // This code will not execute when the application is reactivated
        private void Application_Launching(object sender, LaunchingEventArgs e)
        {
        }

        // Code to execute when the application is activated (brought to foreground)
        // This code will not execute when the application is first launched
        private void Application_Activated(object sender, ActivatedEventArgs e)
        {
        }

        // Code to execute when the application is deactivated (sent to background)
        // This code will not execute when the application is closing
        private void Application_Deactivated(object sender, DeactivatedEventArgs e)
        {
        }

        // Code to execute when the application is closing (eg, user hit Back)
        // This code will not execute when the application is deactivated
        private void Application_Closing(object sender, ClosingEventArgs e)
        {
        }

        // Code to execute if a navigation fails
        private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            if (System.Diagnostics.Debugger.IsAttached)
            {
                // A navigation has failed; break into the debugger
                System.Diagnostics.Debugger.Break();
            }
        }

        // Code to execute on Unhandled Exceptions
        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            if (System.Diagnostics.Debugger.IsAttached)
            {
                // An unhandled exception has occurred; break into the debugger
                System.Diagnostics.Debugger.Break();
            }
        }

        private void InitializeStyleChanges()
        {
            //87 percent Black - #DE000000
            (App.Current.Resources["PhoneForegroundBrush"] as SolidColorBrush).Color = Color.FromArgb(0xDE, 0x0, 0x0, 0x0);
            //100 percent White - #FFFFFFFF
            (App.Current.Resources["PhoneBackgroundBrush"] as SolidColorBrush).Color = Colors.White;
        }

        #region Phone application initialization

        // Avoid double-initialization
        private bool phoneApplicationInitialized = false;

        // Do not add any additional code to this method
        private void InitializePhoneApplication()
        {
            if (phoneApplicationInitialized)
                return;

            // Create the frame but don't set it as RootVisual yet; this allows the splash
            // screen to remain active until the application is ready to render.
            RootFrame = new PhoneApplicationFrame();
            RootFrame.Navigated += CompleteInitializePhoneApplication;

            // Handle navigation failures
            RootFrame.NavigationFailed += RootFrame_NavigationFailed;

            // Ensure we don't initialize again
            phoneApplicationInitialized = true;
        }

        // Do not add any additional code to this method
        private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
        {
            // Set the root visual to allow the application to render
            if (RootVisual != RootFrame)
                RootVisual = RootFrame;

            // Remove this handler since it is no longer needed
            RootFrame.Navigated -= CompleteInitializePhoneApplication;
        }

        #endregion
    }

An example using this technique is also attached below. If you have any questions on this don't hesitate to ask them in the comments.

WP7ThemedApplication

WP75ThemedApplication

Did you like this? Share it: