WPF Template模版之DataTemplate與ControlTemplate【一】


 

WPF Template模版之DataTemplate與ControlTemplate【一】

標簽: Wpf模版
 分類:
 

目錄(?)[+]

 

    WPF系統不但支持傳統的Winfrom編程的用戶界面和用戶體驗設計,更支持使用專門的設計工具Blend進行專業設計,同時還推出了以模板為核心的新一代設計理念。

1. 模板的內涵

    作為表現形式,每個控件都是為了實現某種用戶操作算法和直觀顯示某種數據而生,一個控件看上去是什么樣子由它的“算法內容”和“數據內容"決定,這就是內容決定形式,這里,我們引入兩個概念:

控件的算法內容:控件能展示哪些數據、具有哪些方法、能響應哪些操作、能激發什么事件,簡而言之就是控件的功能,它們是一組相關的算法邏輯。
控件的數據內容:控件具體展示的數據是什么。


     以往的GUI開發技術(ASP.NET+Winform)中,控件內部邏輯和數據是固定的,程序員不能改變;對於控件的外觀,程序員能做的改變也非常的有限,一般也就是設置控件的屬性,想改變控件的內部結構是不可能的。如果想擴展一個控件的功能或者更改器外觀讓其更適應業務邏輯,哪怕只是一丁點的改變,也需要創建控件的子類或者創建用戶控件。造成這個局面的根本原因是數據和算法的“形式”和“內容”耦合的太緊了。


    在WPF中,通過引入模板,微軟將數據和算法的內容與形式解耦了。WPF中的Template分為兩大類:
ControlTemplate:是算法內容的表現形式,一個控件怎么組織其內部結構才能讓它更符合業務邏輯、讓用戶操作起來更舒服就是由它來控制的。它決定了控件“長成什么樣子”,並讓程序員有機會在控件原有的內部邏輯基礎上擴展自己的邏輯。
DataTemplate:是數據內容的展示方式,一條數據顯示成什么樣子,是簡單的文本還是直觀的圖形就由它來決定了。


Template就是數據的外衣-----ControlTemplate是控件的外衣,DataTemplate是數據的外衣。

2. 數據的外衣DataTemplate

    WPF不但支持UserControl還支持DataTemplate為數據形成視圖。不要以為DataTemplate有多難!從UserControl升級到DataTemplate一般就是復制,粘貼一下再改幾個字符的事兒。

DataTemplate常用的地方有三處,分別是:
ContentControl的ContentTemplate屬性,相當於給ContentControl的內容穿衣服。
ItemsControl的ItemTemplate,相當於給ItemControl的數據條目穿衣服。
GridViewColumn的CellTempldate屬性,相當於給GridViewColumn的數據條目穿衣服。

    事件驅動是控件和控件之間溝通或者說是形式和形式之間的溝通,數據驅動則是數據與控件之間的溝通,是內容決定形式。使用DataTemplate就可以方便的把事件驅動模式轉換為數據驅動模式。

 

    讓我們用一個例子體現DataTemplate的使用。例子實現的需求是這樣的:有一列汽車數據,這列數據顯示在ListBox里面,要求ListBox的條目顯示汽車的廠商圖標和簡要參數,單擊某個條目后在窗體的詳細內容區顯示汽車的圖片和詳細參數。廠商的Logo和汽車的照片都是要用到的,所以先在項目中建立資源管理目錄並把圖片添加進來。Logo文件名與廠商的名稱一致,照片的名稱則與車名一致。組織結構如圖:

 

創建Car數據類型:

[csharp]  view plain  copy
 
 print?
  1. /// <summary>  
  2. /// Car數據類型 -- 必須定義成屬性{ get; set; }  
  3. /// </summary>  
  4. public class Car  
  5. {  
  6.     public string Name { get; set; }  
  7.     public string ImagePath { get; set; }  
  8.     public string Automarker { get; set; }  
  9.     public string Year { get; set; }  
  10. }  

汽車廠商和名稱不能直接拿來作為圖片路徑,這時就要使用Converter:

[csharp]  view plain  copy
 
 print?
  1. /// <summary>  
  2.     /// 路徑轉圖片  
  3.     /// </summary>  
  4.     public class PathToImage:IValueConverter  
  5.     {  
  6.         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)  
  7.         {  
  8.             string url = (string)value;  
  9.             return(new BitmapImage(new Uri(url, UriKind.Relative)));  
  10.         }  
  11.   
  12.         public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)  
  13.         {  
  14.             throw new NotImplementedException();  
  15.         }  
  16.     }  

XAML中添加條目模版:

[html]  view plain  copy
 
 print?
  1. <DataTemplate x:Key="_carListItemViewTemplate">  
  2.     <Grid Margin="2">  
  3.         <StackPanel Orientation="Horizontal">  
  4.             <Image Source="{Binding Path=Automarker, Converter={StaticResource _path2Image}}" Grid.RowSpan="3"  
  5.                    Width="64" Height="64"></Image>  
  6.             <StackPanel Margin="5,10">  
  7.                 <TextBlock Text="{Binding Name}" FontSize="16" FontWeight="Bold"></TextBlock>  
  8.                 <TextBlock Text="{Binding Year}" FontSize="14"></TextBlock>  
  9.             </StackPanel>  
  10.         </StackPanel>  
  11.     </Grid>  
  12. </DataTemplate>  
XAML中添加顯示詳細信息的模版:

 

[html]  view plain  copy
 
 print?
  1. <DataTemplate x:Key="_carDetailViewTemplate">  
  2.     <Border BorderBrush="Black" BorderThickness="1" CornerRadius="6">  
  3.         <StackPanel Margin="5">  
  4.             <Image Width="400" Height="250" Source="{Binding Path=ImagePath, Converter={StaticResource _path2Image}}"></Image>  
  5.             <StackPanel Orientation="Horizontal" Margin="5, 0">  
  6.                 <TextBlock Text="Name:" FontWeight="Bold" FontSize="20"></TextBlock>  
  7.                 <TextBlock Text="{Binding Path=Name}" FontSize="20" Margin="5, 0"></TextBlock>  
  8.             </StackPanel>  
  9.         </StackPanel>  
  10.     </Border>  
  11. </DataTemplate>  
完整的XAML代碼:

 

[html]  view plain  copy
 
 print?
  1. <Window x:Class="WpfApplication11.wnd112"  
  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="DataTemplate" Height="350" Width="623">  
  6.     <Window.Resources>  
  7.         <!--Convert-->  
  8.         <local:PathToImage x:Key="_path2Image"></local:PathToImage>  
  9.         <!--DataTemplate for Detail View -->  
  10.         <DataTemplate x:Key="_carDetailViewTemplate">  
  11.             <Border BorderBrush="Black" BorderThickness="1" CornerRadius="6">  
  12.                 <StackPanel Margin="5">  
  13.                     <Image Width="400" Height="250" Source="{Binding Path=ImagePath, Converter={StaticResource _path2Image}}"></Image>  
  14.                     <StackPanel Orientation="Horizontal" Margin="5, 0">  
  15.                         <TextBlock Text="Name:" FontWeight="Bold" FontSize="20"></TextBlock>  
  16.                         <TextBlock Text="{Binding Path=Name}" FontSize="20" Margin="5, 0"></TextBlock>  
  17.                     </StackPanel>  
  18.                 </StackPanel>  
  19.             </Border>  
  20.         </DataTemplate>  
  21.         <!--DataTemplate for Item View -->  
  22.         <DataTemplate x:Key="_carListItemViewTemplate">  
  23.             <Grid Margin="2">  
  24.                 <StackPanel Orientation="Horizontal">  
  25.                     <Image Source="{Binding Path=Automarker, Converter={StaticResource _path2Image}}" Grid.RowSpan="3"  
  26.                            Width="64" Height="64"></Image>  
  27.                     <StackPanel Margin="5,10">  
  28.                         <TextBlock Text="{Binding Name}" FontSize="16" FontWeight="Bold"></TextBlock>  
  29.                         <TextBlock Text="{Binding Year}" FontSize="14"></TextBlock>  
  30.                     </StackPanel>  
  31.                 </StackPanel>  
  32.             </Grid>  
  33.         </DataTemplate>  
  34.     </Window.Resources>      
  35.     <!---Window Content-->  
  36.     <StackPanel Orientation="Horizontal" Margin="5">  
  37.         <UserControl ContentTemplate="{StaticResource _carDetailViewTemplate}" Content="{Binding Path=SelectedItem, ElementName=_listBoxCars}"></UserControl>  
  38.         <ListBox x:Name="_listBoxCars" Width="180" Margin="5,0" ItemTemplate="{StaticResource _carListItemViewTemplate}"></ListBox>  
  39.     </StackPanel>  
  40. </Window>  

    代碼對於初學者來說有點長但是結構非常簡單。其中最重要的有兩句:
ContentTemplate="{StaticResource _carDatialViewTemplate}",相當於給一個普通的UserControl穿上了一件外衣、讓Car數據以圖文並茂的方式展現出來。這件外衣就是x:Key="_carDatialViewTemplate"標記的DataTemplate資源。
ItemTemplate="{StaticResource _listBoxCars}",把每一件數據的外衣交給ListBox,當ListBox的ItemSource被賦值的時候,ListBox就會為每個條目穿上這件外衣。這件外衣是以x:Key="_listBoxCars"標記的DataTemplate資源。
因為不再使用事件驅動,而且為數據穿衣服的事也已經自動完成,所以后台的C#代碼就非常的簡單。窗體的C#代碼就只剩下這些:

 

[csharp]  view plain  copy
 
 print?
  1. /// <summary>  
  2. /// wnd112.xaml 的交互邏輯  
  3. /// </summary>  
  4. public partial class wnd112 : Window  
  5. {  
  6.     List<Car> _carList;  
  7.     public wnd112()  
  8.     {  
  9.         InitializeComponent();  
  10.   
  11.         _carList = new List<Car>()  
  12.         {  
  13.             new Car(){Name = "Aodi1", ImagePath=@"/Resources/Images/Aodi.jpg", Automarker=@"/Resources/Images/01077_1.png", Year="1990"},  
  14.             new Car(){Name = "Aodi2", ImagePath=@"/Resources/Images/Aodi.png", Automarker=@"/Resources/Images/01077_1.png", Year="2001"},  
  15.         };  
  16.   
  17.         _listBoxCars.ItemsSource = _carList;  
  18.     }  
  19. }  
運行程序,效果如下圖:

3. 控件的外衣ControlTemplate

    每每提到ControlTemplate我都會想到“披着羊皮的狼”這句話-----披上羊皮之后,雖然看上去像只羊,但其行為仍然是匹狼。狼的行為指的是它能吃別的動物、對着滿月嚎叫等事情,控件也有自己的行為,比如顯示數據、執行方法、激發事件等。控件的行為要靠編程邏輯來實現,所以也可以把控件的行為稱為控件的算法內容。舉個例子,WPF中的CheckBox與其基類ToggleButton的功能幾乎完全一樣,但外觀差別上卻非常的大,這就是更換ControlTemplate的結果。經過更換ControlTemplate,我們不但可以制作披着CheckBox外衣的ToggleButton,還能制作披着溫度計外衣的ProgressBar控件。
注意:
實際項目中,ControlTemplate主要有兩大用武之地:
通過更換ControlTemplate來更換控件的外觀,使之具有更優的用戶體驗和外觀。
借助ControlTemplate,程序員和設計師可以並行工作,程序員可以使用WPF標准控件進行編程,等設計師的工作完成之后,只需要把新的ControlTemplate應用的程序中即可。

    ItemsControl具有一個名為ItemsPanel的屬性,它的數據類型是ItemsPanelTemplate。ItemsPanelTemplate也是一種控件Template,它的作用是可以讓程序員可以控制ItemsControl的條目容器。

舉例而言,在我們的印象中ListBox中的條目都是至上而下排列的,如果客戶要求我們做一個水平排列的ListBox怎么辦呢?WPF之前,我們只能重寫控件比較底層的方法和屬性,而現在我們只需要調整ListBox的ItemsPanel屬性。

 

[html]  view plain  copy
 
 print?
  1. <Window x:Class="WpfApplication11.wnd1132"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         Title="wnd1132" Height="80" Width="300">  
  5.     <Grid>  
  6.         <ListBox>  
  7.             <!--條目容器-->  
  8.             <ListBox.ItemsPanel>  
  9.                 <ItemsPanelTemplate>  
  10.                     <StackPanel Orientation="Horizontal"></StackPanel>  
  11.                 </ItemsPanelTemplate>  
  12.             </ListBox.ItemsPanel>  
  13.             <!--條目元素-->  
  14.             <TextBlock Text="Allan"></TextBlock>  
  15.             <TextBlock Text="Allan2"></TextBlock>  
  16.             <TextBlock Text="Allan3"></TextBlock>  
  17.             <TextBlock Text="Allan4"></TextBlock>  
  18.         </ListBox>  
  19.     </Grid>  
  20. </Window>  

 

條目就會包裝在一個水平排列的StackPanel中,從而橫向排列,如下圖所示:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM