Themes for Silverlight Applications

A theming system for Silverlight 2 apps... this post steps you through creating a set of theme assets, adding customizability of themes (colors and fonts in the sample), and using them in an application, including selecting a theme dynamically... complete with source code.

Silverlight provides the ability to completely customize the look and feel of controls (including the intrinsic ones - often something one desires when working with HTML), while preserving their behavior, through a combination of styles, templates, visual states and transitions. For example, the screen shot below (click to run), shows the stock look replaced with a sketch or napkin look and feel (based on Corrina's (our resident designer) rough skin sample).

Red, Blue, Green Sketch controls vs. Stock Silverlight controls (Click to Run)

The general approach to defining app-wide customizations in Silverlight today is to define a bunch of named styles in the Resources dictionary of the Application's XAML markup, and then reference them via the StaticResource markup extension elsewhere in the UI. However this approach has some shortcomings, that HTML designers have long overcome through the use of separate and multiple style sheets.

  • Application.Resources become unwieldy. Styling just a few controls can quickly lead to 1000's of lines of XAML. Simply navigating and finding what you're looking for becomes a chore. The first thing I do when I pick up a theme created by a designer is try to separate out the set of individual styles per control.
  • Styles get mixed up with functionality. To me (speaking from my developer mindset), Application.Resources is better suited for global data and other behavioral aspects of my application shared across the application, rather than serving as a container for visual and presentation items. Hence my desire for separating these out, so I can just import a designer's creation into my app.
  • Styles are not easily swappable. This is also partly because styles are mixed up in an application's resources. If I want to switch from say a Flat look to a Sketch look in my app, I have to carefully swap things in and out, track dependencies between styles etc.
  • Styles cannot be dynamically selected. I'd like to create an application that can dynamically pick a different font/color scheme, i.e. have the notion of themes, perhaps based on a user's profile. This requires some level style merging as well as the ability to include multiple themes separately in the xap package and pick one of them dynamically.

There are probably a few other issues that came up in my discussion with Corrina but suffice to say, these stem from placing styles statically in Application.Resources. Designers working on HTML have long overcome these issues through the use of separate and multiple style sheets that help separate content and behavior from presentation.

I started to think about how I might go about creating a more concrete theming system for Silverlight apps, and naturally I looked to CSS as well as what we had done with Themes in ASP.NET (the App_Themes folder pattern) to see how much I could re-use here, while still feeling natural in the context of Silverlight, and reasonably designable via Expression Blend. The end result was a very small theming feature addition to on-going Silverlight.FX framework.

In the rest of the post, I'll describe how to go about creating the themes above and consuming them in the application.

I've included the code for the theming feature along with the rest of Silverlight.FX with the sample app for you to download and use.

Creating the Theme Assets
The first step is creating the custom control looks, using Expression Blend and the goodness that is Silverlight styles, templates, storyboards and visual state manager. I'll be using Expression Blend for the most part for this part of the work (putting on my virtual designer hat for a moment).

An asset in a theme is simply a user control. At runtime, the Resources defined within the user control are extracted into the application. The reason I chose the user control approach is to allow a design experience in Blend. Furthermore, you can place test controls within the user control that provide an interactive design experience for the styles.

To create the custom sketch look for the Button, simply create a new user control, in a file named Button.xaml for example, and add a Button into it. Right click on the Button, and choose to edit its template. At this point you can define the overall look. I won't go into this step by step - check out Tim's blog post on the VisualStateManager for an overview of the design process.

Building the Button asset in Expression Blend

Tip: You don't need an actual code-behind class when creating an asset. When you add a xaml file in Blend, simply uncheck the "Include Code File" checkbox. Blend will actually have an error opening the file in design view when you do this. To fix that, simply add x:Class="UserControl" to your <UserControl> tag in source view. This effectively sets the code-behind class to be the existing UserControl class from the framework.

You can repeat this process for any other assets you need to create. For my particular sample, I defined styles for TextBox and CheckBox as well. Note that if you want to, you can combine styles for multiple controls in the same xaml file. I created them separate, because I prefer to keep each xaml file small and simple.

Making Assets Themeable
The next step is to enable customizability of the asset from a theme. This is optional... depends if you need or want to support the ability to have multiple themes or variants of the same visual structure. For example, you might want the same overall button look, but create multiple variants of the sketch button that differ in terms of colors and fonts.

The goal is to enable this without requiring duplication of the entire style in each theme. The approach we'll take is creating named values for numbers, colors, strings that are then referenced in the style. I'll switch into XAML view in the designer and add the following resources within my asset.

<UserControl
  ...
  xmlns:sys="clr-namespace:System;assembly=mscorlib">

  <UserControl.Resources>

    <sys:Double x:Key="lineThickness">2</sys:Double>
    <Color x:Key="lineColor">#000000</Color>
    <Color x:Key="fillColor">#CAF562</Color>
    <Color x:Key="fontColor">#000000</Color>
    <SolidColorBrush x:Key="fontBrush" Color="{StaticResource fontColor}" />
    <sys:String x:Key="fontFamily">Verdana</sys:String>
    <sys:Int32 x:Key="fontSize">11</sys:Int32>
    <sys:String x:Key="fontWeight">Bold</sys:String>
    ...
  </UserControl.Resources>
</UserControl>

These resources provide defaults for various aspects of the asset. To complete the work of creating the asset, I'll refer to them within the button's style. Specifically, I'll replace the literal constant values with references to the named resources.

<Style x:Key="sketchButton" TargetType="Button">
  <Setter Property="Foreground" Value="{StaticResource fontBrush}" />
  <Setter Property="FontFamily" Value="{StaticResource fontFamily}" />
  <Setter Property="FontWeight" Value="{StaticResource fontWeight}" />
  <Setter Property="FontSize" Value="{StaticResource fontSize}" />
  ...
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="Button">
        <Grid x:Name="RootElement">
          ...
          <Path x:Name="LineElement" StrokeThickness="{StaticResource lineThickness}" ...>
            <Path.Stroke>
              <SolidColorBrush Color="{StaticResource lineColor}" />
            </Path.Stroke>
          </Path>
          <Path x:Name="FillElement" ...>
            <Path.Fill>
              <SolidColorBrush Color="{StaticResource fillColor}" />
            </Path.Fill>
          </Path>
          ...
        </Grid>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

The design view in Blend continues to work, in that the resources are resolved just fine at design-time.

Creating Themes
Now it is time to use the assets to define an actual theme. A theme can contain theme-specific resources and link in specific assets through an include mechanism. It can also override any default values for the variables we just created.

In my sample, I created Red, Blue and Green themes. Each theme is defined in a file named Theme.xaml, and placed in a folder whose name represents the name of the theme. For example, Theme.xaml in the Blue folder is defined as follows (note this also shows an embedded font sample):

<fxa:Theme
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:fxa="clr-namespace:Silverlight.FX.Applications;assembly=Silverlight.FX"
  xmlns:sys="clr-namespace:System;assembly=mscorlib"
  Includes="Button, CheckBox, TextBox">

  <fxa:Theme.Resources>

    <Color x:Key="fillColor">#7FCAFF</Color>
    <sys:String x:Key="fontFamily">/Assets/Architect.ttf#Architect</sys:String>
    <sys:Int32 x:Key="fontSize">16</sys:Int32>
    <sys:String x:Key="fontWeight">Normal</sys:String>

  </fxa:Theme.Resources>

</fxa:Theme>

The Theme class is from my framework. It derives from UserControl, and adds the Includes property. At runtime, the theme processor looks for each named include within the theme directory, and if it is not found, it looks up one directory for a shared asset (more on the directory structure below).

As you can see in the XAML above, the Blue theme is overriding the default fillColor, and font information, as well as including a reference to an embedded font.

Adding Themes to the ApplicationUsing the Themes
Once I've created the assets and themes, it is now time to consume them in an application. Imagine the designer handing these off to the developer, as I get back into developer mode.

At runtime, the theme feature expects to find all the themes placed within a "Themes" folder within the application's xap package. I'll simply add the folder to my application, and make sure the Build action for each XAML is set to "Content" (so it gets included into the xap), and remove any custom build tool associated with the xaml files (since I don't want any code-behind to be generated).

Selecting a Theme
The final step is to select a theme, given I have three in the application. Here is what I have in my App.xaml file:

<fxa:XApplication ...
  xmlns:fxa="clr-namespace:Silverlight.FX.Applications;assembly=Silverlight.FX"
  ThemeName="Green" WindowName="Page">
</fxa:XApplication>

XApplication is a derived Application type defined within the Silverlight.FX framework. It has various useful features. The two shown here are the ThemeName property and the WindowName property.

The application loads up the specified theme, and then loads up the specified window as the root visual of the application. Incidently, there is nothing in the code-behind for the application. All of this is declarative.

Selecting a Theme Dynamically
Given I have multiple themes, I want to pick one based on some external variable. For example, this might be based on the user's preferences. XApplication supports this scenario through the use of Silverlight's initParams.

<fxa:XApplication ...
  xmlns:fxa="clr-namespace:Silverlight.FX.Applications;assembly=Silverlight.FX"
  ThemeName="$theme|Green" WindowName="Page">
</fxa:XApplication>

Basically this says, lookup the "theme" variable in initParams, and if its found, use its value. Otherwise use the "Green" theme as the default.

If you are using ASP.NET, you might be storing the user's preference via the Profile feature, and you can use it to set the initParams when rendering out the Silverlight object tag.

<object style="width: 290px; height: 100px" type="application/x-silverlight">
  <param name="source" value="ThemeSample.xap" />
  <param name="version" value="2.0" />
  <param name="initParams" value="theme=<%= Profile.Theme %>" />
</object>

How it Works?
You're probably curious about how the system overall works, so here is a summary of the mechanics. However, you do not need to have an in-depth understanding of the framework implementation to use the feature.

The XApplication application class loads the specified theme during its Startup event handling, and then loads the root visual. This is important as the theme must be loaded before the first visual is created, so that any resource references can be resolved.

The theme processor is responsible for a few things. It looks for the right Theme.xaml file and loads that in the xap. It then parses the XML to extract out the resources, as well as the list of includes, and then recursively parses the XML in those included assets to extract out resources. As it is extracting the resources, it merges them into a single XML stream consisting of unique keys (i.e. keys in Theme.xaml override the same keys in assets - this allows the overriding).

The merged XML stream is wrapped in a tag, and parsed via XamlReader.Load. The theme processor pulls out the resulting resources using the keys it accumulated during the XML parsing, and adds them into Application.Resources.

Finally when visual elements are created, and they have a StaticResource reference to something in the theme, those resource references get resolved to the resource that was added into Application.Resources. Basically, once the theme processor is done with its job, it is out of the picture, and the normal Silverlight resource lookup just works.

Summary
The thing I like most about this approach is that it allows for themes to be separated from the core application, can be designed independently, and just dropped in into the application, and requires minimal changes to the model to incorporate into the application. Now I just wish Silverlight had implicit styles, so I could also get rid of all the StaticResource references to named styles...

Posted on Sunday, 7/6/2008 @ 11:37 PM | #Silverlight


Comments

33 comments have been posted.

Michael Sync

Posted on 7/7/2008 @ 3:00 AM
Hello,

Can we change the theme on the fly?

Regards,
Michael

Gargamel

Posted on 7/7/2008 @ 4:06 AM
Hi Nikhil,

I think that the theming mechanism should be provided from the framework itself. Why everyone should come up with a different solution of the problem?

And also - we can not create a really customizable controls, right now, because of the lack of dynamic resources. How you can change some part of the control - say the background of the selected State of a a Tab Item, without to create a new control template?

Best,
Valio

Nikhil Kothari

Posted on 7/7/2008 @ 6:39 AM
@Michael
You can't change the theme on the fly per se.
One thought I have is changing the theme is equivalent to restarting the app when a new theme is selected (i.e. reload with the new theme). Suboptimal, but it at least provides the capability... until Silverlight itself has the notion of being able to reset a Style property, as well as resource references that are dynamic. My thought when thinking about this theme system is that it should theoretically continue to work and light up when those capabilities are added in a future SL version.

Thinking some more, the theme system here is about authoring, packaging and loading of theme assets/resources, and not primarily focused on how they are consumed - in fact, the theme system is out of the picture when the visuals loaded and resource references are resolved.

@Gargamel
Everyone shouldn't have to come up with a solution. At some point some things will make it into the framework. In the meantime feel free to leverage the experimentation and prototyping happening in the Silverlight space, if it is solving some problem that isn't addressed in the framework, and you need to address in your own app. Basically, at any given point, there will be some things not addressed out-of-the-box... such is the nature of a platform or framework. Instead they provide the hooks and mechanisms to go beyond what is built-in.

To your specific tab control question, if your tab control provides the specific property you're looking for - such as SelectedTabStyle - your scenario might work.

Michael Washington

Posted on 7/7/2008 @ 8:29 AM
Thank you for this helpful post.

Fallon Massey

Posted on 7/7/2008 @ 4:45 PM
Do you have any samples using Silverlight.FX?

Lots of stuff there, but I didn't see any examples of use.

Thanks, Fallon.

Nikhil Kothari

Posted on 7/7/2008 @ 5:56 PM
@Fallon
Each blog post of mine has an associated sample for download just like this one - for example, the Amazon Search sample from my previous blog posts demonstrated view models, and even earlier I had samples demonstrating autocomplete and other behaviors. I'll recommend checking out my recent Silverlight blogs posts at http://www.nikhilk.net/Category.Silverlight.aspx.

I am certainly contemplating creating a quick little project for this, so I can share all the samples in one download, and keep them updated as the project evolves.

Fallon Massey

Posted on 7/8/2008 @ 4:49 PM
Nikhil, I'm sorry for not being very clear.

I have seen the examples you referenced, however, the framework has grown WAY beyond some of those examples.

I'm interested in some simple examples of how to use the things in your "Glitz" section. Specifically how to use the transitions and easing methods. Do you have any examples of that?

Thanks, Fallon.

Nikhil Kothari

Posted on 7/8/2008 @ 6:09 PM
@Fallon
Glitz is up next for a blog post. I am debating whether to do one post (its likely to turn into a larger post, or whether I should do a couple or so) Also, debating a bit on factoring the framework into multiple assemblies, but not sure as there are pros and cons (how I wish for static compilation)

By the way, its great to hear you're tracking the framework closely! If you have some suggestions, please do send feedback... :-)

chadbr

Posted on 7/9/2008 @ 9:52 PM
I'm really enjoying your blog --

I like the ideas you have here for themes. I can't stand the thought of hard coding them into App.xaml. I would, of course, like to see some of these concepts baked into the core framework.

I'm hoping you'll put your Silverlight.FX project up on CodePlex. Panning any "deep hooks" to S#?

Rich Griffin

Posted on 7/10/2008 @ 10:16 AM
Awesome work...

I am trying to apply a theme to a ListBox but failing. I have followed the steps in your blog and added the listbox to your project however still no love. The error i get is a runtime error complaining that it can't find the style.

Any suggestions on how i might fix this up ??

thanks

<Rich />

Rich Griffin

Posted on 7/10/2008 @ 11:08 AM
Hi, tried a radiobutton as the control and style is simpler than a ListBox at first i got the same error and with a little bit of digging found that i had not added the control to the list of controls in all the themes. I will give the Listbox control another attempt now i know the problem

Nikhil Kothari

Posted on 7/10/2008 @ 4:11 PM
@Chad -
I haven't planned any deep hooks yet with Script# - though that sounds like a fun thought if it results in something useful. I'll see if some natural synergy pops up - the one that is in the back of my mind is around one aspect of a data framework we're building. If you have any ideas, I'd love to hear.
I will likely turn Silverlight.FX into a project over the course of the following weeks.

@Rich
Its also likely in your ListBox scenario, you have styles for both the ListBox, and the ListBoxItem - make sure they're both in a single xaml file, say ListBox.xaml, and make sure ListBox appears in your Includes list within the Theme.

Rich Griffin

Posted on 7/10/2008 @ 4:48 PM
Everything is working now ;-) i had forgot to add all the themes to the includes list. Could the SLFX autogen this for me by reading the files and automatically adding them to the includes list ?

Are you thinking about putting the SLFX up on codeplex in the future ?

thanks

<Rich />

Fallon Massey

Posted on 7/11/2008 @ 12:25 AM
Nikhil, I think this library is starting to get a real following, meaning there are needs going unfulfilled.

Also, I like the way you think, as it mirrors my approach, and makes building applications a heck of a lot easier when you do it right the first time, and build upon a flexible and strong foundation.

Neil Chen

Posted on 7/16/2008 @ 11:18 PM
Hi Nikhil,

Very nice article!
I have improved a little based on your code to support themeing for custom controls:

http://www.cnblogs.com/RChen/archive/2008/07/17/1245146.html

Nikhil Kothari

Posted on 7/17/2008 @ 8:12 AM
@Neil
Since your blog is in Chinese, I only followed bits of what you did by looking at the code. Perhaps you could explain what was improved, and I'd be happy to try and incorporate the changes.

From the code, it seems you extract xml namespaces while parsing themes, and include them as xml namespaces when building the ResourceDictionary. Is that right? As far as my testing went, that wasn't required, as the xml namespaces get defined on the tag where they are used, rather than at the top-level tag...

Neil Chen

Posted on 7/17/2008 @ 10:43 AM
@Nikhil
Thanks very much for your kind response. When I encountered this issue, I just want to make it workable for me, so I made the slight modification.
Althought I can put the namespace declaration in my <Style /> tag, I think it will be better to preserve top-level namespaces when merging them. For our designers might not know this issue, then if I copy & paste theme files created by designers, it may lead to an exception.

Just for your consideration.

Nikhil Kothari

Posted on 7/17/2008 @ 4:28 PM
Sorry, I wasn't clear. I am not proposing that the xmlns be put on the <Style /> tag, as that isn't the norm. What I was saying was that when using the XmlReader to parse the xml, it automatically transfers xml namespaces defined on the root tag to the specific Style tag...

However, it seems that wasn't true when you tried it, so I will re-test, and try to account for that scenario.

Justin-Josef Angel

Posted on 7/21/2008 @ 7:32 PM
Damn.
I didn't even see that one coming.

Replacing the "Application.Resources" at startup before any visual element can resolve the {StaticResource}. Damn.
That's just brilliant. Way to go.

Nikhil Kothari

Posted on 7/28/2008 @ 8:00 AM
@Neil - I will be including support for your scenario/fix in the next iteration of the framework...

Neil Chen

Posted on 7/28/2008 @ 8:40 AM
@Nikhil
Good news, and I'm very eager to see your other great ideas in the next release too.
thanks.

Neil Chen

Posted on 8/4/2008 @ 7:54 PM
hi, Nikhil

The updated theming code in 'Effects and Transitions for Silverlight' still has some problem.
In my case, it throws an exception:
"""
Sys.InvalidOperationExcetion: Invalid XAML for control 'Xaml1'.
[] (line 1, col 133): 'xmlns' prefix is reserved for use by XML.
"""
I had a look at the code, and it seems lack of some text replacements to reduce namespace confiliction. but the code in my post doesn't produce this error.

Nikhil Kothari

Posted on 10/16/2008 @ 1:04 AM
@Neil - you were right - the bug still existed. Finally and hopefully fixed - see the latest framework off of http://www.nikhilk.net/Silverlight-Controls-With-Effects-And-Transitions.aspx.

peak

Posted on 11/15/2008 @ 10:26 PM
Hi, Nikhil
is there a way to change the theme on the fly, not only at the initialization stage.
i see code like this is not allowed:

(XApplication.Current as App).ThemeName = "Red";

Nikhil Kothari

Posted on 12/6/2008 @ 5:39 AM
Styles cannot be set a second time in Silverlight 2. What you can do is tear down the UI, and recreate a new set of visuals...

Miguel Madero

Posted on 12/15/2008 @ 1:42 AM
The SL Toolkit includes the ImplicitStylesManager, which has the benefit of applying styles on the fly and not specifying the style names for each control. Altough I still see some benefits to using this approach, mainly for big applications where it would be a big task to change the controls that are already pointing to a style. The other reasonm would be performance. This approach sets the Resources and then forgets kicks-off, while the ImplictStyleManager can be a bit heavy (in my experience, altought I might have done something wrong)

Adam Kozmic

Posted on 1/30/2009 @ 10:19 AM
Great stuff. I am working on a similar theming scheme, and unfortunately, I hit a brick wall. I merge a bunch of ResourceDictionaries in a very similar fashion to your method, but my local <UserControl.Resources> elements seem to override the Application.Current.Resources. Now, I now that's the general idea, but in my case, I'm never adding those <UserControl.Resources> to the App.Resources unless they are not overridden by my theme xaml. So in essence, the local resources are never added to the project, so long as the theme xaml overrides them. Did you run into a similar issue? Thanks.

Adam Kozmic

Posted on 1/30/2009 @ 10:27 AM
To Clarify: "So in essence, the local resources are never added to the project, so long as the theme xaml overrides them" is what's happening in the app, but the local UserControl.Resources still exist somehow.

For instance, in my User Control:
<sys:Double x:Key="FormButtonHeight">20</sys:Double>

<style x:Key="ExitButton"...... .... .. Height="{StaticResource FormButtonHeight}"...../>

and in my Theme
<sys:Double x:Key="FormButtonHeight">40</sys:Double>

When I run through my theme generator, only the "40" FormButtonHeight is added to the app.resources dictionary.
However, my form button with style="{StaticResource ExitButton}" is still showing up with Height = 20.

Does adding the "ExitButton" style to app.dictionary somehow pull the "FormButtonHeight" along for the ride? This behavior seems very strange to me, because if I dont include the theme generator, then it says "Style 'ExitButton' not found in resources," which is what I expect....weird.

ratheesh

Posted on 4/17/2009 @ 10:42 PM
it is veryuseful for the students and also for the proffesionals.i want to learn more about this new technology

Brian

Posted on 4/22/2009 @ 9:51 AM
I found a pretty big bug with the ImplicitStyleManager: when ApplyMode is set to "Auto", any controls inside of a scrollviewer get loaded twice! This can lead to a serious performance hit. Below is sample code that demonstrates this. Does anybody know what's going on or a workaround? I tried logging a bug on Codeplex but am unable to register...

<UserControl x:Class="Test.TestControl"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:theming="clr-namespace:System.Windows.Controls.Theming;assembly=System.Windows.Controls.Theming.Toolkit"
theming:ImplicitStyleManager.ApplyMode="Auto"
theming:ImplicitStyleManager.ResourceDictionaryUri="Test;component/themes/Theme.xaml">
<ScrollViewer MaxHeight="445" VerticalScrollBarVisibility="Auto" >
<TextBox Loaded="DatePicker_Loaded"/>
</ScrollViewer>
</UserControl>

Partial Public Class TestControl
Inherits UserControl

Public Sub New()
InitializeComponent()
End Sub

'This event will get fired TWICE!!
Private Sub DatePicker_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
'Do Something
End Sub
End Class

Xaml Templates

Posted on 4/23/2009 @ 7:02 AM
Hi, at http://www.xamltemplates.net//sl you can see a complete theme for all Silverlight controls, check it out.

Ryan

Posted on 5/4/2009 @ 7:50 AM
Where do I find the Silverlight.FX .dll?

Prasad

Posted on 12/22/2009 @ 5:27 AM
Hello Nikhil,

I am using Silverlight 4 toolkit.Basically i wanted to apply themes dynamically at runtime using ImplicitStyleManager to the datagrid.but as you may be knowing that support for ImplicitStyleManager is present for Silverlight 2 and 3.In SL 4 support for ImplicitStyleManager is removed ,it supports implicit styles natively.

I want to change theme on selection change event......

void themes_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Uri uri = new Uri(@"ThemesWithISM;component/System.Windows.Controls.Theming.ShinyBlue.xaml", UriKind.Relative);
ImplicitStyleManager.SetResourceDictionaryUri(LayoutRoot, uri);
ImplicitStyleManager.SetApplyMode(LayoutRoot, ImplicitStylesApplyMode.Auto);
ImplicitStyleManager.Apply(LayoutRoot);

}

But i cannot use this code now as it is not supported in SL4.
can you tell me how can i handle this problem...
Post your comment and continue the discussion.