一、简介

WPF包含数据模板和控件模板,其中控件模板又包括ControlTemplate和ItemsPanelTemplate,这里讨论一下ControlTemplate。
其实WPF的每一个控件都有一个默认的模板,该模板描述了控件的外观以及外观对外界刺激所做出的反应。我们可以自定义一个模板来替换掉控件的默认模板以便打造个性化的控件。
与Style不同,Style只能改变控件的已有属性值(比如颜色字体)来定制控件,但控件模板可以改变控件的内部结构(VisualTree,视觉树)来完成更为复杂的定制,比如我们可以定制这样的按钮:在它的左半部分显示一个小图标而它的右半部分显示文本。
要替换控件的模板,我们只需要声明一个ControlTemplate对象,并对该ControlTemplate对象做相应的配置,然后将该ControlTemplate对象赋值给控件的Template属性就可以了。

二、ControlTemplate包含两个重要的属性

VisualTree,该模板的视觉树,其实我们就是使用这个属性来描述控件的外观的。
Triggers,触发器列表,里面包含一些触发器Trigger,我们可以定制这个触发器列表来使控件对外界的刺激发生反应,比如鼠标经过时文本变成粗体等。

三、实例

1、定制可视化树(VisualTree)

       <Button>
<Button.Template>
<ControlTemplate>
<!--定义视觉树-->
<Grid>
<Ellipse Name="faceEllipse"
Width="{TemplateBinding Button.Width}"
Height="{TemplateBinding Control.Height}"
Fill="{TemplateBinding Button.Background}"/>
<TextBlock Name="txtBlock"
Margin="{TemplateBinding Button.Padding}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Text="{TemplateBinding Button.Content}" />
</Grid>
<!--定义视觉树_end-->
</ControlTemplate>
</Button.Template>
comtroltemplate_test
</Button>

代码效果:

在上面的代码中,我们修改了Button的Template属性,我们定义了一个ControlTemplate,在<ControlTemplate> ... </ControlTemplate>之间包含的是模板的视觉树,也就是如何显示控件的外观,我们这里使用了一个Ellipse(椭圆)和一个TextBlock(文本块)来定义控件的外观。
很容易联想到一个问题:控件(Button)的一些属性,比如高度、宽度、文本等如何在新定义的外观中表现出来呢?
我们使用TemplateBinding 将控件的属性与新外观中的元素的属性关联起来Width="{TemplateBinding Button.Width}" ,这样我们就使得椭圆的宽度与按钮的宽度绑定在一起而保持一致,同理我们使用Text="{TemplateBinding Button.Content}"将TextBlock的文本与按钮的Content属性绑定在一起。

2、定制触发器Triggers

    <Button Grid.Row="1" Grid.Column="0" Margin="10">
<Button.Template>
<ControlTemplate>
<!--定义视觉树-->
<Grid>
<Ellipse Name="faceEllipse"
Width="{TemplateBinding Button.Width}"
Height="{TemplateBinding Control.Height}"
Fill="{TemplateBinding Button.Background}"/>
<TextBlock Name="txtBlock"
Margin="{TemplateBinding Button.Padding}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Text="{TemplateBinding Button.Content}" />
</Grid>
<!--定义视觉树_end-->
<!--定义触发器-->
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Button.Foreground" Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
<!--定义触发器_End-->
</ControlTemplate>
</Button.Template>
comtroltemplate_test
</Button>

代码效果:

在上面的代码中注意到<ControlTemplate. Triggers> ... </ControlTemplate. Triggers> 之间的部分,我们定义了触发器 <Trigger Property="Button.IsMouseOver" Value="True">,其表示当我们Button的IsMouseIOver属性变成True时,将使用设置器<Setter Property="Button. Foreground" Value="Red" /> 来将Button的Foreground属性设置为Red。这里有一个隐含的意思是:当Button的IsMouseIOver属性变成False时,设置器中设置的属性将回复原值。

3、ControlTemplate的重用

你只需要将模板定义为资源,其实大多数情况下,我们也是这样做的。

<Window.Resources>
<ControlTemplate TargetType="Button" x:Key="ButtonTemplate">
<!--定义视觉树-->
<Grid>
<Ellipse Name="faceEllipse"
Width="{TemplateBinding Button.Width}"
Height="{TemplateBinding Control.Height}"
Fill="{TemplateBinding Button.Background}"/>
<TextBlock Name="txtBlock"
Margin="{TemplateBinding Button.Padding}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Text="{TemplateBinding Button.Content}" />
</Grid>
<!--定义视觉树_end-->
<!--定义触发器-->
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Button.Foreground" Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
<!--定义触发器_End-->
</ControlTemplate>
</Window.Resources> 

上面的代码将我们原来的模板定义为窗体范围内的资源,其中TargetType="Button"指示我们的模板作用对象为Button,其中的ButtonTemplate是我们定义的模板的x:Key。
这样在整个窗体范围内的按钮都可以使用这个模板了,模板的使用方法也很简单:

<Button Grid.Column="0" Template="{StaticResource ButtonTemplate}" Content="comtroltemplate_test"/>
<Button Grid.Column="1" Template="{StaticResource ButtonTemplate}" Content="test btn2" />
<Button Grid.Column="2" Template="{StaticResource ButtonTemplate}" Content="test btn3" />

代码效果:

4、在trigger中使用storyboard,事件响应中添加的动画效果

<Window.Resources>
<ControlTemplate TargetType="Button" x:Key="ButtonTemplate">
<!--定义视觉树-->
<Grid>
<Ellipse Name="faceEllipse"
Width="{TemplateBinding Button.Width}"
Height="{TemplateBinding Control.Height}"
Fill="{TemplateBinding Button.Background}"/>
<TextBlock Name="txtBlock"
Margin="{TemplateBinding Button.Padding}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Text="{TemplateBinding Button.Content}" />
</Grid>
<!--定义视觉树_end-->
<!--定义动画资源-->
<ControlTemplate.Resources>
<Storyboard x:Key="MouseClickStoryboard">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="faceEllipse" Storyboard.TargetProperty="Width" BeginTime="00:00:00">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="50"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.3" Value="100"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<!--定义动画资源_end-->
<!--定义触发器-->
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Button.Foreground" Value="Red" />
</Trigger>
<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="faceEllipse">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResourceMouseClickStoryboard}"/>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="txtBlock">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource MouseClickStoryboard}"/>
</EventTrigger.Actions>
</EventTrigger>
</ControlTemplate.Triggers>
<!--定义触发器_End-->
</ControlTemplate>
</Window.Resources>

四、获取.Net中原生控件的的默认ControlTemplate

我们知道每个控件都有自己默认的模板,这是MS编写的,如果我们能够得到这些模板的XAML代码,那么它将是学习模板的最好的示例,
要想获得某个控件ctrl的默认模板,请调用以下方法:

string GetTemplateXamlCode(Control ctrl)
{
string xaml = "no template";
FrameworkTemplate template = ctrl.Template;
if (template != null)
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = new string(' ', );
settings.NewLineOnAttributes = true; StringBuilder strbuild = new StringBuilder();
XmlWriter xmlwrite = XmlWriter.Create(strbuild, settings); try
{
XamlWriter.Save(template, xmlwrite);
xaml = strbuild.ToString();
}
catch (Exception exc)
{
xaml = exc.Message;
}
} return xaml;
}

[No0000DA]WPF ControlTemplate简介的更多相关文章

  1. 深入详解WPF ControlTemplate

    WPF包含数据模板和控件模板,其中控件模板又包括ControlTemplate和ItemsPanelTemplate,这里讨论一下WPF ControlTemplate. 其实WPF的每一个控件都有一 ...

  2. Wpf 数据绑定简介、实例1

    简介:1.WPF绑定使用的源属性必须是依赖项属性,这是因为依赖项属性具有内置的更改通知支持,元素绑定表达式使用了Xaml扩展标记, WPF绑定一个控件是使用Binding.ElementName, 绑 ...

  3. WPF ControlTemplate

    ControlTemplate:控件模板,顾名思义也就是定制特定的控件供公共调用,有点类似WinForm中对一些通用控件进行重写使用. ControlTemplate:控件模板主要有两个重要属性:Vi ...

  4. WPF : ControlTemplate和DataTemplate的区别

    ControlTemplate用于描述控件本身. 使用TemplateBinding来绑定控件自身的属性, 比如{TemplateBinding Background}DataTemplate用于描述 ...

  5. WPF ControlTemplate 动画板 结束事件不触发

    解决此问题很简单 将Storyboard单独提取出来及可 给定Key名称,然后在触发器中的BeginStoryboard的storyboard绑定即可 <!--单独提取并设置Xkey--> ...

  6. WPF开发简介教程

    1/ VS中文件-新建-项目-WPF应用程序 2/ 左上角工具箱中有很多组件可以直接拖拽使用 3/ 双击组件,进入脚本功能编辑界面,如按钮: private void Button_Click_1(o ...

  7. WPF ControlTemplate,DataTemplate

    The Control Template defines the visual appearance of a control. All of the UI elements have some ki ...

  8. WPF DataTemplate與ControlTemplate

    一. 前言     什麼是DataTemplate? 什麼是ControlTemplate? 在stackoverflow有句簡短的解釋 "A DataTemplate, therefore ...

  9. wpf的控件style

    前段时间一直在做wpf的UI开发,每次想做些控件style定制的时候都很头疼 很多控件不知道他的controltemplate是什么样的 为了方便大家写style 特别奉上wpf的style大全 从此 ...

随机推荐

  1. Ubuntu 安装nginx

    https://www.nginx.com/resources/admin-guide/load-balancer/ https://github.com/gplessis/dotdeb-nginx/ ...

  2. Swift 静态派发和动态派发

    前言 方法是 Swift 中的一个重要概念,方法允许你把需要复用的代码封装进方法中,这样当你调用方法时,实际上你的想法是执行方法中的那些代码,方法的出现极大的提高了方法的复用性. Swift 工程的环 ...

  3. Socketserver 笔记

    引入Socketserver的背景: 我们之前使用socket编程的时候,Server端创建一个连接循环(建立连接)+一个通信循环(基于一次连接建立通信循环),(这里的黏包问题我们的实现方式是:我们在 ...

  4. [ci]jenkins server启动,通过jnlp的方式启动slave(容器模式)

    jenkins server启动,通过jnlp的方式启动slave. java -jar jenkins.jar 配置jnlp端口--全局安全 配置云 配置项目 执行成功

  5. 6-9-哈夫曼树(HuffmanTree)-树和二叉树-第6章-《数据结构》课本源码-严蔚敏吴伟民版

    课本源码部分 第6章  树和二叉树 - 哈夫曼树(HuffmanTree) ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接☛☛☛ <数据结构-C语言版> ...

  6. What-does-git-remote-and-origin-mean

    https://www.quora.com/What-does-git-remote-and-origin-mean https://stackoverflow.com/questions/29235 ...

  7. Tomcat配置多个域名绑定到不同项目

    <Host name="www.dn-serve.com" appBase="webapps" unpackWARs="true" a ...

  8. Java知多少(22)方法重载

    在Java中,同一个类中的多个方法可以有相同的名字,只要它们的参数列表不同就可以,这被称为方法重载(method overloading). 参数列表又叫参数签名,包括参数的类型.参数的个数和参数的顺 ...

  9. linux mysql卸载

    卸载mysql 1.查找以前是否装有mysql 命令:rpm -qa|grep -i mysql 可以看到mysql的包: mysql-libs-5.1.71-1.el6.x86_64 2.删除mys ...

  10. [PGM] Temporal Models

    这里的一些东西只是将过去已有的东西用PGM解释了一遍,但优势还是明显的,对整体认识有帮助. Video: https://www.youtube.com/watch?v=ogs4Oj8KahQ& ...