WPF Tutorial – Part 5 : Styles and Control Templates

Frequently, when building a UI, one finds him or herself setting various appearance related properties over and over again. For example, you may want all the Label text in your app to be a Bold Trebuchet 12px font. This is easy to do with CSS in a web app, but not as straight forward with WinForms. WPF recognizes this need and satisfies it with the introduction of the Style element. Let’s see how we would implement the above example in XAML:

<StackPanel>
   <StackPanel.Resources>
      <Style TargetType="{x:Type Label}">
         <Setter Property="FontFamily" Value="Trebuchet" />
         <Setter Property="FontSize" Value="12" />
         <Setter Property="FontWeight" Value="Bold" />
      </Style>
   </StackPanel.Resources>

   <Label>Here is some text.</Label>
   <Label>More text.</Label>
   <Label>The last bit of text.</Label>
</StackPanel>

The basic and most common part of a Style is its Setters. Simply declare the property name and its value and it will be applied. Easy, isn’t it? In the last section, I discussed how DataTemplates could be applied by type or by key. The same applies to styles. In this case, I have scoped the style to the StackPanel; any Label in that panel will pick up the style. I can, of course, override that style on the individual elements if I need. You can also inherit styles one from another by setting the BasedOn attribute of the style element:

<StackPanel>
  <StackPanel.Resources>
    <Style x:Key="baseStyle" TargetType="{x:Type Control}">
      <Setter Property="FontFamily" Value="Trebuchet" />
      <Setter Property="FontSize" Value="12" />
      <Setter Property="FontWeight" Value="Bold" />
    </Style>
    <Style BasedOn="{StaticResource baseStyle}" TargetType="{x:Type Label}">
      <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
          <Setter Property="Foreground" Value="Red" />
        </Trigger>
      </Style.Triggers>
    </Style>
  </StackPanel.Resources>

  <Label>Here is some text.</Label>
  <Label>More text.</Label>
  <Label>The last bit of text.</Label>
</StackPanel>

Notice that I now have two styles; one that applies to all elements inherited from Control, and a second one based on that Style that extends it – the second Style applies only to Labels. I have taken this opportunity to show the second most common element you will find in a Style: its Triggers collection. A Trigger is a stateful aspect of the Style. In this case, any of the Labels in the StackPanel will appear red only when the mouse is hovering over them.

By taking advantage of styles, you can create a consistent and easily maintainable appearance for your UI. There is a lot of power in this concept. However, sometimes you need to go further. Suppose you want all of the buttons in your application to have a gel appearance. You cannot accomplish this with only basic style setters. In this case you need to use a ControlTemplate. In order to understand what a ControlTemplate is, you need to think of controls more abstractly. In WPF, a Button control is something akin to the platonic idea of a Button. In your development experience you have seen many buttons of different shapes and styles, but they were all buttons. They typically all had some mouse over effect and triggered something when they were clicked. With WPF, all of the buttons you have seen could be implemented with the same Button control, but with different ControlTemplates. WPF, by way of ControlTemplates, allows you to switch out the appearance of any control, without having to write any code related to its functionality. Some controls have simple templates and some are complex. Here is a basic gel button template straight out of the SDK:

<StackPanel>
  <StackPanel.Resources>
    <Style TargetType="{x:Type Button}">
      <Setter Property="Foreground" Value="white" />
      <Setter Property="Margin" Value="1" />
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type Button}">
            <Grid>
              <Rectangle x:Name="GelBackground" 
                Opacity="1" RadiusX="9" 
                RadiusY="9" 
                Fill="{TemplateBinding Background}" 
                StrokeThickness="0.35">
                <Rectangle.Stroke>
                  <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                    <GradientStop Color="White" Offset="0" />
                    <GradientStop Color="#666666" Offset="1" />
                  </LinearGradientBrush>
                </Rectangle.Stroke>
              </Rectangle>
              <Rectangle x:Name="GelShine" 
                Margin="2,2,2,0" 
                VerticalAlignment="Top" 
                RadiusX="6" 
                RadiusY="6" 
                Opacity="1" 
                Stroke="Transparent" 
                Height="15px">
                <Rectangle.Fill>
                  <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                    <GradientStop Color="#ccffffff" Offset="0"/>
                    <GradientStop Color="Transparent" Offset="1"/>
                  </LinearGradientBrush>
                </Rectangle.Fill>
              </Rectangle>
              <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
            </Grid>
            <ControlTemplate.Triggers>
              <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Fill" TargetName="GelBackground">
                  <Setter.Value>
                    <RadialGradientBrush>
                      <GradientStop Color="Lime" Offset="0" />
                      <GradientStop Color="DarkGreen" Offset="1" />
                    </RadialGradientBrush>
                  </Setter.Value>
                </Setter>
              </Trigger>
              <Trigger Property="IsPressed" Value="true">
                <Setter Property="Fill" TargetName="GelBackground">
                  <Setter.Value>
                    <RadialGradientBrush>
                      <GradientStop Color="#ffcc00" Offset="0"/>
                      <GradientStop Color="#cc9900" Offset="1"/>
                    </RadialGradientBrush>
                  </Setter.Value>
                </Setter>
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
      <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
          <Setter Property="Foreground" Value="Black"/>
        </Trigger>
        <Trigger Property="IsPressed" Value="True">
          <Setter Property="Foreground" Value="Black"/>
        </Trigger>
      </Style.Triggers>
    </Style>
  </StackPanel.Resources>

  <Button Height="35" Width="125" Background="Black">Normal</Button>
  <Button Height="35" Width="125" Background="Black">Mouse Over</Button> 
</StackPanel>

Figure 10: A templated button

A templated button.

This ControlTemplate is embedded within a Style, but it does not have to be done this way. If you examine the ControlTemplate element, you will see where the look of the Button is being defined. In this case it is simply a layering of rectangles with various gradients, but you have access to the full power of WPF in defining these templates (yes that includes 3D and animation if you so desire). Similar to Styles, ControlTemplates can be assigned by type or key, but be careful that whatever template you are applying matches the control type you are applying it to. Every Control expects to find certain pieces in its Template in order to make the control work right. You should study a working Template of a Control before attempting to create your own. One of the best resources for learning about control templates is Simple Styles. Here you will find basic ControlTemplates for all of the major controls in WPF. They are accessible through the sidebar in Kaxaml (a tool you should try out anyway) and can also now be found in Blend. You can also take advantage of control templating support in your own custom controls. A discussion of this is beyond the scope of this article.

Advertisements

One thought on “WPF Tutorial – Part 5 : Styles and Control Templates

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s