[转]WPF中的ControlTemplate(控件模板)
WPF中的ControlTemplate(控件模板)
周银辉
WPF包含数据模板和控件模板,其中控件模板又包括ControlTemplate和ItemsPanelTemplate,这里讨论一下ControlTemplate。
其实WPF的每一个控件都有一个默认的模板,该模板描述了控件的外观以及外观对外界刺激所做出的反应。我们可以自定义一个模板来替换掉控件的默认模板以便打造个性化的控件。
与Style不同,Style只能改变控件的已有属性值(比如颜色字体)来定制控件,但控件模板可以改变控件的内部结构(VisualTree,视觉树)来完成更为复杂的定制,比如我们可以定制这样的按钮:在它的左办部分显示一个小图标而它的右半部分显示文本。
要替换控件的模板,我们只需要声明一个ControlTemplate对象,并对该ControlTemplate对象做相应的配置,然后将该ControlTemplate对象赋值给控件的Template属性就可以了。
ControlTemplate包含两个重要的属性:
1,VisualTree,该模板的视觉树,其实我们就是使用这个属性来描述控件的外观的
2,Triggers,触发器列表,里面包含一些触发器Trigger,我们可以定制这个触发器列表来使控件对外界的刺激发生反应,比如鼠标经过时文本变成粗体等。
参考以下代码
<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>
</Button>在上面的代码中,我们修改了Button的Template属性,我们定义了一个ControlTemplate,在<ControlTemplate> ... </ControlTemplate>之间包含的是模板的视觉树,也就是如何显示控件的外观,我们这里使用了一个Ellipse(椭圆)和一个TextBlock(文本块)来定义控件的外观。
很容易联想到一个问题:控件(Button)的一些属性,比如高度、宽度、文本等如何在新定义的外观中表现出来呢?
我们使用TemplateBinding 将控件的属性与新外观中的元素的属性关联起来Width="{TemplateBinding Button.Width}" ,这样我们就使得椭圆的宽度与按钮的宽度绑定在一起而保持一致,同理我们使用Text="{TemplateBinding Button.Content}"将TextBlock的文本与按钮的Content属性绑定在一起。
除了定义控件的默认外观外,也许我们想还定义当外界刺激我们的控件时,控件外观做出相应的变化,这是我们需要触发器。参考以下代码:
<Button Content="test btn" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1" >
<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>
</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时,设置器中设置的属性将回复原值。
你可以粘贴以下代码到XamlPad查看效果:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ControlTemplateTest" Height="300" Width="300"
>
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*"/>
<ColumnDefinition Width="0.6*"/>
<ColumnDefinition Width="0.2*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.4*"/>
</Grid.RowDefinitions>
<Button Content="test btn" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1" >
<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>
</Button>
</Grid>
</Window>
接下来的一个问题是:如果我要重用我的模板,应该怎么办呢?
你需要将模板定义为资源,其实大多数情况下,我们也是这样做的
参考以下代码:
<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,这样在整个窗体范围内的按钮都可以使用这个模板了,模板的使用方法也很简单:
<Button Content="test btn" Template="{StaticResource ButtonTemplate}" />其中的ButtonTemplate是我们定义的模板的x:Key
你可以粘贴以下代码到XamlPad查看效果:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ControlTemplateTest" Height="300" Width="300"
>
<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>
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*"/>
<ColumnDefinition Width="0.6*"/>
<ColumnDefinition Width="0.2*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.4*"/>
</Grid.RowDefinitions>
<Button Content="test btn1" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="1" />
<Button Content="test btn2" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1" Template="{StaticResource ButtonTemplate}" />
<Button Content="test btn2" Grid.Column="2" Grid.ColumnSpan="1" Grid.Row="2" Grid.RowSpan="1" Template="{StaticResource ButtonTemplate}" />
</Grid>
</Window>
额外提一下的是,我们也可以在触发器中,调用一个故事板来达到对事件响应时的动画效果
参考以下代码
<!--定义动画资源-->
<ControlTemplate.Resources>
<Storyboard x:Key="MouseClickButtonStoryboard">
<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>我们为模板定义了一个动画资源,此后在模板的触发器中我们就可以调用该资源来实现一个动画效果了:
<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="faceEllipse">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource MouseClickButtonStoryboard}"/>
</EventTrigger.Actions>
</EventTrigger>你可以粘贴以下代码到XamlPad查看效果:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ControlTemplateTest" Height="300" Width="300"
>
<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="MouseClickButtonStoryboard">
<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="{StaticResource MouseClickButtonStoryboard}"/>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="txtBlock">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource MouseClickButtonStoryboard}"/>
</EventTrigger.Actions>
</EventTrigger>
</ControlTemplate.Triggers>
<!--定义触发器_End-->
</ControlTemplate>

</Window.Resources>
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*"/>
<ColumnDefinition Width="0.6*"/>
<ColumnDefinition Width="0.2*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.4*"/>
</Grid.RowDefinitions>
<Button Content="test btn1" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="1" />
<Button Content="test btn2" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1" Template="{StaticResource ButtonTemplate}" />
<Button Content="test btn2" Grid.Column="2" Grid.ColumnSpan="1" Grid.Row="2" Grid.RowSpan="1" Template="{StaticResource ButtonTemplate}" />
</Grid>
</Window>
最好的模板示例:我们知道每个控件都有自己默认的模板,这是MS编写的,如果我们能够得到这些模板的XAML代码,那么它将是学习模板的最好的示例,
要想获得某个控件ctrl的默认模板,请调用以下方法:
string GetTemplateXamlCode(Control ctrl)
{
FrameworkTemplate template = ctrl.Template;
string xaml = "";
if (template != null)
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = new string(' ', 4);
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;
}
}
else
{
xaml = "no template";
}
return xaml;
}文章来源:http://www.cnblogs.com/zhouyinhui/category/86467.html
[转]WPF中的ControlTemplate(控件模板)的更多相关文章
- WPF中的ControlTemplate(控件模板)(转)
原文地址 http://www.cnblogs.com/zhouyinhui/archive/2007/03/28/690993.html WPF中的ControlTemplate(控件模板) ...
- WPF中的ControlTemplate(控件模板)
原文:WPF中的ControlTemplate(控件模板) WPF中的ControlTemplate(控件模板) ...
- WPF 中动态改变控件模板
在某些项目中,可能需要动态的改变控件的模板,例如软件中可以选择不同的主题,在不同的主题下软件界面.控件的样式都会有所不同,这时即可通过改变控件模板的方式实现期望的功能. 基本方法是当用户点击切换主题按 ...
- WPF基础篇之控件模板(ControlTemplate)
WPF中每一个控件都有一个默认的模板,该模板描述了控件的外观以及外观对外界刺激所做出的反应.我们可以自定义一个模板来替换掉控件的默认模板以便打造个性化的控件. 与Style不同,Style只能改变控件 ...
- Xamarin XAML语言教程构建ControlTemplate控件模板 (四)
Xamarin XAML语言教程构建ControlTemplate控件模板 (四) 2.在页面级别中构建控件模板 如果开发者要在页面级别中构建控件模板,首先必须将ResourceDictionary添 ...
- Xamarin XAML语言教程构建ControlTemplate控件模板 (二)
Xamarin XAML语言教程构建ControlTemplate控件模板 (二) (2)打开MainPage.xaml文件,编写代码,将构建的控件模板应用于ContentView中.代码如下: &l ...
- Xamarin XAML语言教程构建ControlTemplate控件模板
Xamarin XAML语言教程构建ControlTemplate控件模板 控件模板ControlTemplate ControlTemplate是从Xamarin.Forms 2.1.0开始被引入的 ...
- 在WPF中使用WinForm控件方法
1. 首先添加对如下两个dll文件的引用:WindowsFormsIntegration.dll,System.Windows.Forms.dll. 2. 在要使用WinForm控 ...
- WPF中的image控件的Source赋值
WPF中的Image控件Source的设置 1.XAML中 简单的方式(Source="haha.png"); image控件的Source设置为相对路径后(Source=&quo ...
随机推荐
- Let's Encrypt 安装配置教程,免费的 SSL 证书
官网:https://letsencrypt.org/ 安装Let's Encrypt 安装非常简单直接克隆就可以了 git clone https://github.com/letsencrypt/ ...
- mvc和mvvm区别
mvc和mvvm区别 MVC和MVVM的区别其实并不大.都是一种设计思想. 主要就是MVC中Controller演变成MVVM中的viewModel. MVVM主要解决了MVC中大量的DOM操作使页面 ...
- 出现不不能引java.util.Date包的情况
出现不不能引java.util.Date包的情况 那个时间段不能引,IDE的bug,等一会儿就好了 心得:很多时候没必要和bug死磕,因为真的不是你的问题.
- VS2010-MFC(MFC常用类:CString类)
转自:http://www.jizhuomi.com/software/228.html CString类简介 CString类作为MFC的常用类,当之无愧.可以这样说,只要是从事MFC开发,基本都会 ...
- Spark 读写数据、抽象转换 拾遗
读 package com.test.spark import org.apache.spark.sql.{Dataset, Row, SaveMode, SparkSession} /** * @a ...
- python字典的常用操作,数据类型划分
一.数据类型划分之一 可分为:可变数据类型,不可变数据类型 不可变数据类型:元祖,布尔值(Bool),数值 int ,字符串 str 可哈希 可变数据类型: list,d ...
- virtualbox虚拟机下的cdlinux找不到无线网卡的解决方法
virtualbox虚拟机下的cdlinux找不到无线网卡的解决方法 自己解决了,记录一下. cdlinux 带reaver1.4的版本 http://pan.baidu.com/share/link ...
- spark 应用场景1-求年龄平均值
原文引自:http://blog.csdn.net/fengzhimohan/article/details/78535143 该案例中,我们将假设我们需要统计一个 10 万人口的所有人的平均年龄,当 ...
- php断点续传之文件上传与文件下载
下载: 原理: Http头的 Range.Content-Range()未启用单点登录GerenalRequest URL: http://www.demotest.com/php/fileDownl ...
- linux sudo命令失败 提示sudo:/usr/bin/sudo 必须属于用户 ID 0(的用户)并且设置 setuid 位
sudo:/usr/bin/sudo 必须属于用户 ID 0(的用户)并且设置 setuid 位 一.前言 这是一个神奇的错误,缘由是因为有人将/usr/bin/sudo的权限改为777或其他. 解决 ...