WPF系统不但支持传统的Winfrom编程的用户界面和用户体验设计,更支持使用专门的设计工具Blend进行专业设计,同时还推出了以模板为核心的新一代设计理念。算法
1. 模板的内涵
做为表现形式,每一个控件都是为了实现某种用户操做算法和直观显示某种数据而生,一个控件看上去是什么样子由它的“算法内容”和“数据内容"决定,这就是内容决定形式,这里,咱们引入两个概念:编程
控件的算法内容:控件能展现哪些数据、具备哪些方法、能响应哪些操做、能激发什么事件,简而言之就是控件的功能,它们是一组相关的算法逻辑。
控件的数据内容:控件具体展现的数据是什么。工具
以往的GUI开发技术(ASP.NET+Winform)中,控件内部逻辑和数据是固定的,程序员不能改变;对于控件的外观,程序员能作的改变也很是的有限,通常也就是设置控件的属性,想改变控件的内部结构是不可能的。若是想扩展一个控件的功能或者更改器外观让其更适应业务逻辑,哪怕只是一丁点的改变,也须要建立控件的子类或者建立用户控件。形成这个局面的根本缘由是数据和算法的“形式”和“内容”耦合的太紧了。post
在WPF中,经过引入模板,微软将数据和算法的内容与形式解耦了。WPF中的Template分为两大类:
ControlTemplate:是算法内容的表现形式,一个控件怎么组织其内部结构才能让它更符合业务逻辑、让用户操做起来更舒服就是由它来控制的。它决定了控件“长成什么样子”,并让程序员有机会在控件原有的内部逻辑基础上扩展本身的逻辑。
DataTemplate:是数据内容的展现方式,一条数据显示成什么样子,是简单的文本仍是直观的图形就由它来决定了。url
Template就是数据的外衣-----ControlTemplate是控件的外衣,DataTemplate是数据的外衣。spa
2. 数据的外衣DataTemplate
WPF不但支持UserControl还支持DataTemplate为数据造成视图。不要觉得DataTemplate有多难!从UserControl升级到DataTemplate通常就是复制,粘贴一下再改几个字符的事儿。.net
DataTemplate经常使用的地方有三处,分别是:
ContentControl的ContentTemplate属性,至关于给ContentControl的内容穿衣服。
ItemsControl的ItemTemplate,至关于给ItemControl的数据条目穿衣服。
GridViewColumn的CellTempldate属性,至关于给GridViewColumn的数据条目穿衣服。设计

事件驱动是控件和控件之间沟通或者说是形式和形式之间的沟通,数据驱动则是数据与控件之间的沟通,是内容决定形式。使用DataTemplate就能够方便的把事件驱动模式转换为数据驱动模式。
让咱们用一个例子体现DataTemplate的使用。例子实现的需求是这样的:有一列汽车数据,这列数据显示在ListBox里面,要求ListBox的条目显示汽车的厂商图标和简要参数,单击某个条目后在窗体的详细内容区显示汽车的图片和详细参数。厂商的Logo和汽车的照片都是要用到的,因此先在项目中创建资源管理目录并把图片添加进来。Logo文件名与厂商的名称一致,照片的名称则与车名一致。组织结构如图:

建立Car数据类型:
- public class Car
- {
- public string Name { get; set; }
- public string ImagePath { get; set; }
- public string Automarker { get; set; }
- public string Year { get; set; }
- }
汽车厂商和名称不能直接拿来做为图片路径,这时就要使用Converter:
-
-
- 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>
-
- <local:PathToImage x:Key="_path2Image"></local:PathToImage>
-
- <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 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>
-
- <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#代码就只剩下这些:
- 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中,从而横向排列,以下图所示: