Up to this point, you've seen the core WPF building blocks for developing applications. You use the application model to host and deliver application content, which consists mainly of controls. To simplify the arrangement of controls in a UI, and to ensure the arrangement is maintained in the face of changes to window size and display settings, you use the WPF layout system. Because most applications allow users to interact with data, you use data binding to reduce the work of integrating your UI with data. To enhance the visual appearance of your application, you use the comprehensive range of graphics, animation, and media support provided by WPF. Finally, if your application operates over text and documents, you can use the WPF text, typography, document, annotation, packaging, and printing capabilities.
Often, though, the basics are not enough for creating and managing a truly distinct and visually stunning user experience. The standard WPF controls may not integrate with the desired appearance of your application. Data may not be displayed in the most effective way. Your application's overall user experience may not be suited to the default look and feel of Windows themes. In many ways, a presentation technology needs visual extensibility as much as any other kind of extensibility.
For this reason, WPF provides a variety of mechanisms for creating unique user experiences, including a rich content model for controls, triggers, control and data templates, styles, UI resources, and themes and skins.
Content Model
The main purpose of a majority of the WPF controls is to display content. In WPF, the type and number of items that can constitute the content of a control is referred to as the control's content model. Some controls can contain a single item and type of content; for example, the content of a TextBox is a string value that is assigned to the Text property. The following example sets the content of a TextBox.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.TextBoxContentWindow"
Title="TextBox Content">
...
<TextBox Text="This is the content of a TextBox." />
...
</Window>
The following figure shows the result.
.png)
Other controls, however, can contain multiple items of different types of content; the content of a Button, specified by the Content property, can contain a variety of items including layout controls, text, images, and shapes. The following example shows a Button with content that includes a DockPanel, a Label, a Border, and a MediaElement.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.ButtonContentWindow"
Title="Button Content">
...
<Button Margin="20">
<!-- Button Content -->
<DockPanel Width="200" Height="180">
<Label DockPanel.Dock="Top" HorizontalAlignment="Center">Click Me!</Label>
<Border Background="Black" BorderBrush="Yellow" BorderThickness="2"
CornerRadius="2" Margin="5">
<MediaElement Source="media/wpf.wmv" Stretch="Fill" />
</Border>
</DockPanel>
</Button>
...
</Window>
The following figure shows the content of this button.
.png)
For more information on the kinds of content that is supported by various controls, see WPF Content Model.
Triggers
Although the main purpose of XAML markup is to implement an application's appearance, you can also use XAML to implement some aspects of an application's behavior. One example is the use of triggers to change an application's appearance based on user interactions. For more information, see "Triggers" in Styling and Templating.
Control Templates
The default UIs for WPF controls are typically constructed from other controls and shapes. For example, a Button is composed of both ButtonChrome and ContentPresenter controls. The ButtonChrome provides the standard button appearance, while the ContentPresenter displays the button's content, as specified by the Content property.
Sometimes the default appearance of a control may be incongruent with the overall appearance of an application. In this case, you can use a ControlTemplate to change the appearance of the control's UI without changing its content and behavior.
For example, the following example shows how to change the appearance of a Button by using a ControlTemplate.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.ControlTemplateButtonWindow"
Title="Button with Control Template" Height="158" Width="290">
<!-- Button using an ellipse -->
<Button Content="Click Me!" Click="button_Click">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Margin="5">
<Ellipse Stroke="DarkBlue" StrokeThickness="2">
<Ellipse.Fill>
<RadialGradientBrush Center="0.3,0.2" RadiusX="0.5" RadiusY="0.5">
<GradientStop Color="Azure" Offset="0.1" />
<GradientStop Color="CornflowerBlue" Offset="1.1" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<ContentPresenter Name="content" HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</Window>
Imports System.Windows ' Window, RoutedEventArgs, MessageBox
Namespace SDKSample
Public Class ControlTemplateButtonWindow
Inherits Window
Public Sub New()
InitializeComponent()
End Sub
Private Sub button_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
MessageBox.Show("Hello, Windows Presentation Foundation!")
End Sub
End Class
End Namespace
using System.Windows; // Window, RoutedEventArgs, MessageBox
namespace SDKSample
{
public partial class ControlTemplateButtonWindow : Window
{
public ControlTemplateButtonWindow()
{
InitializeComponent();
}
void button_Click(object sender, RoutedEventArgs e)
{
// Show message box when button is clicked
MessageBox.Show("Hello, Windows Presentation Foundation!");
}
}
}
In this example, the default button UI has been replaced with an Ellipse that has a dark blue border and is filled using a RadialGradientBrush. The ContentPresenter control displays the content of the Button, "Click Me!" When the Button is clicked, the Click event is still raised as part of the Button control's default behavior. The result is shown in the following figure.
.png)
For more information, see ControlTemplate. For an introductory sample, see Styling with ControlTemplates Sample.
Data Templates
Whereas a control template lets you specify the appearance of a control, a data template lets you specify the appearance of a control's content. Data templates are frequently used to enhance how bound data is displayed. The following figure shows the default appearance for a ListBox that is bound to a collection of Task objects, where each task has a name, description, and priority.
.png)
The default appearance is what you would expect from a ListBox. However, the default appearance of each task contains only the task name. To show the task name, description, and priority, the default appearance of the ListBox control's bound list items must be changed by using a DataTemplate. The following XAML defines such a DataTemplate, which is applied to each task by using the ItemTemplate attribute.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.DataTemplateWindow"
Title="With a Data Template">
...
<!-- Data Template (applied to each bound task item in the task collection) -->
<DataTemplate x:Key="myTaskTemplate">
<Border Name="border" BorderBrush="DarkSlateBlue" BorderThickness="2"
CornerRadius="2" Padding="5" Margin="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Padding="0,0,5,0" Text="Task Name:"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=TaskName}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Padding="0,0,5,0" Text="Description:"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Description}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Padding="0,0,5,0" Text="Priority:"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Priority}"/>
</Grid>
</Border>
</DataTemplate>
...
<!-- Data template is specified by the ItemTemplate attribute -->
<ListBox
ItemsSource="{Binding}"
ItemTemplate="{StaticResource myTaskTemplate}"
HorizontalContentAlignment="Stretch"
IsSynchronizedWithCurrentItem="True"
Margin="5,0,5,5" />
...
</Window>
The following figure shows the effect of this code.
.png)
Note that the ListBox has retained its behavior and overall appearance; only the appearance of the content being displayed by the list box has changed.
For more information, see Data Templating Overview. For an introductory sample, see Introduction to Data Templating Sample.
Styles
Styles enable developers and designers to standardize on a particular appearance for their product. WPF provides a strong style model, the foundation of which is the Style element. The following example creates a style that sets the background color for every Button on a window to Orange.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.StyleWindow"
Title="Styles">
...
<!-- Style that will be applied to all buttons -->
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Orange" />
<Setter Property="BorderBrush" Value="Crimson" />
<Setter Property="FontSize" Value="20" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Margin" Value="5" />
</Style>
...
<!-- This button will have the style applied to it -->
<Button>Click Me!</Button>
<!-- This labe will not have the style applied to it -->
<Label>Don't Click Me!</Label>
<!-- This button will have the style applied to it -->
<Button>Click Me!</Button>
...
</Window>
Because this style targets all Button controls, the style is automatically applied to all the buttons in the window, as shown in the following figure.
.png)
For more information, see Styling and Templating. For an introductory sample, see Introduction to Styling and Templating Sample.
Resources
Controls in an application should share the same appearance, which can include anything from fonts and background colors to control templates, data templates, and styles. You can use WPF's support for user interface (UI) resources to encapsulate these resources in a single location for reuse.
The following example defines a common background color that is shared by a Button and a Label.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.ResourcesWindow"
Title="Resources Window">
<!-- Define window-scoped background color resource -->
<Window.Resources>
<SolidColorBrush x:Key="defaultBackground" Color="Red" />
</Window.Resources>
...
<!-- Button background is defined by window-scoped resource -->
<Button Background="{StaticResource defaultBackground}">One Button</Button>
<!-- Label background is defined by window-scoped resource -->
<Label Background="{StaticResource defaultBackground}">One Label</Label>
...
</Window>
This example implements a background color resource by using the Window.Resources property element. This resource is available to all children of the Window. There are a variety of resource scopes, including the following, listed in the order in which they are resolved:
An individual control (using the inherited FrameworkElement..::.Resources property).
A Window or a Page (also using the inherited FrameworkElement..::.Resources property).
An Application (using the Application..::.Resources property).
The variety of scopes gives you flexibility with respect to the way in which you define and share your resources.
As an alternative to directly associating your resources with a particular scope, you can package one or more resources by using a separate ResourceDictionary that can be referenced in other parts of an application. For example, the following example defines a default background color in a resource dictionary.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Define background color resource -->
<SolidColorBrush x:Key="defaultBackground" Color="Red" />
<!-- Define other resources -->
...
</ResourceDictionary>
The following example references the resource dictionary defined in the previous example so that it is shared across an application.
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="BackgroundColorResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
...
</Application>
Resources and resource dictionaries are the foundation of WPF support for themes and skins.
For more information, see Resources Overview. For an introductory sample, see Application Resources Sample.
Themes and Skins
From a visual perspective, a theme defines the global appearance of Windows and the applications that run within it. Windows comes with several themes. For example, Microsoft Windows XP comes with both the Windows XP and Windows Classic themes, while Windows Vista comes with the Windows Vista and Windows Classic themes. The appearance that is defined by a theme defines the default appearance for a WPF application. WPF, however, does not integrate directly with Windows themes. Because the appearance of WPF is defined by templates, WPF includes one template for each of the well-known Windows themes, including Aero (Windows Vista), Classic (Microsoft Windows 2000), Luna (Microsoft Windows XP), and Royale (Microsoft Windows XP Media Center Edition 2005). These themes are packaged as resource dictionaries that are resolved if resources are not found in an application. Many applications rely on these themes to define their visual appearance; remaining consistent with Windows appearance helps users become familiar with more applications more easily.
On the other hand, the user experience for some applications does not necessarily come from the standard themes. For example, Microsoft Windows Media Player operates over audio and video data and benefits from a different style of user experience. Such UIs tend to provide customized, application-specific themes. These are known as skins, and applications that are skinned often provide hooks by which users can customize various aspects of the skin. Microsoft Windows Media Player has several prefabricated skins as well as a host of third-party skins.
Both themes and skins in WPF are most easily defined using resource dictionaries. The following example shows sample skin definitions.
<!-- Blue Skin -->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SDKSample">
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Blue" />
</Style>
...
<!-- Blue Skin -->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SDKSample">
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Blue" />
</Style>
...
</ResourceDictionary>
...
</ResourceDictionary>
<!-- Yellow Skin -->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SDKSample">
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Yellow" />
</Style>
...
<!-- Yellow Skin -->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SDKSample">
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Yellow" />
</Style>
...
</ResourceDictionary>
...
</ResourceDictionary>
For more information, see "Shared Resources and Themes" in Styling and Templating. For an introductory sample, see Skinned Application Sample.
Custom Controls
Although WPF provides a host of customization support, you may encounter situations where existing WPF controls do not meet the needs of either your application or its users. This can occur when:
The UI that you require cannot be created by customizing the look and feel of existing WPF implementations.
The behavior that you require is not supported (or not easily supported) by existing WPF implementations.
At this point, however, you can take advantage of one of three WPF models to create a new control. Each model targets a specific scenario and requires your custom control to derive from a particular WPF base class. The three models are listed here:
User Control Model. A custom control derives from UserControl and is composed of one or more other controls.
Control Model. A custom control derives from Control and is used to build implementations that separate their behavior from their appearance using templates, much like the majority of WPF controls. Deriving from Control allows you more freedom for creating a custom UI than user controls, but it may require more effort.
Framework Element Model. A custom control derives from FrameworkElement when its appearance is defined by custom rendering logic (not templates).
The following example shows a custom numeric up/down control that derives from UserControl.
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NumericUpDown">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<!-- Value text box -->
<Border BorderThickness="1" BorderBrush="Gray" Margin="2" Grid.RowSpan="2"
VerticalAlignment="Center" HorizontalAlignment="Stretch">
<TextBlock Name="valueText" Width="60" TextAlignment="Right" Padding="5"/>
</Border>
<!-- Up/Down buttons -->
<RepeatButton Name="upButton" Click="upButton_Click" Grid.Column="1"
Grid.Row="0">Up</RepeatButton>
<RepeatButton Name="downButton" Click="downButton_Click" Grid.Column="1"
Grid.Row="1">Down</RepeatButton>
</Grid>
</UserControl>
imports System 'EventArgs
imports System.Windows 'DependencyObject, DependencyPropertyChangedEventArgs,
' FrameworkPropertyMetadata, PropertyChangedCallback,
' RoutedPropertyChangedEventArgs
imports System.Windows.Controls 'UserControl
Namespace SDKSample
' Interaction logic for NumericUpDown.xaml
Partial Public Class NumericUpDown
Inherits System.Windows.Controls.UserControl
'NumericUpDown user control implementation
...
End Class
End Namespace
using System; // EventArgs
using System.Windows; // DependencyObject, DependencyPropertyChangedEventArgs,
// FrameworkPropertyMetadata, PropertyChangedCallback,
// RoutedPropertyChangedEventArgs
using System.Windows.Controls; // UserControl
namespace SDKSample
{
public partial class NumericUpDown : UserControl
{
// NumericUpDown user control implementation
...
}
}
The next example illustrates the XAML that is required to incorporate the user control into a Window.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.UserControlWindow"
xmlns:local="clr-namespace:SDKSample"
Title="User Control Window">
...
<!-- Numeric Up/Down user control -->
<local:NumericUpDown />
...
</Window>
The following figure shows the NumericUpDown control hosted in a Window.
.png)
For more information on custom controls, see Control Authoring Overview. For introductory samples, see Control Customization Samples.