WPF控件模版数据模板
- 控件模板(ControlTemplate)
-
- 定义方式
- [TemplateBinding 用法](#TemplateBinding 用法)
- 数据模板(DataTemplate)
之前的篇章虽然有提到过 控件模版和数据模板,但是这里还是要重点在讲解一下。
控件模板(ControlTemplate)
-
定义与用途
作用:控件模板用于定义控件的可视化结构和行为,允许完全自定义控件的外观,而不影响其功能。例如,将默认的矩形按钮改为圆形,并自定义其悬停、点击效果。
-
应用场景:需要修改控件的视觉布局(如替换按钮的默认外观),或调整控件的组成部分(如添加动画、调整状态变化逻辑)。
-
关键特性
TargetType:指定模板适用的控件类型(如Button、ListBox)。视觉状态(Visual States):通过VisualStateManager定义控件在不同状态(如MouseOver、Pressed)下的外观变化。
必需元素:需保留控件的功能逻辑。例如,按钮的ContentPresenter用于显示内容,若省略则内容无法显示
定义方式
内联定义(直接写在ListBox中)
若将ControlTemplate直接内联到ListBox的Template属性中(如示例代码),则该模板仅作用于当前ListBox实例。这种写法不会影响其他ListBox控件的外观,属于局部应用
javascript
<ListBox>
<ListBox.Template>
<ControlTemplate TargetType="ListBox">
<!-- 自定义模板内容 -->
</ControlTemplate>
</ListBox.Template>
</ListBox>
特点:
无x:Key标识,直接绑定到当前控件的Template属性。
修改仅限当前控件,不影响其他ListBox实例
资源字典中定义
若将ControlTemplate定义在资源字典(如Window.Resources或全局资源文件)中,其作用范围由以下两种方式决定
- 显式指定x:Key
通过x:Key标识的模板需手动引用,适用于需要复用的场景
特点:
必须通过StaticResource或DynamicResource显式引用。
仅作用于手动引用该模板的ListBox。
javascript
<Window.Resources>
<ControlTemplate x:Key="CustomListBoxTemplate" TargetType="ListBox">
<!-- 模板内容 -->
</ControlTemplate>
</Window.Resources>
<ListBox Template="{StaticResource CustomListBoxTemplate}"/>
- 隐式样式(无x:Key)
若定义模板时省略x:Key并指定TargetType="ListBox",该模板会隐式作用于所有同类型控件(即所有未显式指定模板的ListBox)
特点:
覆盖所有ListBox的默认样式。
需通过TargetType匹配控件类型,适用于统一全局风格
javascript
<Window.Resources>
<Style TargetType="ListBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<!-- 全局模板内容 -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
定义方式区别

TemplateBinding 用法
TemplateBinding 是一个将模板内部元素的属性与目标控件属性动态关联的关键机制。
TemplateBinding Background 用最通俗的话来说,它相当于在模板中对目标控件说:"你的Background属性值是多少,我就用多少,按下面图片列子就是 Ellipse的填充颜色根据模板button的背景来,设置红色那填充就是红色
javascript
<Button Content="Click Me">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid>
<!-- 椭圆背景绑定到按钮的Background -->
<Ellipse Fill="{TemplateBinding Background}" Stroke="Black"/>
<!-- 内容区域绑定到按钮的Content -->
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
对比普通Binding的区别
其实用普通的binding 也能实现 比如下面这俩个写法 效果一样的
javascript
<Border Background="{TemplateBinding Background}"/>
<Border Background="{Binding Background, RelativeSource={RelativeSource TemplatedParent}}"/>
而TemplateBinding实际上是这种写法的简化版,只适用于控件模板内部,且语法更简洁
常见误区
- 误以为自动继承属性
有人认为模板内部元素会自动继承宿主控件的属性,但实际必须显式使用TemplateBinding建立关联。 - 忽略目标类型限制
如果宿主控件(如Button)没有某个属性(例如Fill),则模板内部无法通过TemplateBinding绑定该属性。 - 忘记TargetType
在定义ControlTemplate时,必须指定TargetType(如TargetType="Button"),否则TemplateBinding可能无法正确解析属性
何时使用
- 需要动态同步属性:如按钮的Background、BorderThickness等需要根据宿主控件状态变化;
- 简化模板代码:避免冗长的RelativeSource绑定;
- 性能敏感场景:在需要高频更新属性的动画或复杂控件中优先使用
通过这种机制,WPF实现了控件逻辑与视觉表现的解耦------开发者只需修改宿主控件的属性,模板外观即可自动响应
数据模板(DataTemplate)
定义与用途
- 作用:数据模板定义数据对象的呈现方式,将数据属性映射到UI元素。例如,将Customer对象的姓名和地址以特定布局显示。
- 应用场景:需自定义数据在控件中的显示形式(如在ListBox中显示复杂数据项)。
- 关键特性
DataType:指定模板适用的数据类型,支持隐式应用(无x:Key时自动匹配类型)。 - 绑定与布局:通过数据绑定({Binding})连接数据属性与UI元素,并定义布局结构(如StackPanel、Grid)
主要应用控件
ItemsControl及其派生控件
ListBox
为列表中的每个数据项定义布局,例如显示学生列表时自定义包含头像、姓名和分数的卡片布局
javascript
<ListBox ItemsSource="{Binding Students}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Avatar}"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
ListView
类似ListBox,但支持更复杂的列定义。常用于数据表格展示,结合GridView定义列模板
javascript
<ListView>
<ListView.View>
<GridView>
<GridViewColumn Header="姓名" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="年龄" CellTemplate="{StaticResource AgeTemplate}"/>
</GridView>
</ListView.View>
</ListView>
ComboBox
自定义下拉项的外观,例如在选项中加入图标和详细描述
javascript
<ComboBox ItemTemplate="{StaticResource PersonTemplate}"/>
ContentControl及其派生控件
Button、Label、Window
当控件内容为复杂对象时,用DataTemplate替代默认的ToString()显示。例如按钮内容显示为带图标的用户信息
javascript
<Button Content="{Binding SelectedUser}">
<Button.ContentTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding Icon}"/>
<TextBlock Text="{Binding UserName}"/>
</StackPanel>
</DataTemplate>
</Button.ContentTemplate>
</Button>
TabItem
自定义标签页头的显示内容,如显示带状态指示器的标题
javascript
<TabControl>
<TabItem Header="{Binding TabHeader}">
<TabItem.HeaderTemplate>
<DataTemplate>
<StackPanel>
<Ellipse Fill="{Binding StatusColor}" Width="10"/>
<TextBlock Text="{Binding Title}"/>
</StackPanel>
</DataTemplate>
</TabItem.HeaderTemplate>
</TabItem>
</TabControl>
其他特殊场景控件
TreeView
通过HierarchicalDataTemplate定义层级数据的显示,例如文件树结构
javascript
<TreeView ItemsSource="{Binding Folders}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
DataGrid(需手动配置)
虽然DataGrid默认使用列绑定,但可通过CellTemplate自定义单元格内容。例如在单元格内显示评分星级
javascript
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn Header="评分">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<RatingControl Value="{Binding Score}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
动态模板选择
核心方法
必须继承 DataTemplateSelector 类并重写 SelectTemplate 方法。该方法接收数据项和容器参数,返回匹配的 DataTemplate
- 设计多个 DataTemplate
在 XAML 资源中为不同数据类型定义模板。例如,文本项用 TextBlock,颜色项用 Border 背景
javascript
<Window.Resources>
<DataTemplate x:Key="TextTemplate">
<TextBlock Text="{Binding Ob.Name}" />
</DataTemplate>
<DataTemplate x:Key="ColorTemplate">
<Border Background="{Binding Ob.Color}" Width="90" Height="30" />
</DataTemplate>
</Window.Resources>
- 实现选择器类
自定义选择器将数据属性与模板关联。例如,根据 TypeName 属性选择模板
javascript
public class ListBoxTemplateSelector : DataTemplateSelector
{
public DataTemplate TextTemplate { get; set; }
public DataTemplate ColorTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var data = item as DataModel;
return data?.TypeName switch
{
"text" => TextTemplate,
"color" => ColorTemplate,
_ => base.SelectTemplate(item, container)
};
}
}
- 绑定到控件
将选择器实例赋给控件的 ItemTemplateSelector 属性
javascript
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplateSelector>
<local:ListBoxTemplateSelector
TextTemplate="{StaticResource TextTemplate}"
ColorTemplate="{StaticResource ColorTemplate}" />
</ListBox.ItemTemplateSelector>
</ListBox>
典型应用场景