Home > Mobile, UX > Fixing Color Themes on Windows Phone

Fixing Color Themes on Windows Phone

There’s a lesser-known feature of Windows Phone that allows customization of control colors without touching their Style or Template. This is accomplished by overriding special color resources defined by the OS. In fact there’s a whole page on MSDN dedicated to listing the color resources and how they’re used by various controls.

 

For example, adding this to App.xaml:

 

<Application.Resources>
    <SolidColorBrush x:Key="PhoneRadioCheckBoxBorderBrush" Color="Red"/>
    <SolidColorBrush x:Key="PhoneRadioCheckBoxCheckBrush" Color="Blue"/>
</Application.Resources>

 

Produces this in the designer:

 

image

 

And in Windows Phone 7.0 that’s what you see at run time too. However, starting with Windows Phone 7.5 the colors DON’T display correctly at run time. At run time the color changes lost and the default OS colors are used instead.

 

This is a known issue and it’s discussed several places, but the article I like best is Windows Phone Mango Custom Application Theme Step-by-Step. I like the workarounds they provide too, especially Option1: Dynamically load all colors from a XAML file. With this approach, color resources are moved to an external file then loaded in at run time. The problem I have with their implementation is that it breaks the design experience since it does not allow these resources to be merged during design. So I set off to provide a simple solution that would work at both design time and at run time.

 

My solution starts in a similar way by adding a resource dictionary to the application that will contain the color theme:

 

image

 

I put mine in a subfolder but that’s just a matter of taste.

 

Next, we change the Build Action of this file to ‘Content’ instead of ‘Page’ (more on that in a moment).

 

image

 

Now we need to move all of our color resources out of App.xaml and into this resource dictionary. Here’s what mine looks like after the move:

 

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
xmlns:System="clr-namespace:System;assembly=mscorlib">

    <SolidColorBrush x:Key="PhoneRadioCheckBoxBorderBrush" Color="Red"/>
    <SolidColorBrush x:Key="PhoneRadioCheckBoxCheckBrush" Color="Blue"/>
    <SolidColorBrush x:Key="PhoneRadioCheckBoxPressedBrush" Color="Green"/>
    <SolidColorBrush x:Key="PhoneButtonBasePressedForegroundBrush" Color="Yellow"/>

</ResourceDictionary>

 

Finally – and this is one of the differences in my approach – we go back to App.xaml and merge this resource dictionary back in:

 

<!–Application Resources–>
<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="CustomTheme/ThemeResources.xaml"/>
        </ResourceDictionary.MergedDictionaries>
        <local:LocalizedStrings xmlns:local="clr-namespace:ThemeSample" x:Key="LocalizedStrings"/>
    </ResourceDictionary>
</Application.Resources>

 

Now build the application and when you view the page in the Visual Studio (or Blend), you’ll see the color changes applied:

 

image

 

We’re not quite done yet though because the changes are still lost at run time. So what did we gain by moving the resources to a separate dictionary? We got the ability to update them quickly and easily at runtime using a little code.

 

In the sample code you’ll find a file called ResourceExtensions.cs. With this added to our project we can call one line of code and our colors are fixed at run time too. The method is called MergeColors and it’s an extension method added to the ResourceDictionary class itself. This method needs to be called inside the App() constructor. It should be called after InitializeComponent() but before InitializePhoneApplication(). Here’s a snippet:

 

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

    // Standard XAML initialization
    InitializeComponent();

    // Merge custom colors
    App.Current.Resources.MergeColors(new Uri("/CustomTheme/ThemeResources.xaml", UriKind.Relative));

    // Phone-specific initialization
    InitializePhoneApplication();

    

 

MergeColors first looks to see if the specified resource dictionary is already merged into the application (which it should be, since we merged it above so we can see our colors at design time).  If the dictionary is merged it’s removed. Otherwise, it’s loaded into memory. Finally, each of the color resources is updated in the parent dictionary.

 

Now we have our custom colors at both design time and run time.

 

image

 

So, why did we change the Build Action above to ‘Content’ instead of ‘Page’? It’s because the Uri for a ‘Content’ dictionary is much closer to the Uri at run time. That makes it easier for MergeColors to see if the dictionary is merged at runtime and remove it.

 

That’s everything you need if you download the source. For completeness and for those who would rather copy and paste than download, here’s what’s inside the MergeColors extension method:

 

static public void MergeColors(this ResourceDictionary dictionary, Uri resourcesUri)
{
    // Validate
    if (dictionary == null) throw new ArgumentNullException("dictionary");
    if (resourcesUri == null) throw new ArgumentNullException("resourcesUri");

    // Check to see if the resources are currently merged
    var resources = (from r in dictionary.MergedDictionaries
                    where resourcesUri.OriginalString.Contains(r.Source.OriginalString)
                    select r).FirstOrDefault();

    // If resources are currently merged, remove them
    if (resources != null)
    {
        dictionary.MergedDictionaries.Remove(resources);
    }
    // Otherwise, load them
    else
    {
        resources = new ResourceDictionary { Source = resourcesUri };
    }

    // Loop through all color entries defined in the resource file
    // If the same entry is found in the target dictionary, update its value
    foreach (DictionaryEntry entry in resources)
    {
        // Try to get source and target values as color brushes
        var colorBrush = entry.Value as SolidColorBrush;
        var existingBrush = dictionary[entry.Key] as SolidColorBrush;

        // If both were found, update
        if (existingBrush != null && colorBrush != null)
        {
            existingBrush.Color = colorBrush.Color;
        }
    }
}

Advertisements
Categories: Mobile, UX Tags:
  1. April 25, 2014 at 3:17 am

    Boss!
    You saved my Life!!!
    It was 7 then 8 now 8.1. But still they don’t have any workaround to change border color!

  1. No trackbacks yet.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: