ListView的ItemsPanel的类型为ItemsPanelTemplate,
ListView的ItemTemplate属性的类型是DataTemplate
而Button的Template属性的类型是ControlTemplate,其实任何控制的Template属性的类型都是
ControlTemplate,如Button:
要理解模板,首要避开的一个坑就是,模板属性名称和模板类型名称。不区分这个东西,你会误以为有很多模板类型,直接吓蒙,劝退。
每个控件的模板属性和模板类型,是实例与类的关系,这里一定注意!
其次,要认识到,下面这种语法这并不是嵌套,而是属性的赋值,是一个实例化的过程:
ListView的ItemsPanel属性的类型为ItemsPanelTemplate,所以这里在里面写了一个ItemsPanelTemplate,这表示实例化了一个ItemsPanelTemplate对象并赋值给ListView的ItemsPanel属性。
谈论模板,避不开视觉树,首先看逻辑树,这个很简单,因为它很直观就是xaml中嵌套这些“业务逻辑”:
123456
sdfad
现在上面这段代码的逻辑树,就是:Window -》UniformGrid-》Button-》TextBox (并列的关系我就不画了)
视觉树,就要看控件内部,比如Button这个控件,就是由更基础的元素构建的,只是被封装起来我们看不到细节而已,但是你能看到button中间有文字,背景是灰色的。
接下来,我们可以借助编辑模板这个功能,观察一下,Button的内部世界(视觉树):
选中Button,右键选择=》编辑模板
这里通过Style设置了button的所有属性,但是这里我们重点关注的是Template这个属性!这里就是视觉树的呈现。那我们发现构成button的元素部分,异常的简单:
一个 border里面放了一个ContentPresenter(数据模板相关,后面讲解) 。而这些被包含在了一个叫做ControlTemplate的标签里。
ControlTemplate控件模板就是给修改视觉树提供了一个接口!接下来我想看看Listbox的控件模板,方法一样:
元素构成部分也不多:
最后我想看看TextBlock的ControlTemplate,然而你会发现TextBlock根本没有ControlTemplate,因为他根本就不算是一个控件,他直接继承自FrameworkElement,它只是一个元素。(包括Border和ItemPresenter以及ContentPresenter都是继承FrameworkElement并没有直接继承自Control)
那么这里,我总结一下:
逻辑树中每个控件的内部其实包含了视觉树。视觉树也是有基本的元素和控件构成!因为视觉树是被封装起来的,所以微软为程序员提供了修改控件内部(视觉树)的机会,及控件模板。修改控件模板,可以轻易改变控件的外表!举个例子:
这两个都是checkbox,我们不需要重写控件,只需要通过控件模板修改一下逻辑树以及Trigger就能实现。(Trigger下一篇再说)
注意:这里指定TargetType是很重要的,不然IsChecked这个属性无法通过编译
接下来是数据模板,他也要看这个课视觉树,前面的视觉树中,我们发现了一个不认识的东西,但是视觉树里基本都有他:ItemPresenter以及ContentPresenter(以Presenter结尾的东西)
这些以Presenter结尾的元素,统称为“内容占位”。为啥wpf中控件之间可以任意嵌套?奥秘就在这里,如果你给button内部嵌套个啥的,这个东西都会扔给ContentPresenter,ContentPresenter会将其包裹起来。而Presenter结尾的元素也就是数据模板作用的对象。
举个例子:
效果如下:
用snoop观察一下我们这个程序的内部结构:
你发现了三个Presenter结尾的元素,
一个ScrollContentPresenter是属于ListBox的视觉树种的ScrollView的,ScrollContentPresenter就包含了ItemsPresenter,就包含了一系列的ListBoxItem,而ListBoxItem就包含了我们在数据模板中定义的DataTemplate的内容:
小结,数据模板,其实也能改变视觉树,只不过他并不是重新修改控件的整个视觉树,而是向Presenter结尾的元素中添加内容。而这些内容需要绑定的数据数据才会被实例化。
ItemsPanelTemplate,这个今天累了,后续补充吧。
1 我们需要避开两个坑
2 理解什么是视觉树
3 数据模板和控件模板都是微软提供的接口,用于修改视觉树。