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數據類型:
/// <summary> /// Car數據類型 -- 必須定義成屬性{ get; set; } /// </summary> public class Car { public string Name { get; set; } public string ImagePath { get; set; } public string Automarker { get; set; } public string Year { get; set; } }
汽車廠商和名稱不能直接拿來作為圖片路徑,這時就要使用Converter:
/// <summary> /// 路徑轉圖片 /// </summary> public class PathToImage:IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { string url = (string)value; return(new BitmapImage(new Uri(url, UriKind.Relative))); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
XAML中添加條目模版:
<DataTemplate x:Key="_carListItemViewTemplate"> <Grid Margin="2"> <StackPanel Orientation="Horizontal"> <Image Source="{Binding Path=Automarker, Converter={StaticResource _path2Image}}" Grid.RowSpan="3" Width="64" Height="64"></Image> <StackPanel Margin="5,10"> <TextBlock Text="{Binding Name}" FontSize="16" FontWeight="Bold"></TextBlock> <TextBlock Text="{Binding Year}" FontSize="14"></TextBlock> </StackPanel> </StackPanel> </Grid> </DataTemplate>
XAML中添加顯示詳細信息的模版:
<DataTemplate x:Key="_carDetailViewTemplate"> <Border BorderBrush="Black" BorderThickness="1" CornerRadius="6"> <StackPanel Margin="5"> <Image Width="400" Height="250" Source="{Binding Path=ImagePath, Converter={StaticResource _path2Image}}"></Image> <StackPanel Orientation="Horizontal" Margin="5, 0"> <TextBlock Text="Name:" FontWeight="Bold" FontSize="20"></TextBlock> <TextBlock Text="{Binding Path=Name}" FontSize="20" Margin="5, 0"></TextBlock> </StackPanel> </StackPanel> </Border> </DataTemplate>
完整的XAML代碼:
<Window x:Class="WpfApplication11.wnd112" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication11" Title="DataTemplate" Height="350" Width="623"> <Window.Resources> <!--Convert--> <local:PathToImage x:Key="_path2Image"></local:PathToImage> <!--DataTemplate for Detail View --> <DataTemplate x:Key="_carDetailViewTemplate"> <Border BorderBrush="Black" BorderThickness="1" CornerRadius="6"> <StackPanel Margin="5"> <Image Width="400" Height="250" Source="{Binding Path=ImagePath, Converter={StaticResource _path2Image}}"></Image> <StackPanel Orientation="Horizontal" Margin="5, 0"> <TextBlock Text="Name:" FontWeight="Bold" FontSize="20"></TextBlock> <TextBlock Text="{Binding Path=Name}" FontSize="20" Margin="5, 0"></TextBlock> </StackPanel> </StackPanel> </Border> </DataTemplate> <!--DataTemplate for Item View --> <DataTemplate x:Key="_carListItemViewTemplate"> <Grid Margin="2"> <StackPanel Orientation="Horizontal"> <Image Source="{Binding Path=Automarker, Converter={StaticResource _path2Image}}" Grid.RowSpan="3" Width="64" Height="64"></Image> <StackPanel Margin="5,10"> <TextBlock Text="{Binding Name}" FontSize="16" FontWeight="Bold"></TextBlock> <TextBlock Text="{Binding Year}" FontSize="14"></TextBlock> </StackPanel> </StackPanel> </Grid> </DataTemplate> </Window.Resources> <!---Window Content--> <StackPanel Orientation="Horizontal" Margin="5"> <UserControl ContentTemplate="{StaticResource _carDetailViewTemplate}" Content="{Binding Path=SelectedItem, ElementName=_listBoxCars}"></UserControl> <ListBox x:Name="_listBoxCars" Width="180" Margin="5,0" ItemTemplate="{StaticResource _carListItemViewTemplate}"></ListBox> </StackPanel> </Window>
代碼對於初學者來說有點長但是結構非常簡單。其中最重要的有兩句:
ContentTemplate="{StaticResource _carDatialViewTemplate}",相當於給一個普通的UserControl穿上了一件外衣、讓Car數據以圖文並茂的方式展現出來。這件外衣就是x:Key="_carDatialViewTemplate"標記的DataTemplate資源。
ItemTemplate="{StaticResource _listBoxCars}",把每一件數據的外衣交給ListBox,當ListBox的ItemSource被賦值的時候,ListBox就會為每個條目穿上這件外衣。這件外衣是以x:Key="_listBoxCars"標記的DataTemplate資源。
因為不再使用事件驅動,而且為數據穿衣服的事也已經自動完成,所以後臺的C#代碼就非常的簡單。窗體的C#代碼就只剩下這些:
/// <summary> /// wnd112.xaml 的交互邏輯 /// </summary> public partial class wnd112 : Window { List<Car> _carList; public wnd112() { InitializeComponent(); _carList = new List<Car>() { new Car(){Name = "Aodi1", ImagePath=@"/Resources/Images/Aodi.jpg", Automarker=@"/Resources/Images/01077_1.png", Year="1990"}, new Car(){Name = "Aodi2", ImagePath=@"/Resources/Images/Aodi.png", Automarker=@"/Resources/Images/01077_1.png", Year="2001"}, }; _listBoxCars.ItemsSource = _carList; } }
運行程序,效果如下圖:
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屬性。
<Window x:Class="WpfApplication11.wnd1132" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="wnd1132" Height="80" Width="300"> <Grid> <ListBox> <!--條目容器--> <ListBox.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal"></StackPanel> </ItemsPanelTemplate> </ListBox.ItemsPanel> <!--條目元素--> <TextBlock Text="Allan"></TextBlock> <TextBlock Text="Allan2"></TextBlock> <TextBlock Text="Allan3"></TextBlock> <TextBlock Text="Allan4"></TextBlock> </ListBox> </Grid> </Window>
條目就會包裝在一個水平排列的StackPanel中,從而橫向排列,如下圖所示:
轉自http://www.cnblogs.com/lizhenlin/p/5906729.html
WPF 模板