~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
开发工具与关键技术:Visual Studio 、模板
作者:邓崇富
撰写时间: 5 月 24 日
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
在WPF中,通过引入模板(Template)微软将数据和算法的“内容”与“形式”解耦了。WPF中的Template分为两大类:
ControlTemplate是算法内容的表现形式,一个控件怎样组织其内部结构才能让它更符合业务逻辑、让客户操作起来更加舒服就是由它来控制的。他决定了控件“长成什么样子”,并让程序员有机会在控件原有的内部逻辑基础上扩展自己的逻辑。DataTemplate是数据内容的表现形式,一条数据显示成什么样子,是简单的文本还是直观的图形动画就由它来决定。
总而言之,Template就是“外衣”,ControlTemplate是控件的外衣,DataTemplate是数据的外衣。
数据的外衣(DataTemplate):
DataTemplate常用的地方有3个,分别是:
ContentControl的ContentTemplate属性,相当于给ContentControl的内容穿衣服。ItemsControl的ItemTemplate属性,相当于给ItemsControl的数据条目穿衣服。GridViewColumn的CellTemplate属性,相当于给GridViewColumn单元格里的数据穿衣服。
下面用个例子来对比UserControl与DataTemplate的使用。假如有一列汽车的数据,这列数据显示在ListBox里,要求ListBox的条目显示汽车的厂商图标和简要的参数,单击某个条目后在窗体的详细内容区域显示汽车的图片和详细参数。
首先要为在项目里建立资源管理目录并把图片添加进来。Logo的文件名与厂商名称要一致,照片的文件名则与车名一致,因为无论是使用UserControl还是DataTemplate,厂商的Logo和汽车的照片都要用到的。
文件结构如下图:
首先创建一个Car数据类型的类文件:
namespace WPF练习
{
public class Car
{
public string Automaker { get; set; }
public string Name { get; set; }
public string Year { get; set; }
public string TopSpeed { get; set; }
}
}
为了在ListBox里显示Car类型数据,我们需要创建一个UserControl,命名为CarListItemView。这个UserControl由一个Car类型实例在背后支持,当设置这个实例的时候,界面元素将实例的属性值显示在各个控件里。CarListItemView的XAML的代码如下:
<UserControl x:Class="WPF练习.CarListItemView"
<!--省略了两个命名空间-->
<Grid Margin="2">
<StackPanel Orientation="Horizontal">
<Image x:Name="ImageLogos" Grid.RowSpan="3" Width="64" Height="64"/>
<StackPanel Margin="5,10">
<TextBlock x:Name="textBlockName" FontSize="16" FontWeight="Bold"/>
<TextBlock x:Name="textBlockYear" FontSize="14"/>
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
CarListItemView界面的后台代码如下:
public partial class CarListItemView : UserControl
{
public CarListItemView()
{
InitializeComponent();
}
private Car car;
public Car Car
{
get { return car; }
set
{
car = value;
this.textBlockName.Text = car.Name;
this.textBlockYear.Text = car.Year;
string uriStr = string.Format(@"/Pitrues/Logos/{0}.PNG", car.Automaker);
this.ImageLogos.Source = new BitmapImage(new Uri(uriStr, UriKind.Relative));
}
}
}
类似于上面的原理,还需要为Car类型数据准备一个详细信息的视图,在创建一个UserControl命名为CarDetailView, XAML代码如下:
<UserControl x:Class="WPF练习.CarDetailView"
<!--此处省略了两个命名空间-->
<Border BorderBrush ="Black" BorderThickness="1" CornerRadius="6">
<StackPanel Margin="5">
<Image x:Name="ImagePhoto" Width="400" Height="250"/>
<StackPanel Orientation="Horizontal" Margin="5,0">
<TextBlock Text="Name:" FontWeight="Bold" FontSize="20"/>
<TextBlock x:Name="textBlockName" FontSize="20" Margin="5,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5,0">
<TextBlock Text="Automaker:" FontWeight="Bold"/>
<TextBlock x:Name="textBlockAutomaker" Margin="5,0"/>
<TextBlock Text="Year:" FontWeight="Bold"/>
<TextBlock x:Name="textBlockYear" Margin="5,0"/>
<TextBlock Text="Top Speed:" FontWeight="Bold"/>
<TextBlock x:Name="textBlockTopSpeed" Margin="5,0"/>
</StackPanel>
</StackPanel>
</Border>
</UserControl>
XAML的后台代码:
public partial class CarDetailView : UserControl
{
public CarDetailView()
{
InitializeComponent();
}
private Car car;
public Car Car
{
get { return car; }
set
{
car = value;
this.textBlockName.Text = car.Name;
this.textBlockYear.Text = car.Year;
this.textBlockTopSpeed.Text = car.TopSpeed;
this.textBlockAutomaker.Text = car.Automaker;
string uriStr = string.Format(@"/Pitrues/Images/{0}.jpg", car.Name);
this.ImagePhoto.Source = new BitmapImage(new Uri(uriStr, UriKind.Relative));
}
}
}
最后把上面两个界面组装到主窗体上,下面是组窗体的XAML代码:
<Window x:Class="WPF练习.Window8"
<!--此处省略里四个命名空间-->
xmlns:local="clr-namespace:WPF练习"
mc:Ignorable="d"
Title="Window8" Height="350" Width="623">
<StackPanel Orientation="Horizontal" Margin="5">
<local:CarDetailView x:Name="detailView"/>
<ListBox x:Name="ListBoxCars" Width="180" Margin="5,0" SelectionChanged="listBoxCars_SelectionChanged"/>
</StackPanel>
</Window>
下面是组窗体的后台代码:
public partial class Window8 : Window
{
public Window8()
{
InitializeComponent();
InitialCarList();
}
private void InitialCarList()
{
List<Car> carList = new List<Car>()
{
new Car(){Automaker = "Lamborghini",Name = "Diablo",Year = "1990",TopSpeed = "340"},
new Car(){Automaker = "Lamborghini",Name = "Murcielago",Year = "2001",TopSpeed = "353"},
new Car(){Automaker = "Lamborghini",Name = "Gallardo",Year = "",TopSpeed = "325"},
new Car(){Automaker = "Lamborghini",Name = "Reventon",Year = "",TopSpeed = "356"}
};
foreach (Car car in carList)
{
CarListItemView view = new CarListItemView();
view.Car = car;
this.ListBoxCars.Items.Add(view);
}
}
//选项变化事件的处理器
private void listBoxCars_SelectionChanged(object sender,SelectionChangedEventArgs e)
{
CarListItemView view = e.AddedItems[0] as CarListItemView;
if (view != null)
{
this.detailView.Car = view.Car;
}
}
}
运行后单击ListBox里的条目,效果图如下: