WPF提供了两种主要的模板类型:
- ControlTemplate:定义了控件的视觉结构和视觉行为。
-
DataTemplate:定义数据如何呈现在控件中(例如,在
ListBox
或ComboBox
中)。
ControlTemplate:
控件模板允许开发者完全自定义控件的外观。通过定义控件模板,开发者可以改变控件的结构和样式,而无需更改控件的功能。
简单的说,button,grid等等这些都是控件,而通过控件模板,可以自己来定义这些控件怎么显示。
和Style的区别是,控件模板可以彻底改变控件的默认视觉结构,而不仅仅是调整某些属性。
步骤1:定义模板
首先,在资源字典或控件的资源中定义一个ControlTemplate
。
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ControlTemplate x:Key="CustomButtonTemplate" TargetType="Button">
<Grid>
<Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="2"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Window.Resources>
<Grid>
<Button Content="Click Me" Width="100" Height="100" Template="{StaticResource CustomButtonTemplate}" Background="LightBlue" BorderBrush="DarkBlue"/>
</Grid>
</Window>
在这个示例中,我们创建了一个ControlTemplate
,它定义了一个带有Ellipse
(椭圆形)和ContentPresenter
(内容呈现器)的按钮。TemplateBinding
用于绑定模板中元素的属性到控件的属性(例如,背景颜色和边框颜色)。
步骤2:应用模板
接下来,将模板应用到按钮控件上。
<Button Content="Click Me" Width="100" Height="100" Template="{StaticResource CustomButtonTemplate}" Background="LightBlue" BorderBrush="DarkBlue"/>
触发器
你可以在控件模板中使用触发器来定义控件在不同状态下的外观。例如,可以根据按钮的IsPressed
状态改变其外观。
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ControlTemplate x:Key="CustomButtonTemplate" TargetType="Button">
<Grid>
<Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="2"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ellipse" Property="Fill" Value="LightGray"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<Grid>
<Button Content="Click Me" Width="100" Height="100" Template="{StaticResource CustomButtonTemplate}" Background="LightBlue" BorderBrush="DarkBlue"/>
</Grid>
</Window>
当按钮被按下时,触发器将椭圆的填充颜色更改为浅灰色。
Style和控件模板
你通常会将控件模板与Style结合使用,以便更方便地复用和管理模板。
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="CustomButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse x:Name="ellipse" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="2"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ellipse" Property="Fill" Value="LightGray"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button Content="Click Me" Width="100" Height="100" Style="{StaticResource CustomButtonStyle}" Background="LightBlue" BorderBrush="DarkBlue"/>
</Grid>
</Window>
模板绑定和模板部件
-
模板绑定:
TemplateBinding
用于在控件模板中将控件属性绑定到模板中元素的属性。 -
模板部件:某些控件在其模板中具有特定的命名部件,这些部件必须在模板中定义。例如,
ScrollViewer
控件模板中必须包含名为PART_HorizontalScrollBar
和PART_VerticalScrollBar
的元素。
数据模板:
数据模板(DataTemplate
)是用于定义如何显示数据的模板。它允许开发者以声明的方式定义数据对象的可视化表示,通常与控件如 ListBox
、ListView
、ComboBox
、DataGrid
等一起使用,这些控件能够绑定到数据集合(如 IEnumerable
),并为每一个数据项生成一个视图。
当你绑定一个数据集合到一个控件时,WPF 通常会调用数据项的 ToString()
方法来生成显示内容。然而,这通常无法满足复杂界面的需求。这时,可以使用 DataTemplate
来定制数据项的显示方式。
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
如果我们有一个 ListBox
,并且想要显示 Person
对象的 Name
和 Age
,可以定义如下的 DataTemplate
:
<Window.Resources>
<DataTemplate x:Key="PersonTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text=" - " />
<TextBlock Text="{Binding Age}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<ListBox ItemsSource="{Binding Persons}" ItemTemplate="{StaticResource PersonTemplate}" />
-
Window.Resources
:这是一个资源字典,用来定义全局的或者局部的资源。在这里,我们定义了一个DataTemplate
,名为PersonTemplate
。 -
DataTemplate
:定义了如何显示Person
对象。在模板内部,我们使用了一个StackPanel
,并且使用TextBlock
来显示Name
和Age
。 -
Binding
:表示数据绑定,它将TextBlock
的Text
属性绑定到数据对象的Name
和Age
属性。 -
ItemTemplate
:ListBox
控件使用的属性,它指定了用于显示每个数据项的模板。
隐式数据模板
除了显式地通过 x:Key
指定模板外,还可以通过隐式数据模板使其自动适用于特定类型的所有数据项:
<Window.Resources>
<DataTemplate DataType="{x:Type local:Person}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text=" - " />
<TextBlock Text="{Binding Age}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
在这种情况下,不需要在控件上显式指定 ItemTemplate
,WPF 会自动使用该模板来显示 Person
类型的所有对象。