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

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