码迷,mamicode.com
首页 > Windows程序 > 详细

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

时间:2015-04-19 14:45:35      阅读:908      评论:0      收藏:0      [点我收藏+]

标签:wpf   模版   

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,例如下面的代码:

<Window x:Class="WpfApplication11.wnd11421"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="wnd11421" Height="200" Width="300">
    <Window.Resources>
        <!--ControlTemplate作用在所有目标控件上,Style不能标记x:key-->
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Template">
                <Setter.Value>
                    <!--使用TemplateBinding,与模版目标一致-->
                    <ControlTemplate TargetType="{x:Type TextBox}">
                        <Border SnapsToDevicePixels="True"
                                Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                CornerRadius="5">
                            <ScrollViewer SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"></ScrollViewer>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="Margin" Value="5"></Setter>
            <Setter Property="BorderBrush" Value="Black"></Setter>
            <Setter Property="Height" Value="28"></Setter>
        </Style>
    </Window.Resources>
    <StackPanel>
        <TextBox></TextBox>
        <TextBox></TextBox>
        <TextBox Style="{x:Null}"></TextBox>
    </StackPanel>
</Window>
    Style没有X:key标记,默认为引用到所有的x:type指定的控件上,如果不想应用则将style标记为{x:null}。运行效果如下图:
技术分享


2.2 应用2


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

<Window x:Class="WpfApplication11.wnd11422"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication11"
        Title="wnd11422" Height="200" Width="300">
    <Window.Resources>
        <!--DataTemplate作用在某个数据类型上,使用DataType,不能设置x:key-->
        <DataTemplate DataType="{x:Type local:Unit}">
            <Grid>
                <StackPanel Orientation="Horizontal">
                    <Grid>
                        <Rectangle Fill="Red" Width="{Binding Price}" Stroke="Yellow"></Rectangle>
                        <TextBlock Text="{Binding Year}"/>
                    </Grid>
                    <TextBlock Text="{Binding Price}"></TextBlock>
                </StackPanel>
            </Grid>
        </DataTemplate>
    </Window.Resources>
    <StackPanel>
        <ListBox x:Name="_listBox"></ListBox>
        <ComboBox x:Name="_comBox"></ComboBox>
    </StackPanel>
</Window>

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

    /// <summary>
    /// DataType
    /// </summary>
    public class Unit
    {
        public int Price { get; set; }
        public string Year { get; set; }
    }
指定数据源:

    public partial class wnd11422 : Window
    {
        public wnd11422()
        {
            InitializeComponent();

            List<Unit> _listUnit = new List<Unit>()
            {
                new Unit(){Price=100, Year="2001" },
                new Unit(){Price=120, Year="2002" },
                new Unit(){Price=140, Year="2003" },
                new Unit(){Price=180, Year="2004" },
                new Unit(){Price=150, Year="2005" },
                new Unit(){Price=200, Year="2006" },
            };

            _listBox.ItemsSource = _listUnit;
            _comBox.ItemsSource = _listUnit;
        }
    }
此时DataTemplate会自动加载到所有的Unit类型对象上,尽管我没有为ListBox和CompBox指定ItemTemplate,一样会得到下图的效果:

技术分享

2.3 应用3


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

<Window x:Class="WpfApplication11.wnd11423"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="wnd11423" Height="200" Width="300">
    <Window.Resources>
        <!--Xml中的元素名可以作为DataType-->
        <DataTemplate DataType="XUnit">
            <Grid>
                <StackPanel Orientation="Horizontal">
                    <Grid>
                        <Rectangle Fill="Red" Width="{Binding XPath=@Price}" Stroke="Yellow"></Rectangle>
                        <TextBlock Text="{Binding XPath=@Year}"/>
                    </Grid>
                    <TextBlock Text="{Binding XPath=@Price}"></TextBlock>
                </StackPanel>
            </Grid>
        </DataTemplate>
        <!--XPath指定一组节点-->
        <XmlDataProvider x:Key="ds" XPath="XUnits/XUnit">
            <x:XData>
                <XUnits xmlns="">
                    <XUnit Price="100" Year="2001"></XUnit>
                    <XUnit Price="120" Year="2002"></XUnit>
                    <XUnit Price="140" Year="2003"></XUnit>
                    <XUnit Price="180" Year="2004"></XUnit>
                    <XUnit Price="150" Year="2005"></XUnit>
                    <XUnit Price="200" Year="2006"></XUnit>
                </XUnits>
            </x:XData>
        </XmlDataProvider>
    </Window.Resources>
    <StackPanel>
        <!--XmlDataProvider使用Binding-->
        <ListBox x:Name="_listBox" ItemsSource="{Binding Source={StaticResource ds}}"></ListBox>
        <ComboBox x:Name="_comBox" ItemsSource="{Binding Source={StaticResource ds}}"></ComboBox>
    </StackPanel>
</Window>


2.4 应用4


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

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

<Window x:Class="WpfApplication11.wnd11424"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="wnd11424" Height="400" Width="300">
    <Window.Resources>
        <!--年级模版-->
        <HierarchicalDataTemplate DataType="Grade" ItemsSource="{Binding XPath=Class}">
            <TextBlock Text="{Binding XPath=@Name}"></TextBlock>
        </HierarchicalDataTemplate>
        <!--班级模版-->
        <HierarchicalDataTemplate DataType="Class" ItemsSource="{Binding XPath=Group}">
            <RadioButton Content="{Binding XPath=@Name}"></RadioButton>
        </HierarchicalDataTemplate>
        <!--分组模版-->
        <HierarchicalDataTemplate DataType="Group">
            <CheckBox Content="{Binding XPath=@Name}"></CheckBox>
        </HierarchicalDataTemplate>

        <!--数据模版-->
        <XmlDataProvider x:Key="ds" XPath="Data/Grade">
            <x:XData>
                <Data xmlns="">
                    <Grade Name="一年级">
                        <Class Name="甲班">
                            <Group Name="A组"></Group>
                            <Group Name="B组"></Group>
                            <Group Name="C组"></Group>
                        </Class>
                        <Class Name="乙班">
                            <Group Name="A组"></Group>
                            <Group Name="B组"></Group>
                            <Group Name="C组"></Group>
                        </Class>
                    </Grade>
                    <Grade Name="二年级">
                        <Class Name="丙班">
                            <Group Name="A组"></Group>
                            <Group Name="B组"></Group>
                            <Group Name="C组"></Group>
                        </Class>
                        <Class Name="丁班">
                            <Group Name="A组"></Group>
                            <Group Name="B组"></Group>
                            <Group Name="C组"></Group>
                        </Class>
                    </Grade>
                </Data>
            </x:XData>
        </XmlDataProvider>
    </Window.Resources>
    <!--监听事件-->
    <StackPanel MenuItem.Click="StackPanel_Click">
        <Menu ItemsSource="{Binding Source={StaticResource ds}}"></Menu>
        <TreeView ItemsSource="{Binding Source={StaticResource ds}}" Margin="5"></TreeView>
    </StackPanel>
</Window>
事件处理器:

        private void StackPanel_Click(object sender, RoutedEventArgs e)
        {
            // Head为XmlElement
            XmlElement xmlElem = (e.OriginalSource as MenuItem).Header as XmlElement;
            MessageBox.Show(xmlElem.Attributes["Name"].Value);
        }
技术分享


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

标签:wpf   模版   

原文地址:http://blog.csdn.net/aoshilang2249/article/details/45128337

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!