wpf中数据的外衣DataTemplate的应用
在WPF中,通过引入模版将数据和算法的“内容” 与“形式”解隅了,WPF中木板可以分为两大类
- ContrlTemplate是算法内容的表现形式,一个控件怎么样去组织他的内部结构才能符合他的逻辑、让用户体验起来更舒服,他可以让程序员在内部逻辑的基本之上去扩展自己的逻辑。
- DataTemplate是数据内容的表现形式,一条数据显示成什么样子,是简单的文本呢,还是具有直观的动画效果等等都是由他来控制的。
换句话说 Template就是木板,也可以称作“外衣”,而ContrlTemplate是控件的外衣,DataTemplate是数据的外衣,下面我们就看看数据外衣的一个实列。我们要做成下图的一个 列子。
从上面的图片中我们可以了解到,最右边是一个面板,面板中存放的是一些格式相同的数据,而左边的部分显示的明细,我们可以想象出来是点击右边的内容,左边的框框展示详细的信息,这些都是我们看到上图之后的猜想,具体是什么呢?右边是一个listbox 存放logo图,以及名称,年份等信息,右边是具体的详细信息
肯定有不少同学会想到这个可以用用户控件啊,这些不就是“数据-视图”吗?视图的话不就是靠userControl来实现嘛?是的,你想的是对的,但是我要说的是WPF不仅支持UserControl还可以支持DataTemplate的视图为数据形成视图。DataTemplate是什么呢?我个人感觉就是UserControl,内容基本都是一样的,升级版本,从UserControl到DataTemplate也就是复制,再改几个数据绑定的字符串(个人理解),没什么不同的。
在DataTemplate中常常用到的3个地方
- ContentControl中的ContentTemplate属性,就是模板的意思,相当于给ContentControl的内容穿外衣
- itemsControl中的ItemTemplate属性,和上面是一样的意思,相当于给itemsControl的数据穿上外衣
- GridViewColumn中CellTemplate属性,相当于给GridViewColumn的数据穿上外衣
上面的我们先以用户控件的形式实现:
先建一个汽车类,其中包含下面一些属性
public class Car { public string AutoMark { get; set; } public string Name { get; set; } public string Year { get; set; } public string Speed { get; set; } }
看上面的图中,我们可以发现右边的内容是一个listbox,里面有一个用户控件CarListItemView,包含图片和内容的用户控件,代码如下
<UserControl x:Class="WpfApplication4.CarListItemView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" > <Grid Margin="2"> <StackPanel Orientation="Horizontal"> <Image Width="64" Height="64" Name="imageLogo"></Image> <StackPanel Margin="5,10"> <TextBlock Name="txtBlockName" FontSize="16" FontWeight="Bold"></TextBlock> <TextBlock Name="txtBlockYear" FontSize="16" FontWeight="Bold"></TextBlock> </StackPanel> </StackPanel> </Grid> </UserControl>
///下面是后台代码
private Car car;
public Car Car {
get { return car; }
set { car = value;
this.txtBlockName.Text = car.Name;
this.txtBlockYear.Text = car.Year;
string uriStr = string.Format(@"/pic/logo/{0}.jpg", car.AutoMark) ;
this.imageLogo.Source = new BitmapImage(new Uri(uriStr,UriKind.Relative));
}
}
右边的内容也是一个模板
<UserControl x:Class="WpfApplication4.CarDetailView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" > <Border BorderBrush="Aquamarine" BorderThickness="1" CornerRadius="6"> <StackPanel Margin="5"> <Image Width="500" Height="300" Name="image"></Image> <StackPanel Margin="5,0" Orientation="Horizontal"> <TextBlock Text="Name:" FontWeight="Bold" FontSize="20"></TextBlock> <TextBlock Name="txtName" FontSize="20" Margin="5,0"></TextBlock> </StackPanel> <StackPanel Orientation="Horizontal" Margin="5,0"> <TextBlock Text="AutoMark:" FontWeight="Bold" FontSize="20"></TextBlock> <TextBlock Name="txtAutoMark" FontWeight="Bold" FontSize="20" Margin="5,0"></TextBlock> <TextBlock Text="Year:" FontWeight="Bold" FontSize="20"></TextBlock> <TextBlock Name="txtYear" FontWeight="Bold" FontSize="20" Margin="5,0"></TextBlock> <TextBlock Text="ToSpeed:" FontWeight="Bold" FontSize="20"></TextBlock> <TextBlock Name="txtToSpeed" FontWeight="Bold" FontSize="20" Margin="5,0"></TextBlock> </StackPanel> </StackPanel> </Border> </UserControl>
2个用户控件创建完之后,我们就可以使用组装了,首先分成2块,第一块左边放的是详细信息,右边其实就是一个listbox,listbox的item就是CarListItemView,所以下面我们的主窗口代码就不难写了
<Window x:Class="WpfApplication4.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:loc="clr-namespace:WpfApplication4" Title="Window1" Height="440" Width="720"> <StackPanel Margin="5" Orientation="Horizontal"> <loc:CarDetailView x:Name="detailView"> </loc:CarDetailView> <ListBox x:Name="listCar" Width="160" Margin="5,0" SelectionChanged="listCar_SelectionChanged"> </ListBox> </StackPanel> </Window>
后台代码:
public void init() { List<Car> list = new List<Car>() { new Car(){ AutoMark="bieke", Name="bieke", Speed="200", Year="1938"}, new Car(){ AutoMark="bmw", Name="bmw", Speed="240", Year="1998"}, new Car(){ AutoMark="dazhong", Name="dazhong", Speed="130", Year="1948"}, new Car(){ AutoMark="jiebao", Name="jiebao", Speed="300", Year="1998"}, new Car(){ AutoMark="lotus", Name="lotus", Speed="500", Year="1968"} }; foreach(Car car in list){ CarListItemView carDetai = new CarListItemView(); carDetai.Car = car; this.listCar.Items.Add(carDetai); } } private void listCar_SelectionChanged(object sender, SelectionChangedEventArgs e) { CarListItemView item = e.AddedItems[0] as CarListItemView; if (item!=null) { detailView.Car = item.Car; } }
这样程序就写完了,运行看看效果哦。
其实在wpf中最最核心的东西还是数据绑定,和路由事件,我们不需要像传统的形式那样浪费wpf中数据驱动界面的重要功能,这种方法完全没有借助与wpf中Bingding实现数据驱动界面,上面的列子中我们必须借助listBox事件去推动CarDetailView的数据显示,这种模式叫事件驱动(控件与控件之间的沟通),下面我们要使用DataTemplate的数据驱动模式(数据与控件之间的沟通,是由内容决定的)代码其实就是userControl的代码,只是稍加改动。
<Window x:Class="WpfApplication4.Window2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:loc="clr-namespace:WpfApplication4" Title="Window2" Height="440" Width="720"> <Window.Resources> <loc:AutoMarkToLogoPathConver x:Key="logoPath"></loc:AutoMarkToLogoPathConver> <loc:NameToPhotoPathConver x:Key="imagePath"></loc:NameToPhotoPathConver> <DataTemplate x:Key="CarDetailTemplate"> <Border BorderBrush="Aquamarine" BorderThickness="1" CornerRadius="6"> <StackPanel Margin="5"> <Image Width="500" Height="300" Name="image" Source="{Binding Name, Converter={StaticResource ResourceKey= imagePath}}"></Image> <StackPanel Margin="5,0" Orientation="Horizontal"> <TextBlock Text="Name:" FontWeight="Bold" FontSize="20"></TextBlock> <TextBlock Name="txtName" FontSize="20" Margin="5,0" Text="{Binding Name}"></TextBlock> </StackPanel> <StackPanel Orientation="Horizontal" Margin="5,0"> <TextBlock Text="AutoMark:" FontWeight="Bold" FontSize="20"></TextBlock> <TextBlock Name="txtAutoMark" FontWeight="Bold" FontSize="20" Margin="5,0" Text="{Binding AutoMark}"></TextBlock> <TextBlock Text="Year:" FontWeight="Bold" FontSize="20"></TextBlock> <TextBlock Name="txtYear" FontWeight="Bold" FontSize="20" Margin="5,0" Text="{Binding Year}"></TextBlock> <TextBlock Text="ToSpeed:" FontWeight="Bold" FontSize="20"></TextBlock> <TextBlock Name="txtToSpeed" FontWeight="Bold" FontSize="20" Margin="5,0" Text="{Binding Speed}"></TextBlock> </StackPanel> </StackPanel> </Border> </DataTemplate> <DataTemplate x:Key="CarListItemTemplate"> <Grid Margin="2"> <StackPanel Orientation="Horizontal"> <Image Width="64" Height="64" Name="imageLogo" Source="{Binding Name, Converter={StaticResource ResourceKey=logoPath}}"></Image> <StackPanel Margin="5,10"> <TextBlock Name="txtBlockName" FontSize="16" FontWeight="Bold" Text="{Binding Name}"></TextBlock> <TextBlock Name="txtBlockYear" FontSize="16" FontWeight="Bold" Text="{Binding Year}"></TextBlock> </StackPanel> </StackPanel> </Grid> </DataTemplate> </Window.Resources> <StackPanel Orientation="Horizontal" Margin="5"> <UserControl ContentTemplate="{StaticResource ResourceKey=CarDetailTemplate}" Content="{Binding SelectedItem, ElementName=listBoxCar}"> </UserControl> <ListBox x:Name="listBoxCar" Width="180" Margin="0,5" ItemTemplate="{StaticResource ResourceKey=CarListItemTemplate}"> </ListBox> </StackPanel> </Window>
public class AutoMarkToLogoPathConver : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return new BitmapImage(new Uri(string.Format(@"/pic/logo/{0}.jpg", value.ToString()), UriKind.Relative)); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } public class NameToPhotoPathConver : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return new BitmapImage(new Uri(string.Format(@"/pic/img/{0}.jpg", value.ToString()), UriKind.Relative)); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }
这里需要解释一下,这上面2个类,图片的image,而上面Car类中全是string类型,这样无法实现图片的展示,那我们该这么做呢,这时候需要用到Converter?在wpf中有一种类型转换,只要实行IValueConverter接口就可以了。在xmal中这样用Source="{Binding Name, Converter={StaticResource ResourceKey= imagePath}}",在这句代码中我们可以看出来,绑定是Name这个属性,然后通过Converter,把它转换成image的形式,其实Name的属性还是string,根本就没影响本身的Name属性,怎么样很强大吧。
大概理解的就这么多。呵呵。。。。。
需要源代码的来这里下载。
http://download.csdn.net/detail/hanhaijin_007/4829584