Partager via


Styling and Templating

Windows Presentation Foundation (WPF) styling and templating refer to a suite of features (styles, templates, triggers, and storyboards) that allow an application, document, or user interface (UI) designer to create visually compelling applications and to standardize on a particular look for their product. An author or designer can customize a look extensively on an application-by-application basis, but a strong styling and templating model is necessary to allow maintenance and sharing of a look. Windows Presentation Foundation (WPF) provides that model.

Another feature of the WPF styling model is the separation of presentation and logic. This means that designers can design the appearance of an application using only XAML at the same time that developers write the programming logic.

This topic contains the following sections.

  • Prerequisites
  • Styles Basics
  • Data Templates
  • Control Templates
  • Triggers
  • Themes
  • Related Topics

Prerequisites

This topic discusses the Introduction to Styling and Templating Sample application, which is an application with two TextBlock elements and a ListBox control that is bound to a list of images:

This topic focuses on the styling and templating aspects of the application and does not discuss any data binding concepts. For information about data binding, see Data Binding Overview.

In addition, it is important to understand Resources, which are essentially what enable objects such as Style, ControlTemplate, and DataTemplate to be reusable. For more information on resources, see Resources Overview.

Styles Basics

This section contains the following subsections.

  • Extending Styles
  • The TargetType Property
  • Styles and Resources
  • Setting Styles Programmatically
  • Setter.Value Property as a Binding or DynamicResource
  • Setting Event Handlers Using a Style

You can think of a Style as a convenient way to apply property values. For instance, you may have the following TextBlock elements and you want to change the default look of the text.

<TextBlock>My Pictures</TextBlock>
<TextBlock>Check out my new pictures!</TextBlock>

This image shows the default look:

Styling sample screen shot

An obvious way to do that would be to set the properties such as FontSize and FontFamily on the TextBlock elements directly. However, if you want your TextBlock elements to share the same properties but you don't want to have to make two changes every time you need to make a change, you can create a Style and place it in the Resources section of your XAML file, as shown here:

<Window.Resources>

  ...

  <!--A Style that affects all TextBlocks-->
  <Style TargetType="TextBlock">
    <Setter Property="HorizontalAlignment" Value="Center" />
    <Setter Property="FontFamily" Value="Comic Sans MS"/>
    <Setter Property="FontSize" Value="14"/>
  </Style>

  ...

</Window.Resources>

When you set the TargetType of your style to the TextBlock type without assigning the style with an x:Key, the style gets applied to both of your TextBlock elements. We provide more information about this in The TargetType Property section.

Now the TextBlock elements appear as follows:

Styling sample screen shot

Extending Styles

Perhaps you want your two TextBlock elements to share some similarities, such as the FontFamily and the centered HorizontalAlignment, but you also want the text "My Pictures" to have some additional properties. You can do that using the BasedOn property, as shown here:

<Window.Resources>

  ...

  <!--A Style that extends the previous TextBlock Style-->
  <!--This is a "named style" with an x:Key of TitleText-->
  <Style BasedOn="{StaticResource {x:Type TextBlock}}"
         TargetType="TextBlock"
         x:Key="TitleText">
    <Setter Property="FontSize" Value="26"/>
    <Setter Property="Foreground">
    <Setter.Value>
        <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
          <LinearGradientBrush.GradientStops>
            <GradientStop Offset="0.0" Color="#90DDDD" />
            <GradientStop Offset="1.0" Color="#5BFFFF" />
          </LinearGradientBrush.GradientStops>
        </LinearGradientBrush>
      </Setter.Value>
    </Setter>
  </Style>

  ...

</Window.Resources>

Notice that the above style is given an x:Key. To apply the style, you set the Style property on your TextBlock, as shown here:

<TextBlock Style="{StaticResource TitleText}" Name="textblock1">My Pictures</TextBlock>
<TextBlock>Check out my new pictures!</TextBlock>

This TextBlock style now has a HorizontalAlignment value of Center, a FontFamily value of Comic Sans MS, a FontSize value of 26, and a Foreground value set to the LinearGradientBrush specified above. Notice that we have essentially overridden the FontSize value of the base style. This is because if there is more than one Setter setting the same property in a Style, the Setter that is declared last takes precedence.

The following shows you what the TextBlock elements now look like:

This TitleText style extends the style that has been created for the TextBlock type. You can also extend a style that has an x:Key. For more information about the BasedOn property, see the BasedOn page.

The TargetType Property

As shown in the first example above, setting the TargetType property to TextBlock without assigning the style with an x:Key allows your style to be applied to all TextBlock elements. What actually happens is that doing so implicitly sets the x:Key to {x:Type TextBlock}. This also means that if you give the Style an x:Key value of anything other than {x:Type TextBlock}, the Style would not be applied to all TextBlock elements automatically. Instead, you need to apply the style to the TextBlock elements explicitly.

If your style is in the resources section and you do not set the TargetType property on your style, then you must provide an x:Key. In that case, you must also qualify the properties in your Setter objects with a class name, using the syntax Property="ClassName.Property". For example, instead of setting Property="FontSize", you would need to set Property to "TextBlock.FontSize" or "Control.FontSize".

Also note that many WPF controls consist of a combination of other WPF controls. You want to keep that in mind if you create a style that applies to all controls of a type. For instance, if you create a style that targets the TextBlock type in a Window, the style is applied to all TextBlock controls in the window, even if the TextBlock is part of another control, such as a ListBox.

Styles and Resources

You can use a style on any element that derives from FrameworkElement or FrameworkContentElement. The most common way to declare a style is as a resource inside of the Resources section in a XAML file, as the examples shown above. Because styles are resources, they obey the same scoping rules that apply to all resources, so where you declare it affects where it can be applied. If, for instance, you declare the style in the root element of your application definition XAML file, the style can be used anywhere in your application. If you are creating a navigation application and declare the style in one of the application's XAML files, the style can only be used in that XAML file. For more information on scoping rules for resources, see Resources Overview.

In addition, we provide more information about styles and resources in the What's Next section.

Setting Styles Programmatically

To set the defined TitleText style on a TextBlock named textblock1, do the following:

textblock1.Style = (Style)(this.Resources["TitleText"]);

Note that once a style has been applied it is sealed and cannot be changed. Therefore, if you want to dynamically change a style that has already been applied you must create a new style to replace the existing one. The IsSealed property provides information.

The StyleSelector class allows you to create an object that chooses a style to apply based on custom logic. For an example, see the StyleSelector page.

Setter.Value Property as a Binding or DynamicResource

Note that you can set the Value property of your Setter to a Binding or a DynamicResource extension. For more information, see the Setter.Value page.

Setting Event Handlers Using a Style

So far we've only discussed the use of setters to set property value. You can also set event handlers in a style. For more information, see the EventSetter page.

Data Templates

In this sample application, we have a ListBox control that is bound to a list of photos:

<ListBox ItemsSource="{Binding Source={StaticResource MyPhotos}}"
         Background="Silver" Width="600" Margin="10" SelectedIndex="0"/>

This ListBox currently looks like the following:

Most controls have some type of content, and that content often comes from data you are binding to. In this sample, the data is the list of photos. In WPF, you use a DataTemplate to define the visual representation of data. Basically, what you put into a DataTemplate determines what the data looks like in the rendered application.

In our sample application, each custom Photo object has a Source property of type string that specifies the file path of the image. Currently, the photo objects appear as file paths.

For the photos to appear as images, you create a DataTemplate as a resource:

<Window.Resources>

  ...

  <!--DataTemplate to display Photos as images
      instead of text strings of Paths-->
  <DataTemplate DataType="{x:Type local:Photo}">
    <Border Margin="3">
      <Image Source="{Binding Source}"/>
    </Border>
  </DataTemplate>

  ...

</Window.Resources>

Notice that the DataType property is very similar to the TargetType property of the ControlTemplate. If your DataTemplate is in the resources section, when you specify the DataType property to a type and not assign it an x:Key, the DataTemplate gets applied whenever that type appears. You always have the option to assign the DataTemplate with an x:Key and then set it as a StaticResource for properties that take DataTemplate types, such as the ItemTemplate property or the ContentTemplate property.

Essentially, the DataTemplate in the above example defines that whenever there is a Photo object, it should appear as an Image within a Border. With this DataTemplate, our application now looks like this:

Photo image

The data templating model provides other features. For example, if you are displaying collection data that contains other collections using a HeaderedItemsControl type such as a Menu or a TreeView, there is the HierarchicalDataTemplate. Another data templating feature is the DataTemplateSelector, which allows you to choose a DataTemplate to use based on custom logic. For more information, see Data Templating Overview, which provides a more in-depth discussion of the different data templating features.

Control Templates

This section contains the following subsections.

  • Without Using the ControlTemplate
  • What is a ControlTemplate?
  • Creating a ControlTemplate
  • IsItemsHost Property
  • ItemsPresenter and ContentPresenter
  • TemplateBinding

Now that our photos appear as images, let's display them horizontally instead of vertically; we want to make the ListBox horizontal.

Without Using the ControlTemplate

First, it is important to point out that it is not necessary to use the ControlTemplate to make the ListBox horizontal. A ListBox has an ItemsPanel property that allows you to set an ItemsPanelTemplate, the template that controls the layout of the items of the ListBox. One option is to simply create a ListBox style and set the ItemsPanel property, as in the following example:

<Style TargetType="ListBox">
  <Setter Property="ItemsPanel">
    <Setter.Value>
      <ItemsPanelTemplate>
        <StackPanel Orientation="Horizontal"
                    VerticalAlignment="Center"
                    HorizontalAlignment="Center"/>
      </ItemsPanelTemplate>
    </Setter.Value>
  </Setter>
</Style>

This works just fine and gives us a horizontal ListBox. This example shows that depending on your scenario, there may be options other than to replace the ControlTemplate. For this example, if we want a horizontal ListBox that has additional properties, such as rounded corners, then we need to work with the ControlTemplate of the ListBox.

Before we provide an example to show how to do that, it is important to first explain the concept of a ControlTemplate.

What is a ControlTemplate?

For most controls, there is appearance and behavior. Consider a button: appearance is the raised area that you can press, and the behavior is the Click event that gets raised in response to a click.

Sometimes, there may be a control that provides the behavior that you need but not the appearance that you need. So far, we have shown that you can use style setters to set property values to affect the look of control. However, to change the structure of a control or to set property values on the components that comprise a control, you need to use a ControlTemplate.

In WPF, the ControlTemplate of a control defines the appearance of the control. You can change the structure and appearance of a control by defining a new ControlTemplate for the control. In many cases, this gives you enough flexibility so that you don't have to write your own custom controls. If you do not define your own ControlTemplate for your control, you get the default template that matches the system theme, which is what gives the Button control its default look.

One thing to keep in mind is that once you create a ControlTemplate for you control, you are replacing the entire ControlTemplate. For example, you may define your Button ControlTemplate the following way.

Note that the ContentPresenter element simply marks where the Content of the Button should go. We will discuss the different pieces in a later section.

<Style TargetType="Button">
  <!--Set to true to not get any properties from the themes.-->
  <Setter Property="OverridesDefaultStyle" Value="True"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="Button">
        <Grid>
          <Ellipse Fill="{TemplateBinding Background}"/>
          <ContentPresenter HorizontalAlignment="Center"
                            VerticalAlignment="Center"/>
        </Grid>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

When this gets applied, the Button appears as an Ellipse:

Button ControlTemplate sample

Remember that what the Button looks like when it is in focus or pressed is all part of the default appearance of the button that you are replacing. Therefore, depending on your needs, you may want to put in your definition what your button should look like when it is pressed. For a complete example, see Button ControlTemplate Example.

If you are creating a ControlTemplate, the best way to get started is to use the ControlTemplate Examples. If you really need to look at the parts that a control is composed of, you can take a look at the themes file located in Themes or you can use the Show Visual Tree functionality of XAMLPad, an application that is installed with the Windows Software Development Kit (SDK).

Creating a ControlTemplate

Now let's continue with our example and create a ControlTemplate that defines a ListBox that is horizontal and has rounded corners. To replace the ControlTemplate of a control, set the Template property to the new ControlTemplate.

<Style TargetType="ListBox">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="ListBox">
        <Border CornerRadius="5" Background="{TemplateBinding ListBox.Background}">
          <ScrollViewer HorizontalScrollBarVisibility="Auto">
            <StackPanel Orientation="Horizontal"
                       VerticalAlignment="Center"
                       HorizontalAlignment="Center"
                       IsItemsHost="True"/>
          </ScrollViewer>
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

When you set the Template property this way, it is really no different than setting other control properties using a Style: you are using a Style as a tool to help you set the Template property. This means that another way to set a ControlTemplate is to set the Template property on your control directly. If you do it that way, you would create a ControlTemplate in the Resources section, provide it with an x:Key, and then use it as a static resource. For an example, see the Template property.

As you can see in the above example, the ControlTemplate class has a TargetType property that is similar to the TargetType property of the Style class. However, note that unlike Style and DataTemplate, ControlTemplate objects do not have the notion of an implicit key. In other words, if you have a standalone ControlTemplate with the TargetType property set to a type, the ControlTemplate does not get applied to that type automatically. Also note that the TargetType property is required on a ControlTemplate if the template definition contains a ContentPresenter.

Try experimenting with the ControlTemplate. For example, replace the StackPanel with a WrapPanel, set the HorizontalScrollBarVisibility property of the ScrollViewer to Disabled, and then set the Width of the ListBox to 300. (The WrapPanel only puts items to the next row when the first row runs out of space. If you don't set the HorizontalScrollBarVisibility property of the ScrollViewer to Disabled the first row does not run out of space because you can scroll to the end. As a result, the WrapPanel does not wrap the items.)

IsItemsHost Property

In this example, one important property that must be there is the IsItemsHost property. The IsItemsHost property is used to indicate in the template of an ItemsControl (controls such as ListBox that work with a list of items,) where generated elements should go. Setting the property to true on the StackPanel means that any items added to the ListBox go into the StackPanel. Note that this property only works on Panel types.

ItemsPresenter and ContentPresenter

However, note that when you specify a panel in the ControlTemplate and mark it as the IsItemsHost this way, the ItemsPanel cannot be replaced by the user of the control without using a ControlTemplate. Therefore, only do it that way if you know you wouldn't want the panel to be replaced without the use of a template. Alternatively, you can use the ItemsPresenter element to mark where the items should go and then specify an ItemsPanelTemplate by setting the ItemsPanel property. The ItemsPanelTemplate page has an example that shows you how to do that. For another example that uses the ItemsPresenter, see TreeView ControlTemplate Example.

If you are creating a template for a ContentControl such as a Button, the corresponding element is the ContentPresenter. Similarly, you place this element in the ControlTemplate of your ContentControl type to indicate where the content should be displayed, as demonstrated in the example in the What is a ControlTemplate? section. For other examples, see Label ControlTemplate Example and ListBoxItem ControlTemplate Example.

TemplateBinding

Another important thing to notice in the previous example is the Background value that is set to {TemplateBinding ListBox.Background}. This is simply indicating that the Background of the Border should be synchronized with the Background value that is set on the ListBox. A TemplatBinding is similar to a Binding. In fact, a TemplatBinding is more efficient but less functional than a Binding; using a TemplatBinding is equivalent to using a Binding with the Source property set to RelativeSource.TemplatedParent.

You use TemplateBinding in your ControlTemplate when you want to give the user of your control the control over the values of certain properties. TemplateBinding is a markup extension that is represented by TemplateBindingExtension class.

You may have noticed that DataTemplate and ControlTemplate are similar in that their content becomes the visualization of an object. With the ListBox ControlTemplate definition, our application now looks like the following:

Styling sample screen shot

Triggers

This section contains the following subsections.

  • Property Triggers
  • EventTriggers and Storyboards
  • MultiTriggers, DataTriggers and MultiDataTriggers

Style, ControlTemplate, DataTemplate all have a Triggers property that can contain a set of triggers. A trigger sets properties or starts actions such as animation when a property value changes or when an event is raised. This topic introduces the different types of triggers.

Property Triggers

To demonstrate how to use triggers to set properties, let's make each ListBoxItem partially transparent unless it is selected.

The following style sets the Opacity value of a ListBoxItem to 0.5. When the IsSelected property is true, however, the Opacity is set to 1.0:

<Style TargetType="ListBoxItem">
  <Setter Property="Opacity" Value="0.5" />
  <Setter Property="MaxHeight" Value="75" />
  <Style.Triggers>
    <Trigger Property="IsSelected" Value="True">
        <Setter Property="Opacity" Value="1.0" />
    </Trigger>

...

  </Style.Triggers>
</Style>

This example uses a Trigger to set a property value, but note that the Trigger class also has the EnterActions and ExitActions properties that enable a trigger to perform actions.

Notice that we have also set the MaxHeight property of the ListBoxItem to 75. In the following screenshot, the third item is the selected item:

EventTriggers and Storyboards

We just showed that a Trigger sets property values or starts actions based on the value of a property. Another type of trigger is the EventTrigger, which starts a set of actions based on the occurrence of an event. For example, the following EventTrigger objects specify that when the mouse pointer enters the ListBoxItem, the MaxHeight property animates to a value of 90 over a 0.2 second period. When the mouse moves away from the item, the property returns to the original value over a period of 1 second. Note how it is not necessary to specify a To value for the MouseLeave animation. This is because the animation is able to keep track of the original value.

<EventTrigger RoutedEvent="Mouse.MouseEnter">
  <EventTrigger.Actions>
    <BeginStoryboard>
      <Storyboard>
        <DoubleAnimation
          Duration="0:0:0.2"
          Storyboard.TargetProperty="MaxHeight"
          To="90"  />
      </Storyboard>
    </BeginStoryboard>
  </EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
  <EventTrigger.Actions>
    <BeginStoryboard>
      <Storyboard>
        <DoubleAnimation
          Duration="0:0:1"
          Storyboard.TargetProperty="MaxHeight"  />
      </Storyboard>
    </BeginStoryboard>
  </EventTrigger.Actions>
</EventTrigger>

For more information, see the Storyboards Overview.

In the following screenshot, the mouse is pointing to the third item:

Styling sample screen shot

MultiTriggers, DataTriggers and MultiDataTriggers

In addition to Trigger and EventTrigger, there are other types of triggers. MultiTrigger allows you to set property values based on multiple conditions. You use DataTrigger and MultiDataTrigger when the property of your condition is data-bound.

For the complete sample we discussed in this topic, see Introduction to Styling and Templating Sample.

Themes

A typical Windows Presentation Foundation (WPF) application might have multiple user interface (UI) resources that are applied throughout the application. Collectively, this set of resources can be considered the theme for the application. Windows Presentation Foundation (WPF) provides support for packaging user interface (UI) resources as a theme by using a resource dictionary that is encapsulated as the ResourceDictionary class.

Windows Presentation Foundation (WPF) themes are defined using the styling and templating mechanism that Windows Presentation Foundation (WPF) exposes for customizing the visuals of any element.

Windows Presentation Foundation (WPF) theme resources are stored in embedded resource dictionaries. These resource dictionaries must be embedded within a signed assembly, and can either be embedded in the same assembly as the code itself or in a side-by-side assembly. In the case of PresentationFramework.dll, the assembly which contains Windows Presentation Foundation (WPF) controls, theme resources are in a series of side-by-side assemblies.

The theme becomes the last-stop place to look when searching for the style of an element. Typically, the search will begin by walking up the element tree searching for an appropriate resource, then look in the application resource collection and finally query the system. This gives application authors a chance to redefine the style for any object at the tree or application level before reaching the theme.

You can define resource dictionaries as individual files that enable you to reuse a theme across multiple applications. You can also create swappable themes by defining multiple resource dictionaries that provide the same types of resources but with different values. Redefining these styles or other resources at the application level is the recommended approach for skinning an application.

To share a set of resources, including styles and templates, across applications, you can create a XAML file and define a ResourceDictionary. For example, take a look at the following screenshot that shows part of the Styling with ControlTemplates Sample:

If you look at the XAML files in the sample, you will notice that the files all have the following:

<ResourceDictionary.MergedDictionaries>
  <ResourceDictionary Source="Shared.xaml" />
</ResourceDictionary.MergedDictionaries>

It is the sharing of shared.xaml, which defines a ResourceDictionary that contains a set of style and brush resources that enables the controls in the sample to have a consistent look.

For another example, see Themed Application Sample. For more information, see Merged Resource Dictionaries.

If you are creating a theme for you custom control, take a look at the External Control Library section of the Control Authoring Overview.

See Also

Other Resources

Themes
Photo Store Demo