WPF系统不但支持传统的Winfrom编程的用户界面和用户体验设计,更支持使用专门的设计工具Blend进行专业设计,同时还推出了以模板为核心的新一代设计理念。
作为表现形式,每个控件都是为了实现某种用户操作算法和直观显示某种数据而生,一个控件看上去是什么样子由它的“算法内容”和“数据内容"决定,这就是内容决定形式,这里,我们引入两个概念:
控件的算法内容:控件能展示哪些数据、具有哪些方法、能响应哪些操作、能激发什么事件,简而言之就是控件的功能,它们是一组相关的算法逻辑。
控件的数据内容:控件具体展示的数据是什么。
以往的GUI开发技术(ASP.NET+Winform)中,控件内部逻辑和数据是固定的,程序员不能改变;对于控件的外观,程序员能做的改变也非常的有限,一般也就是设置控件的属性,想改变控件的内部结构是不可能的。如果想扩展一个控件的功能或者更改器外观让其更适应业务逻辑,哪怕只是一丁点的改变,也需要创建控件的子类或者创建用户控件。造成这个局面的根本原因是数据和算法的“形式”和“内容”耦合的太紧了。
在WPF中,通过引入模板,微软将数据和算法的内容与形式解耦了。WPF中的Template分为两大类:
ControlTemplate:是算法内容的表现形式,一个控件怎么组织其内部结构才能让它更符合业务逻辑、让用户操作起来更舒服就是由它来控制的。它决定了控件“长成什么样子”,并让程序员有机会在控件原有的内部逻辑基础上扩展自己的逻辑。
DataTemplate:是数据内容的展示方式,一条数据显示成什么样子,是简单的文本还是直观的图形就由它来决定了。
Template就是数据的外衣-----ControlTemplate是控件的外衣,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; } }
/// <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(); } }
<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>
<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>
<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>
/// <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; } }
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中,从而横向排列,如下图所示:
WPF Template模版之DataTemplate与ControlTemplate【一】
原文地址:http://blog.csdn.net/aoshilang2249/article/details/45127723