D 是数据模板
来源:互联网 发布:有关网络暴力的剧本 编辑:程序博客网 时间:2024/04/30 17:40
ItemsControl: 'D' is for DataTemplate
术语“rich content model”经常在WPF的圈子里出现。在这篇文章中,我们剖析了内容模型,特别是该模型在ItemsControl上的应用。
1. WPF内容模型
在WPF中,根据逻辑孩子的类型和数量来分类不同类型的元素。我们把逻辑孩子看作控件的内容。WPF对不同类型的元素定义了一些不同的内容模型。
可以包含单个项的控件(单个类型为Object的逻辑孩子)被称为“内容控件”。这些控件从ContentControl继承。可以包含集合项的控件被称作items controls。这些控件从ItemsControl派生。有的控件还同时包含一个标题头和一个集合项,这种控件叫做“headered items controls”,同样还有包含单个标题头以及单个内容的控件,叫做“headered content controls”,为了完整起见,我需要说明还有其他类型的元素拥有自己的内容模型。例如:TextBlock可以包含一个Inline项的集合,它们是一些从Inline派生的文本元素,用来创建格式化文本。Decorator类用来创建包含单个UIElement类型的孩子的装饰元素。Panel类型用来布局多个UIElement孩子。
2. ItemsControl的内容模型
那么ItemsControl的内容模型有什么特殊的呢?主要来讲,ItemsControl允许它的逻辑孩子可以是任何CLR对象。这令人印象深刻,传统上讲,Windows开发人员通过组合visual元素来创建UI。但是通过使用WPF rich内容模型,可以通过visual元素和数据项来共同构建一个逻辑UI了。
为了更好的理解这点,考虑下面的例子:
<Window x:Class="HomersListBox.Window1"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:src="clr-namespace:HomersListBox"
Title="Homer's ListBox" Width="300" Height="400">
<ListBox Width="200" Height="300">
<src:Character First="Bart" Last="Simpson" Age="10"
Gender="Male" Image="images/bart.png" />
<src:Character First="Homer" Last="Simpson" Age="38"
Gender="Male" Image="images/homer.png" />
<src:Character First="Lisa" Last="Simpson" Age="8"
Gender="Female" Image="images/lisa.png" />
<src:Character First="Maggie" Last="Simpson" Age="0"
Gender="Female" Image="images/maggie.png" />
<src:Character First="Marge" Last="Simpson" Age="38"
Gender="Female" Image="images/marge.png" />
</ListBox>
</Window>
在这个例子中,逻辑树看上去如下:
Character对象是非常简单的CLR对象(每个属性不再介绍)。当你运行这个示例的时候,你会看到如下窗口:
可以看到,Character对象只是一个字符串,如果WPF不知道如何显示一个对象,那么它只是简单的调用ToString方法来获取一个字符串。
如果你用snoop或者mole在检视这个元素树,你看到WPF在visual tree中插入了一个TextBlock用来显示这个字符串。但是该TextBlock并不是逻辑树的一部分。Visual tree只包含visual元素,但是逻辑树可以包含可视元素和非可视元素。
这个自动创建TextBlock的行为是框架需要显示字符串时候的默认行为,你可以简单的重写ToString方法来改变现实的字符串。如下:
public override string ToString()
{
return _first + " " + _last;
}
现在至少显示的数据有些意义了:
当然,你可能想要的更多。
3. 什么是模板?
在WPF中,一个模板就是一个可视元素树(跟有一些资源和触发器),被用来定义逻辑树中的一个成员的外观。当构建元素树的时候,框架查找项是否有对应的模板,如果有,模板被展开为实际的可视元素并被插入到相应的visual tree中。
有许多不同的模板,每一个都从FrameworkTemplate继承,最常用的模板就是ControlTemplate和DataTemplate。控件模板用来提供控件的可视化呈现,这种机制使用了WPF无外观控件模型。
数据模板类用来对数据项进行可视化展示。这也是我们用来为Character对象定义可视外观的模板。
定义DataTemplate
在大多数情况下,你将DataTemplate定义为资源。将我们的模板定义如下:
<Window.Resources>
<DataTemplate x:Key="CharacterTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Margin="5" Source="{Binding Image}" />
<StackPanel Grid.Column="1" Margin="5">
<TextBlock FontWeight="Bold" Text="{Binding First}" />
</StackPanel>
</Grid>
</DataTemplate>
</Window.Resources>
(模板的定义很简单,不再详述)
WPF会自动将展开的可视化树的根的DataContext属性设置为实际的数据项。一个在WPF论坛中经常被问到的问题是:我如何能得到在模板中定义的一个button所对应的数据项呢?答案非常明显:original source的DataContext属性就是当前数据项:
private void OnButtonClick(object sender, RoutedEventArgs e)
{
object item = (e.OriginalSource as FrameworkElement).DataContext;
. . .
}
4. 对ItemsControl应用数据模板
现在我们定义了模板,如何把模板应用到ItemsControl有多种方法,最简单的方法就是显式设置,如下所示:
<ListBox Width="200" Height="300"
ItemTemplate="{StaticResourceCharacterTemplate}">
. . .
</ListBox>
现在运行程序,可以看到模板被应用了:
使用指定类型的数据模板
以上示例显式设置了模板。使用这种方法,每个集合中的项都会有相同的模板。对于相同类型的对象,这没有问题。但是如果集合中包含不同类型的对象呢?如下所示:
<Page
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<ItemsControl Width="100" Height="100">
<sys:Int32>30</sys:Int32>
<sys:DateTime>12/16/1970</sys:DateTime>
<sys:Boolean>True</sys:Boolean>
<sys:Boolean>False</sys:Boolean>
<sys:String>Foo</sys:String>
</ItemsControl>
</Page>
结果如下:
假定你想要为不同的类型显示不同的数据模板,WPF为你提供了类型指定的模板。
例如你决定为布尔类型使用checkbox,而不是字符串true或者false。为了定义这种模板,你需要指定数据模板的DataType属性,如下:
<Page
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Page.Resources>
<DataTemplate DataType="{x:Type sys:Boolean}">
<CheckBox IsChecked="{Binding Mode=OneWay}" />
</DataTemplate>
</Page.Resources>
<ItemsControl Width="100" Height="100">
<sys:Int32>30</sys:Int32>
<sys:DateTime>12/16/1970</sys:DateTime>
<sys:Boolean>True</sys:Boolean>
<sys:Boolean>False</sys:Boolean>
<sys:String>Foo</sys:String>
</ItemsControl>
</Page>
现在ItemsControl显示如下:
我们并没有为ItemTemplate指定任何模板,但是框架为了显示Bool类型的值,它会在资源中查找匹配bool类型的模板,直到找到了包含Checkbox的模板。
5. 为指定CLR数据类型定义默认模板
在前面的例子中,我们使用资源key定义了一个模板。我们可以使用DataType声明来指定默认数据模板。如下:
<DataTemplate DataType="{x:Type src:Character}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Margin="5" Source="{Binding Image}" />
<StackPanel Grid.Column="1" Margin="5">
<TextBlock FontWeight="Bold" Text="{Binding First}" />
</StackPanel>
</Grid>
</DataTemplate>
这就为在逻辑树中的所有的Character对象提供了一个默认展示。即使我们不为ItemsControl指定,也是如此。更者,如果我们在button中包含一个Character对象,同样,模板也会被应用:
<Button HorizontalAlignment="Center" VerticalAlignment="Center">
<src:Character First="Maggie" Image="images/maggie.png" />
</Button>
如果你很好奇,想为所有CLR对象指定一个默认模板,那么你做不到,因为WPF特别禁止DataType=”{x:Type sys:Object}”类型的模板。
6. 使用DataTemplateSelector
虽然类型特定的模板很有用,然而,这可能没有给你选择模板的灵活性。例如:你想为年龄低于21的人选择一个模板,剩余的选择另外一个模板。
为了更灵活的选择模板,你可以实现一个DataTemplateSelector。一个模板选择器是一个从DataTemplateSelector派生并重写了SelectTemplate方法并返回了你需要的模板的类。下面是一个非常简单的例子:
public class CharacterTemplateSelector : DataTemplateSelector
{
private DataTemplate _childTemplate = null;
public DataTemplate ChildTemplate
{
get { return _childTemplate; }
set { _childTemplate = value; }
}
private DataTemplate _adultTemplate = null;
public DataTemplate AdultTemplate
{
get { return _adultTemplate; }
set { _adultTemplate = value; }
}
public override DataTemplate SelectTemplate(object item,
DependencyObject container)
{
if (item is Character)
{
return (item as Character).Age >= 21
? _adultTemplate :_childTemplate;
}
return base.SelectTemplate(item, container);
}
}
现在为了使用模板选择器,我们需要在资源中简单的声明一个实例,并设置恰当的设置属性:
<src:CharacterTemplateSelector x:Key="CharacterTemplateSelector"
ChildTemplate="{StaticResourceCharacterTemplate}"
AdultTemplate="{StaticResourceAdultCharacterTemplate}" />
然后为ItemsControl设置ItemTemplateSelector属性,如下:
<ListBox Width="200" Height="300"
ItemTemplateSelector="{StaticResourceCharacterTemplateSelector}">
. . .
</ListBox>
- D 是数据模板
- D语言中的模板
- K-d Tree 模板
- 一个ppp帧的数据部分(用十六进制写出)是7D 5E FE 27 7D 5D 7D 5D 65 7D 5E。
- 类模板的友元函数的默认实参不能是 类模板的私有数据成员
- direct3d 3d模型模板
- 3D模板阴影原理
- Problem D: 模板是个好东西
- Problem D: 模板是个好东西
- Problem D: 模板是个好东西
- [bzoj1941]k-d tree 模板
- 类模板中友元函数是函数模板
- d) 导入数据
- 4D数据介绍
- matplotlib 3D 数据
- QWeb是odoo模板引擎
- WPF模板(一):控件模板、数据模板、面板模板
- 项目-两个成员的类模板将类声明改为类模板声明,使得数据成员data1和data2可以是任何类型
- Tomcat 配置首页
- 做了个嵌入式的Lua调试器玩
- Javascript中escape(), encodeURI()和encodeURIComponent()之精析与比较
- JAVA写的escape函数,可以处理JAVASCRIPT的ESCAPE处理的字符,避免字符集问题
- 文件锁
- D 是数据模板
- ExtjS的window内嵌入iframe的使用方法
- java线程同步
- 在android中使用Pull解析器读取xml文件,并进行测试
- Sphinx全文索引安装教程
- 委托和lambda
- listView扩展2——java代码方式实现animation动画输出
- sphinx 全文索引的相关知识
- 7层网络协议