WPF 详解模板
在WPF中有三大模板 ControlTemplate,ItemsPanelTemplate,DataTemplate.其中ControlTemplate和 ItemsPanelTemplate是控件模板,DataTemplate是数据模板,他们都派生自FrameworkTemplate抽象类。
1、ControlTemplate
ControlTemplate:控件模板主要有两个重要属性:VisualTree内容属性和Triggers触发器。所谓VisualTree(视觉树),就是呈现我们所画的控件。Triggers可以对我们的视觉树上的元素进行一些变化。一般用于单内容控件。
画一个按钮模板来举例说明:
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse Width="100" Height="100">
<Ellipse.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="blue"/>
<GradientStop Offset="1" Color="LightBlue"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse Width="80" Height="80">
<Ellipse.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="White"/>
<GradientStop Offset="1" Color="Transparent"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Button Content="Hello WPF"/>
结果:
ControlTemplate之子 ContentControl和ContentPresenter
我们在ControlTemplate中画了两个椭圆,应用于所有的Button按钮,但我们Button中有Content属性(内容为 Hello WPF),却没有显示出来。因为这里用ControlTemplate重写了Button的样式,所以我们也要在ControlTemplate中增加 ContentControl。通过ContentControl中的Content来绑定父容器的Content属性。

<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse Width="" Height="">
<Ellipse.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="" Color="blue"/>
<GradientStop Offset="" Color="LightBlue"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse Width="" Height="">
<Ellipse.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="" Color="White"/>
<GradientStop Offset="" Color="Transparent"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<ContentControl VerticalAlignment="Center" HorizontalAlignment="Center" Content="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
这下内容出来了
我们来看一下,ContentControl继承于Control的,用MSDN的话是:表示包含单项内容的控件、ContentControl 可以包含任何类型的公共语言运行库对象。如下ContentControl类图。
为了提高性能,我们可以用一个ControlPresenter来代替ContentControl,效果还是一样,那他们有什么区别呢?
来看下ControlPresenter这个类,它继承于FreameworkElement,如下图:
ControlPresenter 通常叫做内容占位符。所以我们可以看到
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>来代替<ContentControl VerticalAlignment="Center" HorizontalAlignment="Center" Content="{TemplateBinding Content}"/> 。这里少了Content绑定父容器,因为ControlPresenter有个隐式的Content="{TemplateBinding Content}",也就是你可以写也可以不写它。
从他们的基类可以看出,ContentControl比ContentPresenter大多了。其实ControlPresenter是一个原始的构建块,而ContentControl是一个带控件模板的成熟控件(里面包含ControlPresenter)。
所以我们一般用ControlPresenter。
ControlTemplate的VisualTree我们讲过了,下面看下他的trigger如何运用。
我们在原来的代码中增加
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="ellipse1" Property="Fill" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
当我们把鼠标移上去的时候就会变成如下图所示:
发挥我们的想象力,我们可以根据ControlTemplate做更多的特效。如下
<Style TargetType="CheckBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<DockPanel>
<ContentPresenter DockPanel.Dock="Left" VerticalAlignment="Center" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Grid.ColumnSpan="2" Fill="Gray"/>
<TextBlock x:Name="txtBox" Foreground="White" />
</Grid>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="txtBox" Property="Grid.Column" Value="1"/>
<Setter TargetName="txtBox" Property="Text" Value="On"/>
<Setter TargetName="txtBox" Property="Background" Value="LightBlue"/>
</Trigger>
<Trigger Property="IsChecked" Value="{x:Null}">
<Setter TargetName="txtBox" Property="Grid.Column" Value="0"/>
</Trigger>
<Trigger Property="IsChecked" Value="false">
<Setter TargetName="txtBox" Property="Grid.Column" Value="0"/>
<Setter TargetName="txtBox" Property="Text" Value="OFF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Grid>
<CheckBox Width="100" Height="30" Content="Click Me"/>
</Grid>
效果图:点击之前点击之后
2、ItemsPanelTemplate
ItemsPanelTemplate在MSDN的解释是:ItemsPanelTemplate 指定用于项的布局的面板。 GroupStyle 具有一个类型为 ItemsPanelTemplate 的 Panel 属性。 ItemsControl 类型具有一个类型为ItemsPanelTemplate 的 ItemsPanel 属性。
我们先讲ItemTemplate。它一般用在多个内容控件的模板。比如ListBox。
如下看ListBox应用ItemTemplate:
在xaml中
<Style TargetType="ListBox">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Image Source="{Binding UriSource}" Width="100" Height="100"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<ListBox x:Name="listBox" />
在后台代码我们给它一些图片来填充这个ListBox.

public partial class ListBoxUserControl : UserControl
{
public ListBoxUserControl()
{
InitializeComponent();
listBox.ItemsSource = LoadImages();
} public List<BitmapImage> LoadImages()
{
List<BitmapImage> bitmapImages=new List<BitmapImage>();
DirectoryInfo directoryInfo = new DirectoryInfo(@"E:\WPFDEMO\ControlTest\ControlTest\Images");
foreach (var item in directoryInfo.GetFiles("*.jpg"))
{
Uri uri=new Uri(item.FullName);
bitmapImages.Add(new BitmapImage(uri));
}
return bitmapImages;
}
}
每一张图片就是一个Item。
我们如果想让图片以横向显示。一开始我以为用StackPanel的Orientation=”Horiziontal”,发现犯了个错误。这样设 置是现在把Items中某一个item中的内容水平显示啊。这时就要用到ItemsPanelTemplate这个模板了。我们在ListBox的样式中 增加如下红色区域的代码:
<Style TargetType="ListBox">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding UriSource}" Width="100" Height="100"/>
<TextBlock Text="qq" Background="Red"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel"> <Setter.Value> <ItemsPanelTemplate> <StackPanel /> </ItemsPanelTemplate> </Setter.Value> </Setter>
</Style>
这里我在DataTemplate中加了StackPanel 为了说明加在这里的效果只是区分:Items整体横向和Item中内容的区别。
现在我们要想让布局可以随着窗体宽度变化,我们只要把ItemsPanelTemplate中的StackPanel
改成WrapPanel,并且设置ListBox的
ScrollViewer.HorizontalScrollBarVisibility="Disabled",这样才可以看到效果。
ControlTemplate
之 ItemsPresenter和ContentPresenter
我们先构造一个TreeView
xaml中:
在开始加个Loaded="UserControl_Loaded"
<Grid>
<TreeView x:Name="treeview" />
</Grid>
后台代码中:

public class Node
{
private IList<Node> _childNodes;
private string _name;
public Node()
{}
public Node(string name)
{
_name = name;
}
public string Name
{
get { return _name; }
set { _name = value; }
} public IList<Node> ChildNodes
{
get
{
if (_childNodes==null)
_childNodes=new List<Node>();
return _childNodes;
} }
} public partial class TreeViewUserControl : UserControl
{
public TreeViewUserControl()
{
InitializeComponent();
} private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
treeview.PreviewKeyDown += (o,a) => { a.Handled = true; };
PopulateTreeView();
}
void PopulateTreeView()
{
Node rootNode=new Node("GrandFather");
for (int i = 0; i < 2; i++)
{
Node child=new Node("Father");
rootNode.ChildNodes.Add(child);
for (int j = 0; j < 3; j++)
{
Node child2=new Node("Son");
child.ChildNodes.Add(child2);
}
} Node dummy=new Node();
dummy.ChildNodes.Add(rootNode);
treeview.ItemsSource = dummy.ChildNodes;
} }
没有任何样式的TreeView
下面我们如何运用ItemsPresenter和ContentPresenter来添加样式。来实现下面这幅图的效果
在TreeView中ItemsPresenter和ContentPresenter是什么关系:ContentPresenter
是用来显示TreeView中Item的内容 。ItemsPresenter
是用来显示它的子项(Item的子项,也就是说child’s Items)。

<Style TargetType="TreeViewItem">
<Style.Resources>
<LinearGradientBrush x:Key="ItemAreaBrush" StartPoint="0,0.5" EndPoint="0.5,1">
<GradientStop Offset="" Color="#66000000"/>
<GradientStop Offset="" Color="#22000000"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="SelectedItemAreaBrush" StartPoint="0.5, 0" EndPoint="0.5, 1">
<GradientStop Color="Orange" Offset="" />
<GradientStop Color="OrangeRed" Offset="" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="ItemBorderBrush" StartPoint="0.5, 0" EndPoint="0.5, 1">
<GradientStop Color="LightGray" Offset="" />
<GradientStop Color="Gray" Offset="" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="SelectedItemBorderBrush" StartPoint="0.5, 0" EndPoint="0.5, 1">
<GradientStop Color="Yellow" Offset="" />
<GradientStop Color="Black" Offset="" />
</LinearGradientBrush>
<DropShadowBitmapEffect x:Key="DropShadowEffect"/>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<Grid Margin="">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border x:Name="border" Background="{StaticResource ResourceKey=ItemAreaBrush}"
BorderBrush="{StaticResource ItemBorderBrush}" BorderThickness="" CornerRadius="" Padding="">
<ContentPresenter ContentSource="Header" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
<ItemsPresenter Grid.Row=""/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="border" Property="Panel.Background" Value="{StaticResource SelectedItemAreaBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" IsItemsHost="True"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter> </Style>
看红色代码的部分,这里ContentPresenter显示了父容器的Header的内容,比如GrandFather,Father,Son.而ItemsPresenter则是否让他显示其子元素。比如GrandFather的子元素为Father,没有设置<ItemsPresenter Grid.Row=""/> 的话。子元素Father是不会显示的。
这里为了突出层次化,运用了ItemsPanelTemplate。最后我们在资源里引用这个样式。

<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Controls/TreeViewItemStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
<HierarchicalDataTemplate DataType="{x:Type controls:Node}" ItemsSource="{Binding ChildNodes}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</ResourceDictionary>
</UserControl.Resources>
3、DataTemplate和HierarchicalDataTemplate
DataTemplate就是显示绑定数据对象的模板。
HierarchicalDataTemplate继承于DataTemplate,它专门对TreeViewItem 或 MenuItem的一些数据对象的绑定。
想了解下DataTemplate和HierarchicalDataTemplate,就看以上的例子吧。他们中只要有控件的绑定都会用到。想了解的更深一步,请看下南柯之石的这篇文章。
WPF 详解模板的更多相关文章
- 高斯消元法(Gauss Elimination)【超详解&模板】
高斯消元法,是线性代数中的一个算法,可用来求解线性方程组,并可以求出矩阵的秩,以及求出可逆方阵的逆矩阵.高斯消元法的原理是:若用初等行变换将增广矩阵 化为 ,则AX = B与CX = D是同解方程组. ...
- BIND9配置文件详解模板[转载]
在CU上看到了一篇关于BIND9配置文件详解的文章,感觉不错,现转载了分享一下. //named.conf 注释说明 by shellyxz@163.com// 此文件对bind9的默认配置文件的说明 ...
- 【learning】多项式开根详解+模板
概述 多项式开跟是一个非常重要的知识点,许多多项式题目都要用到这一算法. 用快速数论变换,多项式求逆元和倍增法可以在$O(n log n)$的时间复杂度下求出一个$n$次多项式的开根. 前置技能 快速 ...
- KM算法详解+模板
http://www.cnblogs.com/wenruo/p/5264235.html KM算法用来求二分图最大权完美匹配. 本文配合该博文服用更佳:趣写算法系列之--匈牙利算法 现在有N男N女,男 ...
- discuz默认模板文件结构详解-模板文件夹介绍
| — template — default 系统内置风格模板(默认风格)| — template — default – discuz_style_default.xml 风格安装文件,可用 ...
- 多项式求逆元详解+模板 【洛谷P4238】多项式求逆
概述 多项式求逆元是一个非常重要的知识点,许多多项式操作都需要用到该算法,包括多项式取模,除法,开跟,求ln,求exp,快速幂.用快速傅里叶变换和倍增法可以在$O(n log n)$的时间复杂度下求出 ...
- KM算法 详解+模板
先说KM算法求二分图的最佳匹配思想,再详讲KM的实现.[KM算法求二分图的最佳匹配思想] 对于具有二部划分( V1, V2 )的加权完全二分图,其中 V1= { x1, x2, x3, ... , x ...
- manacher算法 详解+模板
manacher算法可以解决字符串的回文子串长度问题. 个人感觉szy学长讲的非常好,讲过之后基本上就理解了. 那就讲一下个人的理解.(参考了szy学长的ppt) 如果一个回文子串的长度是偶数,对称轴 ...
- BP神经网络 详解模板
%原始数据输入 P=[ - 6.142 - 27.5 5.068 - 31.7 5.196 - 34.1 6.362 - 31.54 6.472 - 30.17 6.578 - 29.53 6.351 ...
随机推荐
- 一个表格说明RelativeLayout中的几个重要属性【Written By KillerLegend】
RelativeLayout中几种布局属性的说明 无 无 无 无
- ubuntu12.04和deepin12.06使用root账户登录
修改lightdm.conf登录方式, 1.首先,激活root账户, 在终端中执行 sudo passwd root 连续两次输入 密码,即使root账户的密码,注意密码并不在终端中显示 2. 切 ...
- chrome下的js调试
console输出对象信息:console.log(object[,object,.....])
- 向plsql中导入数据
1.TOOLS-->ODBC IMPORTER 2.TOOLS-->TEXT IMPORTER3.sqlldr userid=zj/zj@orcl control=D:\test.ctl ...
- Redbean:入门(三) - Exec 以及 Query 以及 ConvertToBeans
<?php //引入rb入口文件 include_once 'rb.php'; //定义dsn以及相关的数据 $dsn = 'mysql:host=localhost;dbname=hwibs_ ...
- Android判断当前的android设备是否处于联网状态
首先,要想获得当前android设备是否处于联网状态,那么android本身给我们提供了一个服务 private ConnectivityManager connectivityManager;//用 ...
- supplicant
概述 wpa_supplicant是wifi客户端(client)加密认证工具,和iwconfig不同,wpa_supplicant支持wep.wpa.wpa2等完整的加密认证,而iwconfig只能 ...
- C# DateTime详解
//今天DateTime.Now.Date.ToShortDateString();//昨天,就是今天的日期减一DateTime.Now.AddDays(-1).ToShortDateString() ...
- SQL Server数据库学习笔记-概念数据模型
概念数据模型(Conceptual Data Model)也称为信息模型.它是对客观事物及其联系的抽象,用于信息世界的建模,是现实世界到信息世界的第一层抽象,是数据库设计人员进行数据库设计的有力工具. ...
- java数据结构和算法------合并排序
package iYou.neugle.sort; public class Merge_sort { public static void MergeSort(double[] array, i ...