原文地址 http://www.cnblogs.com/zhouyinhui/archive/2007/03/28/690993.html

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;
        }

WPF中的ControlTemplate(控件模板)(转)的更多相关文章

  1. WPF中的ControlTemplate(控件模板)

    原文:WPF中的ControlTemplate(控件模板) WPF中的ControlTemplate(控件模板)                                             ...

  2. [转]WPF中的ControlTemplate(控件模板)

    WPF中的ControlTemplate(控件模板)                                                                           ...

  3. WPF 中动态改变控件模板

    在某些项目中,可能需要动态的改变控件的模板,例如软件中可以选择不同的主题,在不同的主题下软件界面.控件的样式都会有所不同,这时即可通过改变控件模板的方式实现期望的功能. 基本方法是当用户点击切换主题按 ...

  4. WPF基础篇之控件模板(ControlTemplate)

    WPF中每一个控件都有一个默认的模板,该模板描述了控件的外观以及外观对外界刺激所做出的反应.我们可以自定义一个模板来替换掉控件的默认模板以便打造个性化的控件. 与Style不同,Style只能改变控件 ...

  5. Xamarin XAML语言教程构建ControlTemplate控件模板 (四)

    Xamarin XAML语言教程构建ControlTemplate控件模板 (四) 2.在页面级别中构建控件模板 如果开发者要在页面级别中构建控件模板,首先必须将ResourceDictionary添 ...

  6. Xamarin XAML语言教程构建ControlTemplate控件模板 (二)

    Xamarin XAML语言教程构建ControlTemplate控件模板 (二) (2)打开MainPage.xaml文件,编写代码,将构建的控件模板应用于ContentView中.代码如下: &l ...

  7. Xamarin XAML语言教程构建ControlTemplate控件模板

    Xamarin XAML语言教程构建ControlTemplate控件模板 控件模板ControlTemplate ControlTemplate是从Xamarin.Forms 2.1.0开始被引入的 ...

  8. 在WPF中使用WinForm控件方法

    1.      首先添加对如下两个dll文件的引用:WindowsFormsIntegration.dll,System.Windows.Forms.dll. 2.      在要使用WinForm控 ...

  9. WPF中的image控件的Source赋值

    WPF中的Image控件Source的设置 1.XAML中 简单的方式(Source="haha.png"); image控件的Source设置为相对路径后(Source=&quo ...

随机推荐

  1. 聊聊Dataguard的三种保护模式实验(上)

    Data Guard是Oracle高可用性HA的重要解决方案.针对不同的系统保护需求,DG提供了三种不同类型的保护模式(Protection Mode),分别为:最大保护(Maximum Protec ...

  2. 【ASP.NET Web API教程】1.1 第一个ASP.NET Web API

    Your First ASP.NET Web API (C#)第一个ASP.NET Web API(C#) By Mike Wasson|January 21, 2012作者:Mike Wasson ...

  3. view的onFinishInflate()何时调用的?

    onFinishInflate 当View中所有的子控件均被映射成xml后触发 比如你 自定义一个view叫myView ,路径是,com.test.view.MyView,此view是继承Linea ...

  4. nginx服务器防sql注入/溢出攻击/spam及禁User-agents

    本文章给大家介绍一个nginx服务器防sql注入/溢出攻击/spam及禁User-agents实例代码,有需要了解的朋友可进入参考. 在配置文件添加如下字段即可  代码如下 复制代码 server { ...

  5. Loadrunner脚本之C语言文件处理函数

    一.打开文件 打开文件使用库函数中的fopen函数.fopen函数会为要打开的文件新建一个流,然后返回一个指向file型对象的指针,该file型对象中保存了控制这个流所需要的信息. fp=fopen( ...

  6. Vi的基本使用方法

    转载自http://linux.chinaunix.net/doc/office/2005-01-24/898.shtml vi编辑器是所有Unix及Linux系统下标准的编辑器,它的强大不逊色于任何 ...

  7. Cloud_panel

    传统基础架构应用程序的系统架构师,云计算应用程序的设计确实是相当有挑战性的工作.体现在应用程序架构师首先要了解云计算环境和传统基础架构的差异并且充分利用云计算平台的一些特点来更好的满足用户需求. 对于 ...

  8. Hadoop MapReduce概念学习系列之mr程序组件全貌(二十)

    其实啊,spilt是,控制Apache Hadoop Mapreduce的map并发任务数,详细见http://www.cnblogs.com/zlslch/p/5713652.html map,是m ...

  9. 【Maven】Maven下载源码和Javadoc的方法

    1:Maven命令下载源码和javadocs 当在IDE中使用Maven时如果想要看引用的jar包中类的源码和javadoc需要通过maven命令下载这些源码,然后再进行引入,通过mvn命令能够容易的 ...

  10. Step By Step(Lua字符串库) (转)

    1. 基础字符串函数:    字符串库中有一些函数非常简单,如:    1). string.len(s) 返回字符串s的长度:    2). string.rep(s,n) 返回字符串s重复n次的结 ...