Welcome to Day #28. We’ve nearly finished the 31 Days of Silverlight, but we have a few fun posts remaining. Today’s post is on creating Themes in Silverlight.
Theming is one of those pieces of functionality you see in consumer apps quite a bit. Basically changing the application to look and feel the way I want it. However, it’s definitely more than that. It’s the way you should write your styling information in Silverlight. For the most part, it’s advanced Styling, which we covered on Day #10: Styling A Silverlight Control. The difference is that we are creating external style files that we can then swap between inside the application.
1. Download the Silverlight 3 Toolkit
This article will cover the advanced features that Theming offers you, and assumes that you are somewhat familiar with styling. First, you need to download the Silverlight 3 Toolkit. If you haven’t done this already, head over to http://silverlight.codeplex.com and download it.
2. Add references to two assemblies
We need to add two references to our Silverlight project. They are System.Windows.Controls, and System.Windows.Controls.Theming (this one was added by the Toolkit). Go ahead and add them to your project.
3. Add an XML namespace reference to the Theming assembly
The next thing, as usual, is to add an XML namespace reference. To do this, I recommend relying on Intellisense for it, and just start typing the xmlns:theming=” before it recommends a list of assemblies. If you don’t want to type it, however, you can just use my snippet of code:
xmlns:theming="clr-namespace:System.Windows.Controls.Theming;assembly=System.Windows.Controls.Theming.Toolkit"
4. Add some controls to your page
Before we can start styling our application, we need some controls on our page. I’ve added a TextBlock, a Button, a TextBox, and a Slider to my StackPanel base control. Here’s what my full XAML looks like right now:
<UserControl x:Class="SilverlightTheming.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:theming="clr-namespace:System.Windows.Controls.Theming;assembly=System.Windows.Controls.Theming.Toolkit"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<StackPanel x:Name="LayoutRoot" Width="200" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="This is our form." Margin="0,0,0,10"/>
<Button Content="Save" Margin="0,0,0,10"/>
<TextBox Text="Something to edit." Margin="0,0,0,10"/>
<Slider/>
</StackPanel>
</UserControl>
5. Creating a ResourceDictionary
Our next step is going to be creating a ResourceDictionary file. If you choose “Add New Item…”, one of your choices is “Silverlight Resource Dictionary.” Even though it creates a XAML file (like nearly everything else in Silverlight), this will have a base tag of ResourceDictionary instead of UserControl. This is where we will define our theme. You’ll notice I named my file Green.xaml. This is so that I can remember that this is the green theme. Eventually, when you lather, rinse, and repeat, you’ll have Blue, Red, Black, White, Pink, etc. Effective naming is always useful. I never recommend going with names like “Professional,” “Glass,” and “Techno,” because it’s harder to remember what those look like just by reading the name.
One important note before you move on from this point: By default, your new resource dictionary will have a “Build Action” of “Page” in its properties. You need this to be “Content” instead. If you don’t change this, you will get an error similar to: AG_E_PARSER_BAD_PROPERTY_VALUE. Just change it. It will make your life easier than mine just was.
Editing our new ResourceDictionary
This is where the work comes in. For theming an entire application, we really need to consider the styles for every possible control we might use. In my example, I know I’m only going to be using 4 controls: TextBlock, TextBox, Button, and Slider. But as your application grows, you can see how this would grow quickly as well. (If you don’t want the theme to apply to everything, your life gets easier. This effort only applies to those controls you actually want to change visually.) To make my life easier, I have pre-defined some colors and brushes that I will be using in my theme. This way, if I ever want to change them in the future, I don’t have to change them in 19 different places. Here’s the entirety of my Blue.xaml file…don’t be intimidated. It’s big, but no worse than any of the stylesheets you’ve ever created. 🙂
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!--DEFINING A LIST OF COLORS FOR RE-USE-->
<Color x:Key="TextBrush">#FF000000</Color>
<Color x:Key="NormalBrushGradient1">#FFBAE4FF</Color>
<Color x:Key="NormalBrushGradient2">#FF398FDF</Color>
<Color x:Key="NormalBrushGradient3">#FF006DD4</Color>
<Color x:Key="NormalBrushGradient4">#FF0A3E69</Color>
<Color x:Key="NormalBorderBrushGradient1">#FFBBBBBB</Color>
<Color x:Key="NormalBorderBrushGradient2">#FF737373</Color>
<Color x:Key="NormalBorderBrushGradient3">#FF646464</Color>
<Color x:Key="NormalBorderBrushGradient4">#FF000000</Color>
<Color x:Key="ShadeBrushGradient1">#FF62676A</Color>
<Color x:Key="ShadeBrushGradient2">#FFD1D4D6</Color>
<Color x:Key="ShadeBrushGradient3">#FFFFFFFF</Color>
<Color x:Key="SliderBorderGradient1">#FF3F3F3F</Color>
<Color x:Key="SliderBorderGradient2">#FFADADAD</Color>
<!--DEFINING A LIST OF BRUSHES FOR RE-USE-->
<LinearGradientBrush x:Key="NormalBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="{StaticResource NormalBrushGradient1}" Offset="0" />
<GradientStop Color="{StaticResource NormalBrushGradient2}" Offset="0.41800001263618469" />
<GradientStop Color="{StaticResource NormalBrushGradient3}" Offset="0.418" />
<GradientStop Color="{StaticResource NormalBrushGradient4}" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="NormalBorderBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="{StaticResource NormalBorderBrushGradient1}" />
<GradientStop Color="{StaticResource NormalBorderBrushGradient2}" Offset="0.38" />
<GradientStop Color="{StaticResource NormalBorderBrushGradient3}" Offset="0.384" />
<GradientStop Color="{StaticResource NormalBorderBrushGradient4}" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="ShadeBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="{StaticResource ShadeBrushGradient2}" Offset="0" />
<GradientStop Color="{StaticResource ShadeBrushGradient3}" Offset="0.1" />
<GradientStop Color="{StaticResource ShadeBrushGradient3}" Offset="1" />
</LinearGradientBrush>
<!--Button-->
<Style TargetType="Button">
<Setter Property="Background" Value="{StaticResource NormalBrush}"/>
<Setter Property="Foreground" Value="#FF000000"/>
<Setter Property="Padding" Value="3"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="BorderBrush" Value="{StaticResource NormalBorderBrush}" />
</Style>
<!--TextBox-->
<Style TargetType="TextBox">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Background" Value="{StaticResource NormalBrushGradient1}"/>
<Setter Property="Foreground" Value="#FF000000"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="BorderBrush" Value="{StaticResource NormalBorderBrush}" />
</Style>
<!--Slider-->
<Style TargetType="Slider">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Maximum" Value="10"/>
<Setter Property="Minimum" Value="0"/>
<Setter Property="Value" Value="0"/>
<Setter Property="BorderBrush">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFA3AEB9" Offset="0"/>
<GradientStop Color="#FF8399A9" Offset="0.375"/>
<GradientStop Color="#FF718597" Offset="0.375"/>
<GradientStop Color="#FF617584" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
6. Using the ImplicitStyleManager
That last step was the hard part. Defining styles for your controls, picking colors, and getting all of that XAML created. The ImplicitStyleManager makes the rest of our life easy. We can use it two different ways, and I’ll show you each. The first is the simple way: in XAML. To our “LayoutRoot” element, we just need to apply two properties:
<StackPanel Name="LayoutRoot" theming:ImplicitStyleManager.ApplyMode="Auto" theming:ImplicitStyleManager.ResourceDictionaryUri="Blue.xaml" HorizontalAlignment="Center" VerticalAlignment="Center">
Obviously, you need to make sure that where I put “Blue.xaml,” that you put the location of your ResourceDictionary. After that, run your application. If you used my style information, you should have slightly different looking button and slider controls, and a textbox that has a blue background. Each and every textbox in my application will look like that, unless I explicitly override that styling.
But Jeff, can I call this from code?
Why yes, you can! The other way to invote the ImplicitStyleManager is to just create a reference to it in code. Keep in mind that to do this, you’ll need a using statement for the System.Windows.Controls.Theming assembly, but otherwise, here’s the code:
Uri theme = new Uri("Blue.xaml", UriKind.Relative);
ImplicitStyleManager.SetResourceDictionaryUri(LayoutRoot, theme);
ImplicitStyleManager.SetApplyMode(LayoutRoot, ImplicitStylesApplyMode.Auto);
ImplicitStyleManager.Apply(LayoutRoot);
If you’re planning on giving your user the ability to change their theme on the fly, I’d recommend going the code route. You can add that code to an event handler, and just choose the appropriate Dictionary as the user selected it. So, that’s it! Pretty simple, huh?
Summary
I highly recommend Theming your application, regardless of whether you plan to allow your user to choose a theme or not. By externalizing all of your styling information, you create a familiar feel to traditional web development with CSS. In addition, you could technically create a seperate dictionary for Buttons, TextBlocks, etc. You’d then pull of those together in yet another master dictionary, keeping the code seperated and clean. Go forth and theme!
To download my application sample for Silverlight Theming, click here.
Leave a Reply