WPF中的ControlTemplate(控件模板)

                                                                 WPF中的ControlTemplate(控件模板)
                                                                                                                        周银辉

WPF包含数据模板和控件模板,其中控件模板又包括ControlTemplate和ItemsPanelTemplate,这里讨论一下ControlTemplate。

其实WPF的每一个控件都有一个默认的模板,该模板描述了控件的外观以及外观对外界刺激所做出的反应。我们可以自定义一个模板来替换掉控件的默认模板以便打造个性化的控件。

与Style不同,Style只能改变控件的已有属性值(比如颜色字体)来定制控件,但控件模板可以改变控件的内部结构( VisualTree,视觉树)来完成更为复杂的定制,比如我们可以定制这样的按钮:在它的左办部分显示一个小图标而它的右半部分显示文本。

要替换控件的模板,我们只需要声明一个ControlTemplate对象,并对该ControlTemplate对象做相应的配置,然后将该ControlTemplate对象赋值给控件的Template属性就可以了。

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

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

除了定义控件的默认外观外,也许我们想还定义当外界刺激我们的控件时,控件外观做出相应的变化,这是我们需要触发器。参考以下代码:
None.gif < Button  Content ="test btn"  Grid.Column ="1"  Grid.ColumnSpan ="1"  Grid.Row ="1"  Grid.RowSpan ="1"    >
None.gif        
< Button .Template >
None.gif          
< ControlTemplate >
None.gif            
<!-- 定义视觉树 -->
None.gif            
< Grid >
None.gif              
< Ellipse  Name ="faceEllipse"  Width ="{TemplateBinding Button.Width}"  Height ="{TemplateBinding Control.Height}"   Fill ="{TemplateBinding Button.Background}" />
None.gif              
< TextBlock  Name ="txtBlock"  Margin ="{TemplateBinding Button.Padding}"  VerticalAlignment ="Center"   HorizontalAlignment ="Center"   Text ="{TemplateBinding Button.Content}"   />
None.gif            
</ Grid >
None.gif            
<!-- 定义视觉树_end -->
None.gif            
<!-- 定义触发器 -->
None.gif            
< ControlTemplate .Triggers >
None.gif              
< Trigger   Property ="Button.IsMouseOver"   Value ="True" >
None.gif                
< Setter  Property ="Button.Foreground"  Value ="Red"   />                
None.gif              
</ Trigger >
None.gif            
</ ControlTemplate.Triggers >
None.gif            
<!-- 定义触发器_End -->
None.gif          
</ ControlTemplate >
None.gif        
</ Button.Template >
None.gif      
</ 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查看效果:
None.gif < Window 
None.gif    
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
None.gif    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
None.gif    Title
="ControlTemplateTest"  Height ="300"  Width ="300"
None.gif    
>
None.gif    
< Grid  ShowGridLines ="True" >
None.gif      
None.gif      
< Grid .ColumnDefinitions >
None.gif        
< ColumnDefinition  Width ="0.2*" />
None.gif        
< ColumnDefinition  Width ="0.6*" />
None.gif        
< ColumnDefinition  Width ="0.2*" />
None.gif      
</ Grid.ColumnDefinitions >
None.gif      
< Grid .RowDefinitions >
None.gif        
< RowDefinition  Height ="0.3*" />
None.gif        
< RowDefinition  Height ="0.3*" />
None.gif        
< RowDefinition  Height ="0.4*" />
None.gif      
</ Grid.RowDefinitions >
None.gif
None.gif      
< Button  Content ="test btn"  Grid.Column ="1"  Grid.ColumnSpan ="1"  Grid.Row ="1"  Grid.RowSpan ="1"    >
None.gif        
< Button .Template >
None.gif          
< ControlTemplate >
None.gif            
<!-- 定义视觉树 -->
None.gif            
< Grid >
None.gif              
< Ellipse  Name ="faceEllipse"  Width ="{TemplateBinding Button.Width}"  Height ="{TemplateBinding Control.Height}"   Fill ="{TemplateBinding Button.Background}" />
None.gif              
< TextBlock  Name ="txtBlock"  Margin ="{TemplateBinding Button.Padding}"  VerticalAlignment ="Center"   HorizontalAlignment ="Center"   Text ="{TemplateBinding Button.Content}"   />
None.gif            
</ Grid >
None.gif            
<!-- 定义视觉树_end -->
None.gif            
<!-- 定义触发器 -->
None.gif            
< ControlTemplate .Triggers >
None.gif              
< Trigger   Property ="Button.IsMouseOver"   Value ="True" >
None.gif                
< Setter  Property ="Button.Foreground"  Value ="Red"   />                
None.gif              
</ Trigger >
None.gif            
</ ControlTemplate.Triggers >
None.gif            
<!-- 定义触发器_End -->
None.gif          
</ ControlTemplate >
None.gif        
</ Button.Template >
None.gif      
</ Button >
None.gif        
None.gif    
</ Grid >
None.gif
</ Window >
None.gif


接下来的一个问题是:如果我要重用我的模板,应该怎么办呢?
你需要将模板定义为资源,其实大多数情况下,我们也是这样做的
参考以下代码:
None.gif    < Window .Resources >
None.gif    
< ControlTemplate  TargetType ="Button"  x:Key ="ButtonTemplate" >
None.gif        
<!-- 定义视觉树 -->
None.gif        
< Grid >
None.gif          
< Ellipse  Name ="faceEllipse"  Width ="{TemplateBinding Button.Width}"  Height ="{TemplateBinding Control.Height}"   Fill ="{TemplateBinding Button.Background}" />
None.gif          
< TextBlock  Name ="txtBlock"  Margin ="{TemplateBinding Button.Padding}"  VerticalAlignment ="Center"   HorizontalAlignment ="Center"   Text ="{TemplateBinding Button.Content}"   />
None.gif        
</ Grid >
None.gif        
<!-- 定义视觉树_end -->
None.gif        
<!-- 定义触发器 -->
None.gif        
< ControlTemplate .Triggers >
None.gif          
< Trigger   Property ="Button.IsMouseOver"   Value ="True" >
None.gif            
< Setter  Property ="Button.Foreground"  Value ="Red"   />
None.gif          
</ Trigger >
None.gif        
</ ControlTemplate.Triggers >
None.gif        
<!-- 定义触发器_End -->  
None.gif    
</ ControlTemplate >
None.gif  
</ Window.Resources >
None.gif  
上面的代码将我们原来的模板定义为窗体范围内的资源,其中 TargetType ="Button"指示我们的模板作用对象为Button,这样在整个窗体范围内的按钮都可以使用这个模板了,模板的使用方法也很简单:
None.gif < Button  Content ="test btn"  Template ="{StaticResource ButtonTemplate}"   />
其中的ButtonTemplate是我们定义的模板的x:Key

你可以粘贴以下代码到XamlPad查看效果:
None.gif < Window
None.gif    
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
None.gif    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
None.gif    Title
="ControlTemplateTest"  Height ="300"  Width ="300"
None.gif    
>
None.gif
None.gif  
< Window .Resources >
None.gif    
< ControlTemplate  TargetType ="Button"  x:Key ="ButtonTemplate" >
None.gif        
<!-- 定义视觉树 -->
None.gif        
< Grid >
None.gif          
< Ellipse  Name ="faceEllipse"  Width ="{TemplateBinding Button.Width}"  Height ="{TemplateBinding Control.Height}"   Fill ="{TemplateBinding Button.Background}" />
None.gif          
< TextBlock  Name ="txtBlock"  Margin ="{TemplateBinding Button.Padding}"  VerticalAlignment ="Center"   HorizontalAlignment ="Center"   Text ="{TemplateBinding Button.Content}"   />
None.gif        
</ Grid >
None.gif        
<!-- 定义视觉树_end -->
None.gif        
<!-- 定义触发器 -->
None.gif        
< ControlTemplate .Triggers >
None.gif          
< Trigger   Property ="Button.IsMouseOver"   Value ="True" >
None.gif            
< Setter  Property ="Button.Foreground"  Value ="Red"   />
None.gif          
</ Trigger >
None.gif        
</ ControlTemplate.Triggers >
None.gif        
<!-- 定义触发器_End -->  
None.gif    
</ ControlTemplate >
None.gif  
</ Window.Resources >
None.gif  
None.gif  
None.gif    
< Grid  ShowGridLines ="True" >
None.gif      
None.gif      
< Grid .ColumnDefinitions >
None.gif        
< ColumnDefinition  Width ="0.2*" />
None.gif        
< ColumnDefinition  Width ="0.6*" />
None.gif        
< ColumnDefinition  Width ="0.2*" />
None.gif      
</ Grid.ColumnDefinitions >
None.gif      
< Grid .RowDefinitions >
None.gif        
< RowDefinition  Height ="0.3*" />
None.gif        
< RowDefinition  Height ="0.3*" />
None.gif        
< RowDefinition  Height ="0.4*" />
None.gif      
</ Grid.RowDefinitions >
None.gif
None.gif      
< Button  Content ="test btn1"  Grid.Column ="0"  Grid.ColumnSpan ="1"  Grid.Row ="0"  Grid.RowSpan ="1"    />
None.gif      
< Button  Content ="test btn2"  Grid.Column ="1"  Grid.ColumnSpan ="1"  Grid.Row ="1"  Grid.RowSpan ="1"   Template ="{StaticResource ButtonTemplate}"   />
None.gif      
< Button  Content ="test btn2"  Grid.Column ="2"  Grid.ColumnSpan ="1"  Grid.Row ="2"  Grid.RowSpan ="1"   Template ="{StaticResource ButtonTemplate}"   />
None.gif        
None.gif        
None.gif    
</ Grid >
None.gif
</ Window >
None.gif

额外提一下的是,我们也可以在触发器中,调用一个故事板来达到对事件响应时的动画效果
参考以下代码
None.gif   <!-- 定义动画资源 -->
None.gif      
< ControlTemplate .Resources >
None.gif        
< Storyboard  x:Key ="MouseClickButtonStoryboard" >
None.gif          
< DoubleAnimationUsingKeyFrames  Storyboard.TargetName ="faceEllipse"  Storyboard.TargetProperty ="Width"  BeginTime ="00:00:00" >
None.gif            
< SplineDoubleKeyFrame  KeyTime ="00:00:00"  Value ="50" />
None.gif            
< SplineDoubleKeyFrame  KeyTime ="00:00:00.3"  Value ="100" />
None.gif          
</ DoubleAnimationUsingKeyFrames >
None.gif        
</ Storyboard >
None.gif      
</ ControlTemplate.Resources >
我们为模板定义了一个动画资源,此后在模板的触发器中我们就可以调用该资源来实现一个动画效果了:
None.gif         < EventTrigger  RoutedEvent ="Mouse.MouseDown"  SourceName ="faceEllipse" >
None.gif          
< EventTrigger .Actions >
None.gif            
< BeginStoryboard  Storyboard ="{StaticResource MouseClickButtonStoryboard}" />
None.gif          
</ EventTrigger.Actions >     
None.gif        
</ EventTrigger >
你可以粘贴以下代码到XamlPad查看效果:
None.gif < Window 
None.gif    
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
None.gif    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
None.gif    Title
="ControlTemplateTest"  Height ="300"  Width ="300"
None.gif    
>
None.gif
None.gif  
< Window .Resources >
None.gif    
< ControlTemplate  TargetType ="Button"  x:Key ="ButtonTemplate" >
None.gif      
<!-- 定义视觉树 -->
None.gif      
< Grid >
None.gif        
< Ellipse  Name ="faceEllipse"  Width ="{TemplateBinding Button.Width}"  Height ="{TemplateBinding Control.Height}"   Fill ="{TemplateBinding Button.Background}" />
None.gif        
< TextBlock  Name ="txtBlock"  Margin ="{TemplateBinding Button.Padding}"  VerticalAlignment ="Center"   HorizontalAlignment ="Center"   Text ="{TemplateBinding Button.Content}"   />
None.gif      
</ Grid >
None.gif      
<!-- 定义视觉树_end -->
None.gif
None.gif      
<!-- 定义动画资源 -->
None.gif      
< ControlTemplate .Resources >
None.gif        
< Storyboard  x:Key ="MouseClickButtonStoryboard" >
None.gif          
< DoubleAnimationUsingKeyFrames  Storyboard.TargetName ="faceEllipse"  Storyboard.TargetProperty ="Width"  BeginTime ="00:00:00" >
None.gif            
< SplineDoubleKeyFrame  KeyTime ="00:00:00"  Value ="50" />
None.gif            
< SplineDoubleKeyFrame  KeyTime ="00:00:00.3"  Value ="100" />
None.gif          
</ DoubleAnimationUsingKeyFrames >
None.gif        
</ Storyboard >
None.gif      
</ ControlTemplate.Resources >
None.gif      
<!-- 定义动画资源_end -->
None.gif
None.gif      
<!-- 定义触发器 -->
None.gif      
< ControlTemplate .Triggers >
None.gif        
< Trigger   Property ="Button.IsMouseOver"   Value ="True" >
None.gif          
< Setter  Property ="Button.Foreground"  Value ="Red"   />
None.gif        
</ Trigger >
None.gif        
< EventTrigger  RoutedEvent ="Mouse.MouseDown"  SourceName ="faceEllipse" >
None.gif          
< EventTrigger .Actions >
None.gif            
< BeginStoryboard  Storyboard ="{StaticResource MouseClickButtonStoryboard}" />
None.gif          
</ EventTrigger.Actions >     
None.gif        
</ EventTrigger >
None.gif        
< EventTrigger  RoutedEvent ="Mouse.MouseDown"  SourceName ="txtBlock" >
None.gif          
< EventTrigger .Actions >
None.gif            
< BeginStoryboard  Storyboard ="{StaticResource MouseClickButtonStoryboard}" />
None.gif          
</ EventTrigger.Actions >
None.gif        
</ EventTrigger >
None.gif      
</ ControlTemplate.Triggers >
None.gif      
<!-- 定义触发器_End -->
None.gif      
None.gif    
</ ControlTemplate >
None.gif
None.gif
None.gif   
None.gif  
</ Window.Resources >
None.gif  
None.gif  
None.gif    
< Grid  ShowGridLines ="True" >
None.gif      
None.gif      
< Grid .ColumnDefinitions >
None.gif        
< ColumnDefinition  Width ="0.2*" />
None.gif        
< ColumnDefinition  Width ="0.6*" />
None.gif        
< ColumnDefinition  Width ="0.2*" />
None.gif      
</ Grid.ColumnDefinitions >
None.gif      
< Grid .RowDefinitions >
None.gif        
< RowDefinition  Height ="0.3*" />
None.gif        
< RowDefinition  Height ="0.3*" />
None.gif        
< RowDefinition  Height ="0.4*" />
None.gif      
</ Grid.RowDefinitions >
None.gif
None.gif      
< Button  Content ="test btn1"  Grid.Column ="0"  Grid.ColumnSpan ="1"  Grid.Row ="0"  Grid.RowSpan ="1"    />
None.gif      
< Button  Content ="test btn2"  Grid.Column ="1"  Grid.ColumnSpan ="1"  Grid.Row ="1"  Grid.RowSpan ="1"   Template ="{StaticResource ButtonTemplate}"   />
None.gif      
< Button  Content ="test btn2"  Grid.Column ="2"  Grid.ColumnSpan ="1"  Grid.Row ="2"  Grid.RowSpan ="1"   Template ="{StaticResource ButtonTemplate}"   />
None.gif
None.gif        
None.gif    
</ Grid >
None.gif
</ Window >
None.gif


最好的模板示例:我们知道每个控件都有自己默认的模板,这是MS编写的,如果我们能够得到这些模板的XAML代码,那么它将是学习模板的最好的示例,
要想获得某个控件ctrl的默认模板,请调用以下方法:
None.gif string  GetTemplateXamlCode(Control ctrl)
ExpandedBlockStart.gifContractedBlock.gif        
dot.gif {
InBlock.gif
InBlock.gif            FrameworkTemplate template 
= ctrl.Template;
InBlock.gif
InBlock.gif            
string xaml = "";
InBlock.gif
InBlock.gif            
if (template != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
InBlock.gif                XmlWriterSettings settings 
= new XmlWriterSettings();
InBlock.gif                settings.Indent 
= true;
InBlock.gif                settings.IndentChars 
= new string(' '4);
InBlock.gif                settings.NewLineOnAttributes 
= true;
InBlock.gif
InBlock.gif                StringBuilder strbuild 
= new StringBuilder();
InBlock.gif                XmlWriter xmlwrite 
= XmlWriter.Create(strbuild, settings);
InBlock.gif
InBlock.gif                
try
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    XamlWriter.Save(template, xmlwrite);
InBlock.gif                    xaml 
= strbuild.ToString();
ExpandedSubBlockEnd.gif                }

InBlock.gif                
catch (Exception exc)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    xaml 
= exc.Message;
ExpandedSubBlockEnd.gif                }

ExpandedSubBlockEnd.gif            }

InBlock.gif            
else
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                xaml 
= "no template";
ExpandedSubBlockEnd.gif            }

InBlock.gif
InBlock.gif            
return xaml;
ExpandedBlockEnd.gif        }
















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值