Using Themes with Custom Controls

Last month I explained how to use shared resources in a control library. This month I’ll create an example that uses different styles based on the current setting of the desktop theme.

You can define a different look and feel based on the desktop theme by adding a resource dictionary to the Themes folder. The desktop theme determine which resource dictionary is used. Here is a list the resource dictionary file names and the desktop themes:

Classic.xaml – “Classic” Windows 9x/2000 look on Windows XP.

Luna.NormalColor.xaml – Default blue theme on Windows XP.

Luna.Homestead.xaml – Olive theme on Windows XP.

Luna.Metallic.xaml – Silver theme on Windows XP.

Royale.NormalColor.xaml – Default theme on Windows XP Media Center Edition.

Aero.NormalColor.xaml – Default theme on Windows Vista

In my example, I created a simple custom control that uses various resources. I’ve defined each resource in three resource dictionaries: generic.xaml, Luna.NormalColor.xaml (for the default theme on Windows XP), and Aero.NormalColor.xaml (for the default theme on Windows Vista). The key for the styles are identical in each resource file. For example, in generic.xaml, I defined a style for the StackPanel, which my custom control uses:

  <Style TargetType="StackPanel"

         x:Key="{ComponentResourceKey

                   TypeInTargetAssembly={x:Type local:ShapeResizer},

                   ResourceId={x:Type StackPanel}}">

    <Setter Property="Width" Value="250"/>

    <Setter Property="Height" Value="200"/>

  </Style>

When the user has chosen the default theme on Windows XP, I want the StackPanel to be blue, so I defined the following style in Luna.NormalColor.xaml.

  <Style TargetType="StackPanel"

         x:Key="{ComponentResourceKey

                  TypeInTargetAssembly={x:Type local:ShapeResizer},

                  ResourceId={x:Type StackPanel}}">

    <Setter Property="Width" Value="250"/>

    <Setter Property="Height" Value="200"/>

    <Setter Property="Background" Value="DodgerBlue"/>

  </Style>

Notice that the keys for both styles are identical and are ComponentResourceKey objects. As stated in Defining and Using Shared Resources in a Custom Control Library, styles defined at the theme level must create a ComponentResouceKey for the key.

In my user control, I can reference this style and the correct style will be used depending on the current desktop them in use.

  <!--In my custom control.-->

  <StackPanel Style="{DynamicResource {ComponentResourceKey

                      TypeInTargetAssembly={x:Type local:ShapeResizer},

                      ResourceId={x:Type StackPanel}}}">

  </StackPanel>

Notice that I used the style as a DynamicResource. By doing so, the appearance of the StackPanel will change when the user switches between the default theme for Windows XP and another theme. If I used the style as a StaticResource, the appearance wouldn’t change unless I exited and restarted my application.

Finally, I need to specify the location of the theme resource files by using the ThemeInfoAttribute.

// Specifies the location in which theme dictionaries are stored for types in an assembly.

[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly,

                     ResourceDictionaryLocation.SourceAssembly)]

Attached is the complete example. Run the application and experiment with switching between the default theme on Window XP or Vista and another theme.

You can see the themes files shipped with WPF here.

ThemedResources.zip