Creating a WP7 app: Supporting dark and light themes

In this post I will show how to create an application that supports both the dark and the light themes, without the hassle of creating two sets of images, icons or styles.

This post is part of a series of posts I’ve planned on my findings while writing a windows phone 7 application (see the previous post, on the problem of caching urls, here).

Colors and themes

Owners of a WP7 device can choose whether they have a dark theme of a light theme while using the phone. Users can change this anytime they feel like it. As a WP7 developer your application will need to take into account that both themes can be active. In this post I will only concentrate on the background color which can be dark or light, and ignore the accent color.


When your application starts up, the current active theme will influence the way your application looks. That is, as long as you haven’t explicitly defined certain styles and colors, all the standard WP7 controls will show up in a correct manner. Let’s see how this works. Suppose your extremely cool and intuitive application has a nice shiny button such as this:
<Button Content="Shiny"/>
The button will have a look dependent of what theme is being used (note that the accent color of the theme doesn’t matter here). So if we run our application we will see the following buttons:

When the dark theme(left image) is active our button, and all other standards controls, will automatically have a black background and white foreground brush. However, when the light theme is active, what WP7 basically does is switch the Foreground and the Background property of all controls, as long as they’re not defined explicitly.

The moment however, you define a specific color for a control, WP7 won’t help you with switching colors anymore and making sure your controls will look ok in the active image.
Imagine a fancy yellow button:
<Button Content="Shiny" Background="Yellow"/>

Testing this button in the white theme gives pleasant results (make note: I’m not designer, I’m already happy if my color schemes don’t induce a headache within 5 minutes):

Now let’s see how this looks like in the dark theme:

Ugh..My eyes! What happens is, since we didn’t define the Foreground explicitly (which the text and border in the button uses) the black borders and black content were switched, but the yellow background was not. Still, ugly or not, the button is still sort of useable.

However, suppose you really need your button to have a specific color…for example white.
<Button Content="Shiny" Background="White" Button>
Let’s see what this looks like in the both themes:

Yippie, we made a white rectangle!

Anyhow, you get the point. In summary: If you are planning on playing with colors, you better be prepared to write two different styles that will vary according to the theme being is. Let’s see how we can cope with the fact that your phone can have a dark and light theme.

Detecting the theme

To know which theme is active I’ve seen several tricks (old-school style is apparently by looking at the current RGB values of the fore-or background brush) but in my opinion, the following is the most straightforward (see here for a list of all predefined theme resources):

bool dark= ((Visibility)Application.Current.Resources["PhoneDarkThemeVisibility"] == Visibility.Visible)

We can simply check whether the PhoneDarkThemeVisibility is Visible or not (the “PhoneLightTheme” that also exist will have the opposite value). If it is, we now what the current theme is. We can do 2 things, depending on the type of buttons being used,:

  • Load in a new style for your controls.
  • Work with images that replace the back-and foreground of the buttons.

Load new style

A lot has been written on how to change the theme of your controls at runtime, so in summary what needs to be done is something along the lines of:

  1. Create 2 brushes or styles, one for each theme, and place them in your application resources (App.xaml file)
  2. When you need to change to the other theme, remove the current brush or style from your application, e.g.:

    App.Current.Resources.Remove("MyCurrentTheme");

  3. Insert the other brush or theme in your application, e.g.:

    App.Current.Resource.Add("MyCurrentTheme", mydarkTheme)

Images and themes

The problem described earlier with buttons looking like ugly betty also applies to images. Suppose you created a nice transparent image to be used in your button:

The black part of the image is transparent. Using this button in the dark theme gives pleasant results:

<Button>
component/home_white.png">
</Button>

In fact, thanks to the transparency, we can do fun stuff like:

<Button Background="Green">
component/home_white.png">
</Button>

However, if we don’t explicitly defined a background (why should you? Remember, Metro design is all about no chrome and other silly non-usable UI changes…check out your iPhone or Android to see what I mean :p ) and then switch themes, we again have created a very white rectangle:

We can solve this problem in 2 ways:

  1. Have a different set of images for both themes
  2. Have one set, but toy around with transparency

Image sets

Having two sets of image has the benefit of knowing exactly what each button (or control) will look like in each theme. The drawback is that every time you change one image, you also have to change the other one.Still, from time to time this is how things go. To work with image sets, the most straightforward solution , a variation from here, goes as follows:

  1. Create 2 sets of images and include both as assets, content or resources to your solutions. Keep all names the same, but only change the folder in which they reside (e.g. “buttons/dark/” and “buttons/light”).
  2. When initializing the page or application, query the phone which theme is used. (see earlier).
  3. Load in the correct set of images

How to do this in practice? Suppose we defined a button somewhere in our UI as follows:

<Button >
<Image x:Name="btnImage" ></Image>
</Button>

Following code, which can be placed in the constructor of the page can then be:

Uri uri;
if ((Visibility)Application.Current.Resources["PhoneDarkThemeVisibility"] == Visibility.Visible)
uri = new Uri("/buttons/dark/home.png", UriKind.Relative);
else
uri = new Uri("/buttons/light/home.png", UriKind.Relative);
btnImage.Source= new BitmapImage(uri) ;

Note that we could also easily refactor this code in order to define both folderlocations-strings (“button/dark/” and “button/light/”) elsewhere and only have to write the filename once (“home.png”);

One image set, changing transparency

For those among us, like me, who don’t really like to toy around in image, a third option exists to solve the changing themes problem, explained here and here (first answer). This solution, which I prefer above the ones explained earlier, goes as follows:

  1. Create a single set of black/white images
  2. Implement a style which changes the black/white colors of the images according to the active theme (in practice: change the active transparency from black to white or vice versa)
  3. Apply the style to all controls

The drawback of this method is that it only gives nice results for binary colored colored images. I suppose with a bit of extra coding this recipe can also be applied to other types of images, but it won’t be as straightforward.

In practice, 2 things are needed:

First , a new style is created which can then be applied to any button in the application. In short (full version here):

<phone:PhoneApplicationPage.Resources>
<Style x:Key="IconButton" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{StaticResource PhoneForegroundBrush}"/>
<Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
<Setter Property="BorderThickness" Value="{StaticResource PhoneBorderThickness}"/>
<Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilySemiBold}"/>
<Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMediumLarge}"/>
<Setter Property="Padding" Value="10,3,10,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
<VisualStateManager.VisualStateGroups>
………….
</VisualStateManager.VisualStateGroups>
<Border x:Name="ButtonBackground" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="0" Margin="{StaticResource PhoneTouchTargetOverhang}">
<Grid x:Name="ContentContainer" OpacityMask="{TemplateBinding Content}" Background="{TemplateBinding Foreground}"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

For now, let us zoom in on the main ‘trick’ in the style:

<Grid x:Name="ContentContainer" OpacityMask="{TemplateBinding Content}" Background="{TemplateBinding Foreground}"/>

What we created here is basically a new type of Button which uses an ImageBrush as its content, containing the image we want to be shown on the button. However, the style will make sure that we use an alphamask that uses the image as the mask (so make sure your image actually has an alpha channel; Tip: use PNG!). By setting the background color to the current Foreground color (dependent of the theme chosen) we make sure that the non-transparent portion of the image will have the correct color (what simply happens is that we ‘cut out’ the image from the foreground, using the alphamask).

All that remains now is applying this style to our controls (buttons in this example):

<Button Style="{StaticResource IconButton}" >
<ImageBrush ImageSource="/icons/home.png">
</Button>

If we now test our application we see that the button shows correctly, independent of the active theme. The picture on the left shows the dark theme:

6 gedachten over “Creating a WP7 app: Supporting dark and light themes

  1. I just tried for a game I developing and it worked! Thanks a bunch. I am really grateful.

    Like

  2. Hi! I realize this is sort of off-topic however I
    had to ask. Does running a well-established blog like yours require
    a lot of work? I’m brand new to blogging however I do write in my journal everyday. I’d like
    to start a blog so I can share my personal experience and feelings online.
    Please let me know if you have any suggestions or tips
    for new aspiring bloggers. Appreciate it!

    Like

  3. I’m not sure where you’re getting your information, but good topic.

    I needs to spend some time learning much more or understanding more.
    Thanks for magnificent info I was looking for this information for my mission.

    Like

  4. Thanks for a helpful post! I was actually looking for the OpacityMask thing and it is good to find it highlighted here)

    Like

Plaats een reactie

Deze site gebruikt Akismet om spam te bestrijden. Ontdek hoe de data van je reactie verwerkt wordt.

search previous next tag category expand menu location phone mail time cart zoom edit close