I have spent a lot of time checking out Silverlight and its features. I have also spent a lot of time talking about it to a lot of people. And a reasonably large amount of the people I have talked to, have asked me about templating and styling and about how it works and the differences. I myself also had a little uphill battle to sort out the terms and get going... So I thought I would write an post about it... Sort of explaining it from my view.
But first a little disclaimer. This entry is NOT for people who have worked with WPF before Silverlight. It is intended for people like me. Those that enter the Silverlight scene from the webdevelopment area. Those that are used to HTML instead of XAML and very little support for modifying layout. So if you are a WPF developer, I would recommend 1 of 2 things. Press the back button and read something else on my blog, or read this entry and add comments if you disagree with me. But if you do disagree, be gentle or I will bury you in HTML tags and start hitting you with CSS...
So, lets start by looking at the definition of "styling" and "templating". Styling is basically like CSS. You can set different properties on elements and store that definition under a name and then re-use it throughout your application. However, it is larger than CSS. You can set more or less ANY property on the object. Anything that can be set in XAML, can be set using a style and then re-used. Then...what is templating. Well, a template defines the actual layout of the control. Not just setting colors and things like that, but really changing the entire look of the control. Styling can make you button pink with the text in Verdana, while templating can make the button round with a tiny star in the middle. (How do I come up with these beatiful designs...?).
These two things can actually, and are often, mashed up into one thing. I said that the style could set more or less any property on the control. Well, the template is actually a property on the control. So inside the style, you can add a template and then re-apply it everytime you apply a style. I will return to this later in my post, but I thought I would just mention it here. There is also a control in the Silverlight Toolkit that is called the "Implicit Style Manager" that can make your style feel a bit more like CSS.It can actually do more than that...but simply put that is what it does... A style is not inherited by child controls. So if I add a style to a container control, the controls inside it does not inherit the styles set. This is a big difference from CSS where styles are inherited and mixed and blended...
So...creting a style. Before you can start to create a style, you have to decide where to put it. In Silverlight, you have a couple of different places to store it. Generalle though, you choose from storing it per application or per user control. Most controls have a Resources property where you can store the style. But as I said, generally you store it in either the Applications resources or in the user control where it makes sense. This depends on if the style will be used only in the control or across the entire application.
App:
[code:c#]
<Application ... >
<Application.Resources>
your style
</Application.Resources>
</Application>
[/code]
User control:
[code:c#]
<UserControl ... >
<UserControl.Resources>
your style
</UserControl.Resources>
...
</UserControl>
[/code]
Your style is "named" by using either x:Key or x:Name. Don't ask me why both are available, but that is the way it works. Each style also has to define what type of control it is associated with. This is done by setting the TargetType attribute to the name of the type, for example "Button". Inside the style element, you can then add Setter elements. A Setter element is a definition of one property to set on the target object. A Setter has a Property property naming the property to set on the target as well as a Value property defining the value to set on the property. If the Value needs to be a complex type that can't be set using a simple string, you can use the standard XAML markup rules and set the Value property by using an inner element.
A simple style for a button:
[code:c#]
<UserControl.Resources>
<Style x:Key="MyStyle" TargetType="Button">
<Setter Property="Background" Value="#FFFFFFFF"></Setter>
</Style>
</UserControl.Resources>
[/code]
The background can also be set using the following syntax if you need to set it to a more complex value:
[code:c#]
<UserControl.Resources>
<Style x:Key="MyStyle" TargetType="Button">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,.5" EndPoint="1,.5">
<GradientBrush.GradientStops>
<GradientStop Color="Black" Offset="0" />
<GradientStop Color="Yellow" Offset="0.5" />
<GradientStop Color="Azure" Offset="1" />
</GradientBrush.GradientStops>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
[/code]
Ok...so now I have a style, but how can I apply it? I found that there is a Style property on my Button, bt if I set the Button's Style property to "MyStyle" the shit hits the fan so to say. The entire application stops working. Well, that's because you don't use styles in that way. The style is stored as a resource and must be used as such. The syntax for using a resource is "{StaticResource MyStyle}". So if you want to use the style on you Button you have to write:
[code:c#]
<Button Style="{StaticResource MyStyle}" Width="100" Height="100" />
[/code]
Wow! That is one ugly button. I know...I know...I'm just here to show you how to do it technically, not how to do it graphically.
Ok, so that was styling... What? Was that it? Yep...that was it. It isn't more complicated than that. But it is very powerful since you can set most properties like that. You could for example create a pre-styled Cancel button byt doing the following and just adding the style to all "Cancel" buttons. It will as you see even set the text of the button to Cancel. If you do set for example the Content on the individual button however, it will override the style.
[code:c#]
<Style x:Name="MyStyle" TargetType="Button">
<Setter Property="Background" Value="White" />
<Setter Property="Width" Value="100" />
<Setter Property="Height" Value="40" />
<Setter Property="Content" Value="Cancel" />
<Setter Property="FontFamily" Value="Verdana" />
<Setter Property="FontSize" Value="18" />
<Setter Property="BorderThickness" Value="2" />
</Style>
[/code]
Ok, I get it. It's simple and powerful...yada, yada... Now what about Templating? That must be a lot harder... Well... Not really! But before I start talking about this, I want to mention that this should be done using Expression Blend. Generally the templates become a bit complex and Blend does a very good job making this simple for us to work with. But, for my simple demos, I will show you the XAML markup directly.
So, to the example. Well, if we take the button from previous demos and want to change it into a round button, I would have to change it's template like the following
[code:c#]
<Button Content="Cancel">
<Button.Template>
<ControlTemplate>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Ellipse Width="100" Height="100" Fill="Aqua" />
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
[/code]
The above template will result in a button that graphically looks like a "big" circle with a color of "Aqua" and then the text "Cancel" in the middle. What? Cancel in the middle? Yes... The brilliant element ContentPresenter is responsible from rendering whatever was put in the buttons Content property. The buttons Content property doesn't have to be something simple and boring like a text. It can be anything you want it to be. So inside the button you can present anything, and due to this you have to use a ContentPresenter to present it.
But, you hard coded the ellipse to be 100x100, that's no good. Very true... You would want to be able to set this from the control. Perhaps not in this case, when the template is inserted into the control, but if we move it out of the control and re-use it, it can't really be hard coded. So enter the TemplateBinding. The TemplateBinding is a specific binding made for that scenario. In the binding you say what property you want to read from the controls markup. In tha case of the button, it would look like the following
[code:c#]
<Button Content="Cancel" Width="150" Height="150">
<Button.Template>
<ControlTemplate>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Ellipse Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Fill="Aqua" />
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
[/code]
As you see, I have now added a TemplateBinding instead of the hard coded values. I have also added a width and a height to the button. This will now cause the ellipse to be 150x150. This can be done with any property. So if I wanted to change the color of the ellipse based on the buttons background I would just add a template binding for that as well
[code:c#]
<Ellipse Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Fill="{TemplateBinding Background}" />
[/code]
Ok... So you get it. I just want to mention a little thing that was in the example that you might or might not have noticed. I use a ControlTemplate for the template. So? Well... There is actually more than one template type available. The other template type is the DataTemplate. It is used when you want to set the layout of for example items in a ListBox. So it templates the way that the data that is being presented is displayed. But for this time I will stay on the ControlTemplate.
So, now you know how to template as well as style controls. What about this mash up... Well, since a style sets properties on the control, couldn't it actually set the template property as well? Yes it can. SO you can embed you template into your style as well, and then re-use both the style and template on different controls. So if we combine everything we've looked at in this post it would look something like the following
[code:c#]
<UserControl.Resources>
<Style TargetType="Button" x:Name="CancelStyle">
<Setter Property="Background" Value="Gray" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Width" Value="100" />
<Setter Property="Height" Value="40" />
<Setter Property="Content" Value="Cancel" />
<Setter Property="FontFamily" Value="Verdana" />
<Setter Property="FontSize" Value="18" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Ellipse Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Fill="{TemplateBinding Background}" />
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Button Style="{StaticResource CancelStyle}" />
</Grid>
[/code]
As you can see, some of the properties as magically just consumed by the ContentPresenter. It automatically looks at the parent control to get some of it's settings. For example, it automatically sets the FontFamily of the text in the content to Verdana and the Foreground to White. Even though it isn't explicit set in the template. Very handy...
So, that was a "quick" run through of the templating and styling support in Silverlight 2.0. There will be more about this in the future. It is a very powerful and nice way to put custom look and feel to an application, without having to build new controls. In this post, the button looses all of it's "state layouts", by that I mean the little variations in layout that the button gets automatially when you for example hover over it or click it. These little "animations" are handled by the Visual State Manager. I will try to get my sh** together and write a post about custom controls. In that, I intend to talk a lot more about the VSM as well as the "parts and states" model. There is just so much to say about styling and templating that I don't know where to begin... So stay tuned for more...