標籤:windows phone 8.1
2.2.5 ItemTemplate、ContentTemplate和DataTemplate
在理解ItemTemplate、ContentTemplate和DataTemplate的關係的之前,我們先來看看ContentControl類和ItemsControl類。ContentControl類是內容控制項的基類,如Button, CheckBox,最明顯的特徵就是這個控制項有Content屬性,有Content屬性的系統控制項都是ContentControl的子類。ItemsControl類是列表內容控制項的基類,如ListBox,它和ContentControl類是類似的,只不過ContentControl類是單項的內容,ItemsControl是多項的內容。
那麼所有繼承自ContentControl的內容控制項的ContentTemplate屬性和所有繼承自ItemsControl的清單控制項的ItemTemplate屬性,都是DataTemplate類型的,意思就是我們可以通過DataTemplate來定義ContentControl和ItemsControl的控制項的UI效果和資料的顯示。
2.2.6 資料範本的使用
DataTemplate是一種可視化的資料範本,它強大的作用在於可以把資料通過綁定的方式展現到控制項上。在上面的例子中,我們介紹了用DataTemplate去實現了UI控制項的內容的顯示,那麼其實DataTemplate最主要的作用並不是去取代ControlTemplate的樣式定義,而是通過資料繫結把資料的控制項的資料來源的資訊展現到控制項上。
下面我們還是通過一個Button的控制項來看一下DataTemplate的資料繫結是如何發揮作用的。
代碼清單2-5:資料範本(原始碼:第2章\Examples_2_5)
(1)首先定義一個Person類表示是資料實體的類型,代碼如下:
public class Person { public string LastName { get; set; } public string FirstName { get; set; } }
(2)設計一個DataTemplate,並把這個DataTemplate作為一個資源來使用,這是和Style資源是一樣的道理,DataTemplate也可以作為公用的資源給多個控制項去使用。那麼這個模板的內容是使用StackPanel控制項把Person對象的資訊水平排列起來。
650) this.width=650;" alt="複製代碼" src="http://common.cnblogs.com/images/copycode.gif" />
< Page.Resources> <DataTemplate x:Key="PersonNameDataTemplate"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding LastName}"/> <TextBlock Text=", "/> <TextBlock Text="{Binding FirstName}"/> </StackPanel> </DataTemplate> </ Page.Resources>
650) this.width=650;" alt="複製代碼" src="http://common.cnblogs.com/images/copycode.gif" />
(3)建立一個Button控制項,把ContentTemplate屬性和模板資源關聯起來。
<Button x:Name="singlePersonButton" ContentTemplate="{StaticResource PersonNameDataTemplate}"/>
(4)建立一個Person對象並且賦值給Button控制項的Content屬性。
singlePersonButton.Content = new Person { FirstName = "lee", LastName = "Terry" };
最後我們可以看到按鈕的運行效果2.14所示,DataTemplate可以把資料對象綁定起來來實現更加靈活的通用的強大的UI資料顯示效果。
650) this.width=650;" src="http://images.cnitblog.com/i/152755/201406/021400291149097.png" />
圖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來進行完善這個列表的控制項。
650) this.width=650;" src="http://images.cnitblog.com/i/152755/201406/021402018173642.png" />
(3)定義一個ItemsControl的樣式,其實就是自訂一個ControlTemplate的模板作為ItemsControl控制項的模板來使用,那麼這個模板就是一個內容的展現形式的模板。我們在ControlTemplate模板上定義了一個ScrollViewer控制項然后里面再使用了一個StackPanel控制項,最裡面的是ItemsPresenter控制項。列表的DataTemplate的顯示內容就是直接投影在ItemsPresenter控制項上面的。我們對ScrollViewer控制項和StackPanel控制項都設定了不同的邊框顏色,這樣在啟動並執行時候就可以很明顯地看出來控制項之間的關係是怎樣的。
650) this.width=650;" alt="複製代碼" src="http://common.cnblogs.com/images/copycode.gif" />
<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>
650) this.width=650;" alt="複製代碼" src="http://common.cnblogs.com/images/copycode.gif" />
(4)在ItemsControl上添加Style屬性為上面定義的樣式。
<ItemsControl x:Name="itemsControl" ItemTemplate="{StaticResource PersonNameDataTemplate}" Style="{StaticResource ItemsControlStyle}"/>
圖2.15 資料範本綁定的列表
程式的運行效果2.16所示。
650) this.width=650;" src="http://images.cnitblog.com/i/152755/201406/021403313649218.png" />
圖2.16 清單控制項的各個模組
2.2.7 讀取和更換資料範本
對於系統的樣式,我們可以通過在C#代碼裡面讀取出來然後再修改,實現動態更換主題的目的。那麼技術總是相通的,對於控制項的DataTemplate,我們一樣也可以通過C#代碼去讀取出來,然後動態地更換,實現更加豐富和靈活化的樣式展示方案。在C#代碼裡面讀取和更換資料範本也是通過對ContentTemplate屬性進行讀取和賦值就可以了。
這種讀取和更換資料範本在列表的控制項中會比較常見,比如我要實現一個這樣一個功能,通過一個列表展現出一批資料,使用者打擊某一條資料的時候,這條資料的樣式要發生改變,表示選取了這條資料,然後使用者可以取消這條資料的選擇也可以繼續選擇多條資料。那麼這樣的功能在資料多選的情況下是非常普遍的功能來的。下面我們來實現這樣的一個功能。
代碼清單2-6:動態更換樣式(原始碼:第2章\Examples_2_6)
(1)定義3個DataTemplate資源,一個是非選中狀態,一個是選中狀態的,還有一個是預設的狀態,其實預設的狀態和非選中狀態是一樣的,但是因為預設的狀態的資料項目樣式不能在C#裡面再次調用。在兩個模板中都添加了Tap事件,使用者捕獲使用者的點擊事件。資料來源集合與上一個例子一樣。
650) this.width=650;" alt="複製代碼" src="http://common.cnblogs.com/images/copycode.gif" />
< 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 }"/>
650) this.width=650;" alt="複製代碼" src="http://common.cnblogs.com/images/copycode.gif" />
(2)處理點擊事件,判斷當前控制項的模板和重新賦值模板。可以通過Name屬性訪問XAML中定義的DataTemplate。
650) this.width=650;" alt="複製代碼" src="http://common.cnblogs.com/images/copycode.gif" />
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; } }
650) this.width=650;" alt="複製代碼" src="http://common.cnblogs.com/images/copycode.gif" />
啟動並執行效果2.17所示,當我們點擊一下資料項目的時候,字型會變大,背景會變成紅色,在點擊一次就會變成原來的樣子。
650) this.width=650;" src="http://images.cnitblog.com/i/152755/201406/021406292249614.png" />
圖2.17 動態更換樣式
(3)在這裡還要注意一點的是,如果使用的時ListBox控制項而不是ItemsControl控制項的時候,在擷取ContentPresenter對象的時候需要通過視覺化樹狀結構去尋找。代碼的實現如下所示:
650) this.width=650;" alt="複製代碼" src="http://common.cnblogs.com/images/copycode.gif" />
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; }
650) this.width=650;" alt="複製代碼" src="http://common.cnblogs.com/images/copycode.gif" />
本文來源於《深入理解Windows Phone 8.1 UI控制項編程》
原始碼下載:http://vdisk.weibo.com/s/zt_pyrfNHoezI
歡迎關注我的微博@WP林政
WP8.1技術交流群:372552293
[WP8.1UI控制項編程]Windows Phone理解和運用ItemTemplate、ContentTemplate和DataTemplate