【概念】WPF中的各种Template的区分(ControlTemplate、DataTemplate、HierarchicalDataTemplate)
入门学习:
https://www.cnblogs.com/huangxincheng/archive/2012/06/28/2566595.html
一、控件模板 ControlTemplate
利用ControlTemplate可以彻底的颠覆控件的默认外观。<ControlTemplate>里面的内容就是视觉树VisualTree。
两个重要属性:
(1)ContentPresenter
重定义控件模板,默认模板将会被覆盖,此时需要利用ContentPresenter,把原有模板的属性原封不动的投放到自定义模板中。
(2)Triggers
触发器列表,里面包含一些触发器Trigger,我们可以定制这个触发器列表来使控件对外界的刺激发生反应,比如鼠标经过时文本变成粗体等。
看例子:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ControlTemplate x:Key="rect" TargetType="{x:Type CheckBox}">
<ControlTemplate.Resources>
<SolidColorBrush x:Key="redBrush" Color="Red"/>
</ControlTemplate.Resources>
<StackPanel>
<Rectangle Name="breakRectangle" Stroke="Red" StrokeThickness="2" Width="20" Height="20">
<Rectangle.Fill>
<SolidColorBrush Color="White"/>
</Rectangle.Fill>
</Rectangle>
<ContentPresenter/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="breakRectangle" Property="Fill" Value="{StaticResource ResourceKey=redBrush}">
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<Canvas>
<CheckBox Template="{StaticResource ResourceKey=rect}" Content="我是CheckBox"/>
</Canvas>
</Window>
<ContentPresenter Margin="{TemplateBinding Padding}" /> 实现了将模板中的Margin绑定到原控件中的Padding上去。
写到Style里就是:
<Style x:Key="cbx" TargetType="{x:Type CheckBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<ControlTemplate.Resources>
<SolidColorBrush x:Key="redBrush" Color="Red"/>
</ControlTemplate.Resources>
<StackPanel>
<Rectangle Name="breakRectangle" Stroke="Red" StrokeThickness="2" Width="20" Height="20">
<Rectangle.Fill>
<SolidColorBrush Color="White"/>
</Rectangle.Fill>
</Rectangle>
<ContentPresenter/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="breakRectangle" Property="Fill" Value="{StaticResource ResourceKey=redBrush}">
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
调用:
<CheckBox Style="{StaticResource ResourceKey=cbx}" Content="我是CheckBox"/>
(3)ItemsPresenter
继承自ItemsControl的控件,有一个ItemsPanel属性作为集合元素承载容器。子元素ItemsPresenter负责呈现控件的任务。
只要把ItemsPresenter放在内部模板中,那么ItemsPresenter则会去检测父元素是否为集合控件,然后将ItemsPanel添加到其内部视觉树当中。
<Style x:Key="{x:Type ItemsControl}"
TargetType="{x:Type ItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="true">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
比较常见的继承自ItemsControl的控件,比如ComboBox,ContextMenu,ListBox,DataGrid,ListView等。
二、数据模板 DataTemplate
数据模板定义了数据的显示方式,也就是数据对象的可视结构。主要是可以自定义控件的同时进行数据绑定。
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:src="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ObjectDataProvider x:Key="personList" ObjectType="{x:Type src:PersonList}"/>
<DataTemplate x:Key="rect">
<Border Name="border" BorderBrush="Aqua" BorderThickness="1" Padding="5" Margin="5">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Margin="5,0,0,0"/>
<TextBlock Text="{Binding Age}" Margin="5,0,0,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Address}" Margin="5,0,0,0"/>
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source={StaticResource ResourceKey=personList}}"
ItemTemplate="{StaticResource ResourceKey=rect}"></ListBox>
</Grid>
</Window>
注意这里在调用时应该绑定的是 ItemTemplate 属性。
1、一些例子
数据模板作用于
(1)ContentControl控件(内容控件)
(有Content属性的控件是ContentControl控件)
被ContentControl控件的ContentTemplate属性使用。
最常用的ContentControl控件包括:Button、ButtonBase、CheckBox、ComboBoxItem、Label、ListBoxItem、ListViewItem、RadioButton、ToolTip、UserControl、Window等。
一个运用数据模板的Button实例:https://blog.csdn.net/u014650759/article/details/98472155
数据模板通过Content属性指定数据源,将数据先绑定到Content属性上,再通过ContentTemplate指定数据处理方式。
(2)ItemsControl控件(项控件)
被ItemsControl控件的ItemTemplate属性。
最常见的ItemsControl控件包括:Menu、MenuBase、ContextMenu、ComoBox、ListBox、ListView等。
一个运用数据模板的ListBox实例:https://blog.csdn.net/yl2isoft/article/details/38712449
DataTemplate数据绑定是怎样实现的?
ListBox包含属性ItemTemplate。将ItemTemplate属性绑定到自定义的DataTemplate,ListBox则按照对象个数创建多个ListBoxItem,并将ListBoxItem的DataContext设置为相应的Student对象,同时将DataTemplate中的元素绑定到Student对象的属性。
(3)被GridViewColumn控件的CellTemplate属性使用。
即用于设置控件的数据内容。
2、HierarchicalDataTemplate
它是针对具有分层数据结构的控件设计的,比如说TreeView,相当于可以每一个层级上做DataTemplate。
https://www.cnblogs.com/goldren/archive/2012/12/11/2812697.html
3、数据模板和控件模板的区别
(1)面对的绑定对象类型不一致。
ControlTemplate只是为了自己而呈现的,并不反映基础数据。
DataTemplate可以为用户呈现数据。
(2) 数据模板通常在页面xaml内部直接定义,而控件模板通常放到样式(Style)文件中。
4、展开说一下ContentControl与ContentPresenter
数据是如何显示出来的?虽然数据模板定义了数据的可视结构,但只有控件才是可视的,数据一般是被控件承载,这里需要另外的一个对象ContentPresenter。
内容控件的Content属性是由ContentControl类定义的,ContentPresenter负责将ContentControl的Content属性显示出来。
<!--使用ContentPresenter对Button进行重绘-->
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--使用TextBlock对Button进行重绘-->
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock Text="{TemplateBinding Content}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
可见使用ContentPresenter非常的方便,只要将ContentPresenter放在模板中即可,也不需要做任何的额外的绑定。
(ContentPresenter内部帮我们做了默认的绑定)。如果使用TextBlock还是需要做绑定的。
ContentPresenter默认呈现文字,但可以自己以Content为数据源而重新定义模板(ContentTemplate),则ContentPresenter将不再局限于文字的呈现。
如果只是为了呈现文字的话,是不需要ContentPresenter的。
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" Content="hello">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<Border BorderBrush="Wheat">
<TextBlock Text="{Binding xxx}"/>
</Border>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
不如说ContentControl是ContentPresenter的一个特例,而ContentPresenter则是ContentControl的基础。
为了适配ContentPresenter,ContentControl提供了内容模型的相关属性,本质上ContentPresenter并非仅仅只是用到ContentControl而已,ContentPresenter可以通过指定ContentSource来绑定指定的源属性。
三、ItemsPanelTemplate
首先我们要知道常见的条目控件有:ListBox,Menu,StatusBar等。
比如拿ListBox来说,ItemBox的ItemPanel其实是一个VisualizingStackPanel,就是说ListBox的每一项的排列方式是遵循StackPanel的
原则,也就是从上到下的排列方式。如果要实现从左到右排列:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:src="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ObjectDataProvider x:Key="personList" ObjectType="{x:Type src:PersonList}"/>
<DataTemplate x:Key="rect">
<Border Name="border" BorderBrush="Aqua" BorderThickness="1" Padding="5" Margin="5">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Margin="5,0,0,0"/>
<TextBlock Text="{Binding Age}" Margin="5,0,0,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Address}" Margin="5,0,0,0"/>
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
<ItemsPanelTemplate x:Key="items">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source={StaticResource ResourceKey=personList}}"
ItemTemplate="{StaticResource ResourceKey=rect}" ItemsPanel="{StaticResource ResourceKey=items}"></ListBox>
</Grid>
</Window>
也就是说,ItemsPanelTemplate可以用来定义集合控件的容器外观。
四、总结
1、Template
控件模板,是指整个控件的展示和布局。
如ComboBox,可分为文本区域,下拉按钮区域,Items的Popup区域。
Template就是管理这些位置的布局。
2、ItemsPresenter
可以简单理解为占位符,在样式中使用,标记着这个区域用来展示该控件的Items。
如:ComboBox的下拉列表的可选项。
但是,只负责显示,而不能管理如何显示,如果我们要内容横向排列,就要用到ItemsPanel。
3、ItemsPanel
管理Items的排列方式,如,ComboBox默认是竖直排列的,我们要横着排列,只需要定义ItemsPanel为WrapPanel,就可以了。
这时候Items的排列方式已经完成,如果还要让ComboBox的每个项都重写,比如,背景、图标等,就要用到ItemContainerStyle。
4、ItemContainerStyle
就是每个项的样式,自己重写,就可以定制出每个项的样式了。