Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Silverlight Dynamic Themes

0.00/5 (No votes)
6 Jan 2012 1  
This article describes how to dynamically apply themes to a Silverlight application.

Contents

Introduction

In this article, we'll see how to create a truly dynamic Silverlight themeable application. In order to be able to build the companion project, you'll have to meet the following requirements:

  1. Microsoft Visual Studio 2010 installed.
  2. Silverlight Toolkit from the CodePlex website.
  3. Latest Silverlight 4 themes (JetPack, Accent Color, Windows 7, and Cosmopolitan) from Microsoft Downloads.

We will test dynamic themes on a page like in the screenshot below:

How to dynamically change themes

We want to use the already existing toolkit themes to let users easily change the application look and feel. There are two ways of using existing toolkit themes:

  1. Reference the built-in themes
  2. Embed raw themes into the project

1. Reference the built-in themes

This technique has the following important advantage: you'll only need to reference the theme assemblies and controls (Core, SDK, or Silverlight Toolkit) you are using. Different controls come in different assemblies, but you are not forced to load them all - only the ones you are using. This results in a smaller application and easier to navigate project. The drawback is that there is no way to modify built-in themes. If you intend to create a custom control or you want to adjust the look of an existing one, you can't. Or, you can by overriding a specific control theme with an explicit style - not a dynamic way anymore.

There are many articles on the web describing how to use the built-in themes. We will not stop on this here, but we'll skip to the second technique.

2. Embed raw themes into the project

Using this approach will bring us full control over existing Silverlight themes. To accomplish this, we'll have to add raw Silverlight themes to the project. The main problem with this method is that you'll have to reference assemblies containing controls present in the embedded raw themes. For small applications, this can be unacceptable, but for larger ones, most of the control assemblies are already referenced and no overload will be added.

New Silverlight 4 themes come in a nice structure, with six self-explanatory files per theme:

  1. Brushes.xaml (theme brush definitions)
  2. Fonts.xaml (theme font definitions)
  3. CoreStyles.xaml (core Silverlight controls like Button, TextBox, ComboBox, ListBox ...)
  4. SDKStyles.xaml (TabControl, TreeView, DataGrid, DatePicker ...)
  5. ToolkitStyles.xaml (Accordion, ContextMenu, Chart, BusyIndicator ...)
  6. Styles.xaml (styles used for single theme application layout)

Additionally, we have added CustomStyles.xaml at the same theme level and use it to define styles for our custom controls (in this article, just a page background control).

For each theme, there is a container (a MergedDictionaries) with the above resources embedded as ResourceDictionarys. Below is a snapshot of the AccentColor.xaml content:

<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/SLDynamicThemes2;component/Assets/Themes/AccentColor/CoreStyles.xaml" />
    <ResourceDictionary Source="/SLDynamicThemes2;component/Assets/Themes/AccentColor/SDKStyles.xaml" />
    <ResourceDictionary Source="/SLDynamicThemes2;component/Assets/Themes/AccentColor/ToolkitStyles.xaml" />
    <ResourceDictionary Source="/SLDynamicThemes2;component/Assets/Themes/AccentColor/Styles.xaml" />
    <ResourceDictionary Source="/SLDynamicThemes2;component/Assets/Themes/AccentColor/CustomStyles.xaml" />
</ResourceDictionary.MergedDictionaries>

To dynamically change themes at run-time, we will make use of the toolkit Theme control. There are two options that can be chosen, depending on the application's requirements. First one - easiest and straightforward - was suggested by Michael Epner in article comments and is applicable when the whole application theme is supposed to be changed. It is also more elegant because there is no need to use the ChildWindow workaround initially presented as the only way to theme child windows. The second approach - original article - has to be used when partial application theming is required (i.e., not the whole application, but only part(s) of it). Below you can find both options.

2.a. Full application theming

The easiest solution is to make use of the Theme.SetApplicationThemeUri static function that will take care of changing the whole application theme, without the need to apply additional workarounds. There is no need to include the Theme control to the MainPage layout root or to create any custom ChildWindow to enable child window theming. Just call Theme.SetApplicationThemeUri and the new look will be instantly visible.

void ThemeButton_Click(object sender, RoutedEventArgs e)
{
    Button button = sender as Button;
    if (button.Tag != null)
    {
    string themeName = button.Tag.ToString();
    if (!String.IsNullOrEmpty(themeName))
    {
        Uri themeUri = new Uri(string.Format(@"/SLDynamicThemes2;component/" + 
                               s@"Assets/Themes/{0}", themeName), UriKind.Relative);
        Theme.SetApplicationThemeUri(App.Current, themeUri);
    }
    }
}

2.b. Partial application theming

To dynamically change themes at run-time, we will make use of the toolkit Theme control and add it to the page(s) you want to apply Silverlight themes to. In our sample, the Theme control is added only to MainPage.xaml, enclosing the entire application layout within its borders.

<toolkit:Theme x:Name="ThemeContainer" ThemeUri="/SLDynamicThemes2;component/Assets/Themes/JetPack.xaml">
    <controls:PageBackground HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
        <toolkit:DockPanel LastChildFill="True">
            <StackPanel Orientation ="Horizontal" toolkit:DockPanel.Dock="Top"></StackPanel>
            <Grid>
                <application:Home x:Name="HomePage"/>
            </Grid>
        </toolkit:DockPanel>
    </controls:PageBackground>
</toolkit:Theme>

The only thing we have to do in order to modify the theme is to change the ThemeContainer’s ThemeUri property to the desired one in MainPage.cs.

void ThemeButton_Click(object sender, RoutedEventArgs e)
{
    Button button = sender as Button;
    if (button.Tag != null)
    {
        string themeName = button.Tag.ToString();
        if (!String.IsNullOrEmpty(themeName))
        {
            Uri themeUri = new Uri(string.Format(@"/SLDynamicThemes2;" + 
                 @"component/Assets/Themes/{0}", themeName), UriKind.Relative);
            ThemeContainer.ThemeUri = currentThemeUri = themeUri;
        }
    }
}

The four buttons on the top of the main page (see first screenshot) define the Tag property that identifies the theme to be applied. When clicked, the ThemeContainer’s ThemeUri will change accordingly and the new theme will be applied to the whole application.

Dynamic themes in ChildWindow

Before explaining everything about theming ChildWindow controls, I have to include here an observation of Liviu Catrina: right now, only the content of a ChildWindow can be themed, not the child window appearance itself. There is a limitation in Silverlight Toolkit that prevents ChildWindow control theming (even if raw themes contain the style for child windows). You can read a short explanation provided by Justin Angel (one of the toolkit creators) on the official Silverlight forums.

If you follow the first approach described above - 2.a. Full application theming, there is no need for further ChildWindow tweaking. Theme changes will be automatically applied to any child window the application is containing. For the second implementation - 2.b. Partial application theming, we will have to prepare our child windows in order to make them themeable.

Even if we carefully enclose the application content inside the Theme control, the ChildWindows will still remain outside it and changing a theme will not be reflected in them. Next, we will present a solution that ensures that themes are applied also on a ChildWindow (see the screenshot below):

ChildWindow themes can be enabled by creating a custom class (CustomChildWindow in our sample) where we will override the OnApplyTemplate() function. We will expect that all child windows derived from CustomChildWindow will have a a Theme control named ThemeContainer. The idea is to remember the current ThemeUri of your application and use it when any child window is displayed.

public class CustomChildWindow : ChildWindow
{
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        Theme themeContainer = (Theme)(this).FindName("ThemeContainer");
        if (themeContainer != null)
        {
            App app = (App)App.Current;
            MainPage mainPage = app.RootVisual as MainPage;
            themeContainer.ThemeUri = mainPage.CurrentThemeUri;
        }
    }
}

When the newly created ChildWindow shows up, we will check and apply the CurrentThemeUri saved in the main page. Below you can see the XAML code snippet of the child window we are using.

<toolkit:Theme x:Name="ThemeContainer">
    <Grid x:Name="LayoutRoot" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
        <appControls:PageBackground HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
            <StackPanel HorizontalAlignment="Center"  VerticalAlignment="Center">
                <TextBlock Name="ThemeNameTextBlock" Loaded="ThemeNameTextBlock_Loaded"></TextBlock>
            </StackPanel>
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Bottom">
                <Button Name="OK" Click="Button_Click" Margin="0,0,10,0">OK</Button>
                <Button Name="Cancel" Click="Button_Click" Margin="0,0,10,0">Cancel</Button>
            </StackPanel>
        </appControls:PageBackground>
    </Grid>
</toolkit:Theme>

The ThemeContainer control encloses everything in our child window and any child control will inherit the look and feel of the current application theme.

Conclusion

In this article, we detailed a method you can use to enable dynamic themes in your Silverlight applications. It gives you full control on existing themes and lets you extend them with your own custom controls.

I want to say thank you to everyone for their pertinent suggestions and observations. Your help improved the original document and made it better.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here