• WPF Template模版之DataTemplate与ControlTemplate的关系和应用【二】


    1. DataTemplate和ControlTemplate的关系

        学习过DataTemplate和ControlTemplate,你应该已经体会到,控件只是数据的行为和载体,是个抽象的概念,至于它本身长成什么样子(控件内部结构),它的数据会长成什么样子(数据显示结构)都是靠Template生成的。决定控件外观的是ControlTemplate,决定数据外观的是DataTemplate,它们正是Control类的Template和ContentTemplate两个属性值

        凡是Template,最终都要作用在控件上,这个控件就是Template的目标控件,也叫模板化控件。你可能会问:DataTemplate的目标应该是数据呀,怎么会是控件呢。DataTemplate给人的感觉的确是施加在数据对象上,但施加在数据对象上生成的一组控件总得有个载体吧?这个载体一般落实在一个叫做ContentPresenter对象上。ContentPresenter类只有ContentTemplate属性、没有Template属性,这就证明了承载由DataTemplate生成的一组控件是他的专门用途。

        至此我们可以看出,由ControlTemplate生成的控件树其树根就是ControlTemplate的目标控件,此模板化控件的Template属性值就是一个ControlTemplate实例。与之相仿,由DataTemplate生成的控件其树根是一个ContentPresenter控件,此模板化控件的ContentTemplate属性值就是这个DataTemplate实例。因为ContentPresenter控件是ControlTemplate控件树上的一个节点,所以DataTemplate控件树是ControlTemplate里面的一个子树。

        显然,如果把数据对象赋值给ContentPresenter的DataContext属性,由DataTemplate生成的控件自然会找到这个数据对象并把它当作自己的数据源。

    2. 应用


    2.1 应用1

        

        为Template设置其应用目标有两种方法,一个是逐个设置控件的Template/ContentTemplate/ItemTemlate/CellTemplate等属性,不想应用Template的控件不设置;另一种是整体应用,即把Template应用到某个类型的控件或者数据上。
    把ControlTemplate应用到所有控件上需要借助Style来实现,但Style不能标记X:KEY,例如下面的代码:

    [html] view plain copy
     
     print?
    1. <Window x:Class="WpfApplication11.wnd11421"  
    2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
    4.         Title="wnd11421" Height="200" Width="300">  
    5.     <Window.Resources>  
    6.         <!--ControlTemplate作用在所有目标控件上,Style不能标记x:key-->  
    7.         <Style TargetType="{x:Type TextBox}">  
    8.             <Setter Property="Template">  
    9.                 <Setter.Value>  
    10.                     <!--使用TemplateBinding,与模版目标一致-->  
    11.                     <ControlTemplate TargetType="{x:Type TextBox}">  
    12.                         <Border SnapsToDevicePixels="True"  
    13.                                 Background="{TemplateBinding Background}"  
    14.                                 BorderBrush="{TemplateBinding BorderBrush}"  
    15.                                 BorderThickness="{TemplateBinding BorderThickness}"  
    16.                                 CornerRadius="5">  
    17.                             <ScrollViewer SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"></ScrollViewer>  
    18.                         </Border>  
    19.                     </ControlTemplate>  
    20.                 </Setter.Value>  
    21.             </Setter>  
    22.             <Setter Property="Margin" Value="5"></Setter>  
    23.             <Setter Property="BorderBrush" Value="Black"></Setter>  
    24.             <Setter Property="Height" Value="28"></Setter>  
    25.         </Style>  
    26.     </Window.Resources>  
    27.     <StackPanel>  
    28.         <TextBox></TextBox>  
    29.         <TextBox></TextBox>  
    30.         <TextBox Style="{x:Null}"></TextBox>  
    31.     </StackPanel>  
    32. </Window>  
        Style没有X:key标记,默认为引用到所有的x:type指定的控件上,如果不想应用则将style标记为{x:null}。运行效果如下图:

    2.2 应用2

        把DataTemplate应用到某个数据类型上是设置DataTemplate的DataType属性,并且DataTemplate作为资源时也不能带x:key标记, 例如下面的代码:

    [html] view plain copy
     
     print?
    1. <Window x:Class="WpfApplication11.wnd11422"  
    2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
    4.         xmlns:local="clr-namespace:WpfApplication11"  
    5.         Title="wnd11422" Height="200" Width="300">  
    6.     <Window.Resources>  
    7.         <!--DataTemplate作用在某个数据类型上,使用DataType,不能设置x:key-->  
    8.         <DataTemplate DataType="{x:Type local:Unit}">  
    9.             <Grid>  
    10.                 <StackPanel Orientation="Horizontal">  
    11.                     <Grid>  
    12.                         <Rectangle Fill="Red" Width="{Binding Price}" Stroke="Yellow"></Rectangle>  
    13.                         <TextBlock Text="{Binding Year}"/>  
    14.                     </Grid>  
    15.                     <TextBlock Text="{Binding Price}"></TextBlock>  
    16.                 </StackPanel>  
    17.             </Grid>  
    18.         </DataTemplate>  
    19.     </Window.Resources>  
    20.     <StackPanel>  
    21.         <ListBox x:Name="_listBox"></ListBox>  
    22.         <ComboBox x:Name="_comBox"></ComboBox>  
    23.     </StackPanel>  
    24. </Window>  

    代码中的DataTemplate的目标数据类型和ListBox的条目类型都是Unit:

    [csharp] view plain copy
     
     print?
    1. /// <summary>  
    2. /// DataType  
    3. /// </summary>  
    4. public class Unit  
    5. {  
    6.     public int Price { get; set; }  
    7.     public string Year { get; set; }  
    8. }  
    指定数据源:
    [csharp] view plain copy
     
     print?
    1. public partial class wnd11422 : Window  
    2. {  
    3.     public wnd11422()  
    4.     {  
    5.         InitializeComponent();  
    6.   
    7.         List<Unit> _listUnit = new List<Unit>()  
    8.         {  
    9.             new Unit(){Price=100, Year="2001" },  
    10.             new Unit(){Price=120, Year="2002" },  
    11.             new Unit(){Price=140, Year="2003" },  
    12.             new Unit(){Price=180, Year="2004" },  
    13.             new Unit(){Price=150, Year="2005" },  
    14.             new Unit(){Price=200, Year="2006" },  
    15.         };  
    16.   
    17.         _listBox.ItemsSource = _listUnit;  
    18.         _comBox.ItemsSource = _listUnit;  
    19.     }  
    20. }  
    此时DataTemplate会自动加载到所有的Unit类型对象上,尽管我没有为ListBox和CompBox指定ItemTemplate,一样会得到下图的效果:



    2.3 应用3

        很多时候数据是以XML形式存取的,如果把XML节点先转换为CLR数据类型再应用DataTemplate就麻烦了。DataTemplate很智能,具有直接把XML数据节点当作目标对象的功能-----XML数据中的元素名(标签名)可以作为DataType,元素的子节点和Attribute可以使用XPath来访问。下面的代码使用XmlDataProvider作为数据源(其XPath指出的必须是一组节点),请注意细节之处的变化,结果和应用2的效果相同:

    [html] view plain copy
     
     print?
    1. <Window x:Class="WpfApplication11.wnd11423"  
    2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
    4.         Title="wnd11423" Height="200" Width="300">  
    5.     <Window.Resources>  
    6.         <!--Xml中的元素名可以作为DataType-->  
    7.         <DataTemplate DataType="XUnit">  
    8.             <Grid>  
    9.                 <StackPanel Orientation="Horizontal">  
    10.                     <Grid>  
    11.                         <Rectangle Fill="Red" Width="{Binding XPath=@Price}" Stroke="Yellow"></Rectangle>  
    12.                         <TextBlock Text="{Binding XPath=@Year}"/>  
    13.                     </Grid>  
    14.                     <TextBlock Text="{Binding XPath=@Price}"></TextBlock>  
    15.                 </StackPanel>  
    16.             </Grid>  
    17.         </DataTemplate>  
    18.         <!--XPath指定一组节点-->  
    19.         <XmlDataProvider x:Key="ds" XPath="XUnits/XUnit">  
    20.             <x:XData>  
    21.                 <XUnits xmlns="">  
    22.                     <XUnit Price="100" Year="2001"></XUnit>  
    23.                     <XUnit Price="120" Year="2002"></XUnit>  
    24.                     <XUnit Price="140" Year="2003"></XUnit>  
    25.                     <XUnit Price="180" Year="2004"></XUnit>  
    26.                     <XUnit Price="150" Year="2005"></XUnit>  
    27.                     <XUnit Price="200" Year="2006"></XUnit>  
    28.                 </XUnits>  
    29.             </x:XData>  
    30.         </XmlDataProvider>  
    31.     </Window.Resources>  
    32.     <StackPanel>  
    33.         <!--XmlDataProvider使用Binding-->  
    34.         <ListBox x:Name="_listBox" ItemsSource="{Binding Source={StaticResource ds}}"></ListBox>  
    35.         <ComboBox x:Name="_comBox" ItemsSource="{Binding Source={StaticResource ds}}"></ComboBox>  
    36.     </StackPanel>  
    37. </Window>  
    2.4 应用4

        XML的优势就是可以方便的表示带有层级的数据,比如:年级----班级----小组 或  主菜单---次菜单----三级菜单。同时WPF准备了TreeView和MenuItem控件来显示层级数据。能够帮助层级控件显示层级数据的模板是HierachicalDataTemplate。下面是实际工作中常见的例子:

        值得一提的是,HierarchicalDataTemplate的作用不是MenuItem的内容而是它的Header。如果对MenuItem的单击事件进行侦听处理,我们就可以从被单击的MenuItem的Header中取出XML数据。

    [html] view plain copy
     
     print?
    1. <Window x:Class="WpfApplication11.wnd11424"  
    2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
    4.         Title="wnd11424" Height="400" Width="300">  
    5.     <Window.Resources>  
    6.         <!--年级模版-->  
    7.         <HierarchicalDataTemplate DataType="Grade" ItemsSource="{Binding XPath=Class}">  
    8.             <TextBlock Text="{Binding XPath=@Name}"></TextBlock>  
    9.         </HierarchicalDataTemplate>  
    10.         <!--班级模版-->  
    11.         <HierarchicalDataTemplate DataType="Class" ItemsSource="{Binding XPath=Group}">  
    12.             <RadioButton Content="{Binding XPath=@Name}"></RadioButton>  
    13.         </HierarchicalDataTemplate>  
    14.         <!--分组模版-->  
    15.         <HierarchicalDataTemplate DataType="Group">  
    16.             <CheckBox Content="{Binding XPath=@Name}"></CheckBox>  
    17.         </HierarchicalDataTemplate>  
    18.   
    19.         <!--数据模版-->  
    20.         <XmlDataProvider x:Key="ds" XPath="Data/Grade">  
    21.             <x:XData>  
    22.                 <Data xmlns="">  
    23.                     <Grade Name="一年级">  
    24.                         <Class Name="甲班">  
    25.                             <Group Name="A组"></Group>  
    26.                             <Group Name="B组"></Group>  
    27.                             <Group Name="C组"></Group>  
    28.                         </Class>  
    29.                         <Class Name="乙班">  
    30.                             <Group Name="A组"></Group>  
    31.                             <Group Name="B组"></Group>  
    32.                             <Group Name="C组"></Group>  
    33.                         </Class>  
    34.                     </Grade>  
    35.                     <Grade Name="二年级">  
    36.                         <Class Name="丙班">  
    37.                             <Group Name="A组"></Group>  
    38.                             <Group Name="B组"></Group>  
    39.                             <Group Name="C组"></Group>  
    40.                         </Class>  
    41.                         <Class Name="丁班">  
    42.                             <Group Name="A组"></Group>  
    43.                             <Group Name="B组"></Group>  
    44.                             <Group Name="C组"></Group>  
    45.                         </Class>  
    46.                     </Grade>  
    47.                 </Data>  
    48.             </x:XData>  
    49.         </XmlDataProvider>  
    50.     </Window.Resources>  
    51.     <!--监听事件-->  
    52.     <StackPanel MenuItem.Click="StackPanel_Click">  
    53.         <Menu ItemsSource="{Binding Source={StaticResource ds}}"></Menu>  
    54.         <TreeView ItemsSource="{Binding Source={StaticResource ds}}" Margin="5"></TreeView>  
    55.     </StackPanel>  
    56. </Window>  
    事件处理器:
    [csharp] view plain copy
     
     print?
    1. private void StackPanel_Click(object sender, RoutedEventArgs e)  
    2. {  
    3.     // Head为XmlElement  
    4.     XmlElement xmlElem = (e.OriginalSource as MenuItem).Header as XmlElement;  
    5.     MessageBox.Show(xmlElem.Attributes["Name"].Value);  
    6. }  
     

     



     
     
     
  • 相关阅读:
    自动化系列-pyppeteer安装
    用python做一个可视化生成二维码的工具
    Python第三方包之DingDingBot
    封装属于自己的Python包
    sqlldr使用
    MS MQ 消息队列
    PDF打印
    oracle 存储过程编辑 卡死
    winrar 压缩文件方法
    数值 转换 成 带千位符的数值,且转成大写
  • 原文地址:https://www.cnblogs.com/lizhenlin/p/5906678.html
Copyright © 2020-2023  润新知