在理解ItemTemplate、ContentTemplate和DataTemplate的關係的以前,咱們先來看看ContentControl類和ItemsControl類。ContentControl類是內容控件的基類,如Button, CheckBox,最明顯的特徵就是這個控件有Content屬性,有Content屬性的系統控件都是ContentControl的子類。ItemsControl類是列表內容控件的基類,如ListBox,它和ContentControl類是相似的,只不過ContentControl類是單項的內容,ItemsControl是多項的內容。html
那麼全部繼承自ContentControl的內容控件的ContentTemplate屬性和全部繼承自ItemsControl的列表控件的ItemTemplate屬性,都是DataTemplate類型的,意思就是咱們能夠經過DataTemplate來定義ContentControl和ItemsControl的控件的UI效果和數據的顯示。編程
DataTemplate是一種可視化的數據模板,它強大的做用在於能夠把數據經過綁定的方式展示到控件上。在上面的例子中,咱們介紹了用DataTemplate去實現了UI控件的內容的顯示,那麼其實DataTemplate最主要的做用並非去取代ControlTemplate的樣式定義,而是經過數據綁定把數據的控件的數據源的信息展示到控件上。app
下面咱們仍是經過一個Button的控件來看一下DataTemplate的數據綁定是如何發揮做用的。字體
代碼清單2-5:數據模板(源代碼:第2章\Examples_2_5)spa
(1)首先定義一個Person類表示是數據實體的類型,代碼以下:設計
public class Person { public string LastName { get; set; } public string FirstName { get; set; } }
(2)設計一個DataTemplate,並把這個DataTemplate做爲一個資源來使用,這是和Style資源是同樣的道理,DataTemplate也能夠做爲公共的資源給多個控件去使用。那麼這個模板的內容是使用StackPanel控件把Person對象的信息水平排列起來。code
< Page.Resources> <DataTemplate x:Key="PersonNameDataTemplate"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding LastName}"/> <TextBlock Text=", "/> <TextBlock Text="{Binding FirstName}"/> </StackPanel> </DataTemplate> </ Page.Resources>
(3)建立一個Button控件,把ContentTemplate屬性和模板資源關聯起來。htm
<Button x:Name="singlePersonButton" ContentTemplate="{StaticResource PersonNameDataTemplate}"/>
(4)建立一個Person對象而且賦值給Button控件的Content屬性。對象
singlePersonButton.Content = new Person { FirstName = "lee", LastName = "Terry" };blog
最後咱們能夠看到按鈕的運行效果如圖2.14所示,DataTemplate能夠把數據對象綁定起來來實現更加靈活的通用的強大的UI數據顯示效果。
圖2.14 數據模板綁定的按鈕
那麼剛纔的示例是DataTemplate在ContentControl類型的控件上的應用,那麼下面咱們再來看看DataTemplate在ItemsControl類型的控件上的實現,ContentControl和ItemsControl也是能夠直接做爲控件去使用的,若是咱們並不須要Button或者ListBox這些控件的一些高級功能,就能夠直接使用ContentControl或者ItemsControl控件。
(1)定義一個ItemsControl控件,把ItemTemplate屬性和模板資源關聯起來。
<ItemsControl x:Name="itemsControl" ItemTemplate="{StaticResource PersonNameDataTemplate}" />
(2)建立一個Person對象的集合而且賦值給ItemsControl控件的ItemsSource屬性。
Persons.Add(new Person { FirstName = "lee2", LastName = "Terry2" });
Persons.Add(new Person { FirstName = "lee3", LastName = "Terry3" });
Persons.Add(new Person { FirstName = "lee4", LastName = "Terry4" });
Persons.Add(new Person { FirstName = "lee5", LastName = "Terry5" });
itemsControl.ItemsSource = Persons;
這時候能夠看到運行效果如圖2.15所示,ItemsControl能夠把數據集合經過列表的形式展示出來,可是你會發現直接用ItemsControl實現的列表的功能很是有限,而且也不能滾動,接下來再結合一下ContentTemplate來進行完善這個列表的控件。
圖2.15 數據模板綁定的列表
(3)定義一個ItemsControl的樣式,其實就是自定義一個ControlTemplate的模板做爲ItemsControl控件的模板來使用,那麼這個模板就是一個內容的展示形式的模板。咱們在ControlTemplate模板上定義了一個ScrollViewer控件而後裏面再使用了一個StackPanel控件,最裏面的是ItemsPresenter控件。列表的DataTemplate的顯示內容就是直接投影在ItemsPresenter控件上面的。咱們對ScrollViewer控件和StackPanel控件都設置了不一樣的邊框顏色,這樣在運行的時候就能夠很明顯地看出來控件之間的關係是怎樣的。
<Style x:Name="ItemsControlStyle" TargetType="ItemsControl"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ItemsControl"> <ScrollViewer BorderBrush="Red" BorderThickness="6"> <StackPanel Orientation="Horizontal" Background="Blue"> <Border BorderBrush="Yellow" BorderThickness="3"> <ItemsPresenter /> </Border> </StackPanel> </ScrollViewer> </ControlTemplate> </Setter.Value> </Setter> </Style>
(4)在ItemsControl上添加Style屬性爲上面定義的樣式。
<ItemsControl x:Name="itemsControl" ItemTemplate="{StaticResource PersonNameDataTemplate}" Style="{StaticResource ItemsControlStyle}"/>
程序的運行效果如圖2.16所示。
圖2.16 列表控件的各個模塊
對於系統的樣式,咱們能夠經過在C#代碼裏面讀取出來而後再修改,實現動態更換主題的目的。那麼技術老是相通的,對於控件的DataTemplate,咱們同樣也能夠經過C#代碼去讀取出來,而後動態地更換,實現更加豐富和靈活化的樣式展現方案。在C#代碼裏面讀取和更換數據模板也是經過對ContentTemplate屬性進行讀取和賦值就能夠了。
這種讀取和更換數據模板在列表的控件中會比較常見,好比我要實現一個這樣一個功能,經過一個列表展示出一批數據,用戶打擊某一條數據的時候,這條數據的樣式要發生改變,表示選取了這條數據,而後用戶能夠取消這條數據的選擇也能夠繼續選擇多條數據。那麼這樣的功能在數據多選的狀況下是很是廣泛的功能來的。下面咱們來實現這樣的一個功能。
代碼清單2-6:動態更換樣式(源代碼:第2章\Examples_2_6)
(1)定義3個DataTemplate資源,一個是非選中狀態,一個是選中狀態的,還有一個是默認的狀態,其實默認的狀態和非選中狀態是同樣的,可是由於默認的狀態的數據項樣式不能在C#裏面再次調用。在兩個模板中都添加了Tap事件,用戶捕獲用戶的點擊事件。數據源集合與上一個例子同樣。
< Page.Resources> <!--選中數據項的樣式--> <DataTemplate x:Key="dataTemplateSelectKey" x:Name="dataTemplateSelectName"> <Grid Tapped="StackPanel_Tap_1" Background="Red"> <TextBlock Text="{Binding LastName}" FontSize="50" /> </Grid> </DataTemplate> <!--默認數據項的樣式,注意默認的數據項樣式不能在C#中再次調用--> <DataTemplate x:Key="dataTemplateDefaultKey" x:Name=" dataTemplateDefaultName"> <StackPanel Orientation="Horizontal" Tapped ="StackPanel_Tap_1"> <TextBlock Text="{Binding LastName}"/> <TextBlock Text=", "/> <TextBlock Text="{Binding FirstName}"/> </StackPanel> </DataTemplate> <!--非選中數據項的樣式--> <DataTemplate x:Key="dataTemplateNoSelectKey" x:Name="dataTemplateNoSelectName"> <StackPanel Orientation="Horizontal" Tapped ="StackPanel_Tap_1"> <TextBlock Text="{Binding LastName}"/> <TextBlock Text=", "/> <TextBlock Text="{Binding FirstName}"/> </StackPanel> </DataTemplate> </ Page.Resources> ……省略若干代碼 //建立ItemsControl控件來綁定列表的數據 <ItemsControl x:Name="listbox" ItemTemplate="{StaticResource dataTemplateDefaultKey }"/>
(2)處理點擊事件,判斷當前控件的模板和從新賦值模板。能夠經過Name屬性訪問XAML中定義的DataTemplate。
private void StackPanel_Tap_1(object sender, TappedRoutedEventArgs e) { // 獲取ItemsControl對象的ItemContainerGenerator屬性 // 經過點擊的控件的DataContext判斷所綁定的數據對象 // 而後從ItemContainerGenerator裏面獲取到當前的ContentPresenter對象 ContentPresenter myContentPresenter = (ContentPresenter)(listbox.ItemContainerGenerator.ContainerFromItem((sender as Panel).DataContext)); // 判斷數據模板是選中狀態的仍是非選中狀態的,而後進行賦值 if (myContentPresenter.ContentTemplate.Equals(dataTemplateSelectName)) { //賦值非選中狀態的模板 myContentPresenter.ContentTemplate = dataTemplateNoSelectName; } else { //賦值選中狀態的模板 myContentPresenter.ContentTemplate = dataTemplateSelectName; } }
運行的效果如圖2.17所示,當咱們點擊一下數據項的時候,字體會變大,背景會變成紅色,在點擊一次就會變成原來的樣子。
圖2.17 動態更換樣式
(3)在這裏還要注意一點的是,若是使用的時ListBox控件而不是ItemsControl控件的時候,在獲取ContentPresenter對象的時候須要經過可視化樹去查找。代碼的實現以下所示:
private void StackPanel_Tap_1(object sender, TappedRoutedEventArgs e) { //獲取到的對象是ListBoxItem ListBoxItem myListBoxItem = (ListBoxItem)(listbox.ItemContainerGenerator.ContainerFromItem((sender as Panel).DataContext)); // 在ListBoxItem中查找ContentPresenter ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myListBoxItem); ……//省略若干代碼 } //查找可視化樹某個類型的元素 private childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child != null && child is childItem) return (childItem)child; else { childItem childOfChild = FindVisualChild<childItem>(child); if (childOfChild != null) return childOfChild; } } return null; }
本文來源於《深刻理解Windows Phone 8.1 UI控件編程》
源代碼下載:http://vdisk.weibo.com/s/zt_pyrfNHoezI
歡迎關注個人微博@WP林政
WP8.1技術交流羣:372552293