正确理解WPF中的TemplatedParent
(注:Logical Tree中文称为逻辑树,Visual Tree中文称为可视化树或者视觉树,由于名称不是很统一,文中统一用英文名称代表两个概念,况且VisualTreeHelper和LogicalTreeHelper也是WPF中提供的类名称) 众所周知WPF中的Logical Tree是逻辑上定义的元素层次树,而实际上显示在屏幕上的元素层次树是Visual Tree,Visual Tree是
(注:Logical Tree中文称为逻辑树,Visual Tree中文称为可视化树或者视觉树,由于名称不是很统一,文中统一用英文名称代表两个概念,况且VisualTreeHelper和LogicalTreeHelper也是WPF中提供的类名称)
众所周知WPF中的Logical Tree是逻辑上定义的元素层次树,而实际上显示在屏幕上的元素层次树是Visual Tree,Visual Tree是Logical Tree节点扩充后的的产物。因此从Visual Tree的角度上看(Visual Tree当然是完整的一个),Logical Tree被分割成一段一段的,而这些段与段的连接点,就是和TemplatedParent有关。
这个概念在WPF类模型中是FrameworkElement.TemplatedParent属性。WPF中的模板(数据模板和控件模板)都可以扩展Logical Tree,那么模板所修饰的对象就是模板中元素的TemplatedParent,此时模板元素和修饰对象都会出现在Visual Tree中,但模板元素肯定不属于被修饰元素的Logical Tree,但是模板有自己的Logical Tree,两个Logical Tree是分开的,但是通过TemplatedParent,两者之间又有联系。
说再多不如实例形象,来看下面示例代码:
这是一个简单的ContentControl,它的Content是一个按钮,然后定义了控件模板和数据模板,代码中一些关键元素有Name属性,我们在后续讨论就以Name属性的值来引用这些元素。
<!-- 控件模板 -->
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<Border Name="bd1">
<ContentPresenter Name="cp1" ContentSource="Content"/>
</Border>
</ControlTemplate>
</ContentControl.Template>
<!-- 数据模板 -->
<ContentControl.ContentTemplate>
<DataTemplate>
<Border Name="bd2">
<ContentPresenter Name="cp2" Content="{Binding}" />
</Border>
</DataTemplate>
</ContentControl.ContentTemplate>
<!-- 逻辑孩子 -->
<Button Name="btn">按钮</Button>
</ContentControl>
这个ContentControl的Visual Tree如下图:

图中相同颜色的节点代表它们属于同一个Logical Tree,可以看出来,整个Visual Tree分成多个Logical Tree,而这些Logical Tree是分开的,比如上面代码中的两个Border(名称是bd1和bd2),它们的Parent属性的值都是null,即没有逻辑父节点。但是这些逻辑树通过TemplatedParent是互相有联系的。比如控件模板中的元素的TemplatedParent指代最上方的ContentControl,而数据模板元素的TemplatedParent则是控件模板内的ContentPresenter元素。
通过代码也可以验证这些:(bd1, bd2, cp1, cp2分别代表控件模板和数据模板中的Border和ContentPresenter)
{
var bd1 = (Border)contentControl.Template.FindName("bd1", contentControl);
var cp1 = (ContentPresenter)contentControl.Template.FindName("cp1", contentControl);
var bd2 = (Border)contentControl.ContentTemplate.FindName("bd2", cp1);
var cp2 = (ContentPresenter)contentControl.ContentTemplate.FindName("cp2", cp1);
PrintInfo(bd1, cp1, bd2, cp2, btn);
}
void PrintInfo(params FrameworkElement[] eles)
{
string s = "";
foreach (var ele in eles)
s += String.Format("{2}\r\nParent: {0}\r\nTemplatedParent: {1}\r\n\r\n", ele.Parent, ele.TemplatedParent, ele.Name);
MessageBox.Show(s);
}
输出信息:(冒号后没有值则代表null)
Parent:
TemplatedParent: System.Windows.Controls.ContentControl: 按钮
cp1
Parent: System.Windows.Controls.Border
TemplatedParent: System.Windows.Controls.ContentControl: 按钮
bd2
Parent:
TemplatedParent: System.Windows.Controls.ContentPresenter
cp2
Parent: System.Windows.Controls.Border
TemplatedParent: System.Windows.Controls.ContentPresenter
btn
Parent: System.Windows.Controls.ContentControl: 按钮
TemplatedParent:
最后还有一个btn,指代ContentControl中的内容按钮,它属于主干逻辑树,因此Parent是ContentControl,同时它也不属于任何模板,不存在修饰对象,因此TemplatedParent为null
另外WPF数据绑定Binding类还支持RelativeSource对象,这个RelativeSource类的Mode属性有一个TemplatedParent值,这个值就是代表数据绑定会将数据源作为,同时WPF中的TemplateBinding标记扩展可以方便定义此类绑定,另外TemplateBinding的绑定模式是OneWay。
了解了TemplatedParent,使用TemplateBinding也就非常灵活了,一般情况下TemplateBinding使用在定义控件模板下,但是在数据模板中也可以使用,比如下面这个例子:
<Button>Content</Button>
<ContentControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{TemplateBinding Content}" />
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
这个TemplateBinding的数据源在哪里?答案就是ContentControl中默认控件模板里的ContentPresenter,所以这里数据模板内的ContentPresenter的Content直接绑定到控件模板中的ContentPresenter的Content属性,当然这个仅仅为了做示例,实际上用Content=”{Binding}”也可以。
本文来自刘圆圆的博客,原文地址:http://www.cnblogs.com/mgen/archive/2011/08/31/2160581.html
正确理解WPF中的TemplatedParent的更多相关文章
- 正确理解WPF中的TemplatedParent (转贴)
http://blog.csdn.net/idebian/article/details/8761388 (注:Logical Tree中文称为逻辑树,Visual Tree中文称为可视化树或者视觉树 ...
- 理解WPF中的视觉树和逻辑树
轉載地址:http://blog.csdn.net/changtianshuiyue/article/details/26981797 理解WPF中的视觉树和逻辑树 Understanding th ...
- WPF教程九:理解WPF中的对象资源
在WPF中,所有继承自FrameworkElement的元素都包含一个Resources属性,这个属性就是我们这篇要讲的资源. 这一篇讲解的资源是不是上一篇的程序集资源(那个是在编译过程中打包到程序集 ...
- WPF教程六:理解WPF中的隧道路由和冒泡路由事件
WPF中使用路由事件升级了传统应用开发中的事件,在WPF中使用路由事件能更好的处理事件相关的逻辑,我们从这篇开始整理事件的用法和什么是直接路由,什么是冒泡路由,以及什么是隧道路由. 事件最基本的用法 ...
- 如何正确理解正则表达式中的分隔符 \b
前言:好久不见,博客园. 最近在学习研究regex,其中有个特迷惑自己的知识点是分隔符 ( word boundary) [\b] (注:为了方便,后文都以[]来包含字符,并不是reg规则里面的[] ...
- 2.如何正确理解古典概率中的条件概率《zobol的考研概率论教程》
写本文主要是帮助粉丝理解考研中的古典概率-条件概率的具体定义. "B事件发生的条件下,A事件发生的概率"? "在A集合内有多少B的样本点"? "在B约 ...
- 正确理解JavaScript中的this关键字
JavaScript有this关键字,this跟JavaScript的执行上下文密切相关,很多前端开发工程师至今对this关键字还是模棱两可,本文将结合代码讲解下JavaScript的this关键字. ...
- (转载)新手如何正确理解GitHub中“PR(pull request)”中的意思
我从知乎看到的两个答案,分别从实际意义以及语言学角度告诉你改怎么理解PR,很简洁,这个理解非常棒,会解决新手刚看到PR(pull request)这个词时的困惑. 实际意义: 有一个仓库,叫R ...
- 正确处理WPF中Slider值改变事件的方式
最近在用WPF数据绑定重写一下播放器项目时遇到的关于Slider的问题,在窗体透明度调节和播放进度调节上用了Slider控件.调节窗体透明度我是 这么想的:将窗体的Opacity属性的值与Slider ...
随机推荐
- 浪在ACM新春大作战
题目链接: # Name 补题状态 A Memory and Crow 已补 B Memory and Trident 已补 C Memory and De-Evolution 已补 D Memory ...
- UVa 1583 - Digit Generator 解题报告 - C语言
1.题目大意 如果a加上a的各个数字之和得到b,则说a是b的生成元.给出n其中$1\le n\le 100000$,求其最小生成元,若没有解则输出0. 2.思路 使用打表的方法打出各个数字a对应的b, ...
- hadoop问题集(1)
参考: http://dataunion.org/22887.html 1.mapreduce_shuffle does not exist 执行任何时报错: Container launch ...
- JS判断是IOS还是Android以及如何解决h5打包后在ios下内容与状态栏重叠问题
h5打包后在ios下内容与状态栏重叠问题: 1:知道设备的类型: var u = navigator.userAgent, app = navigator.appVersion; var isAndr ...
- 第一次接触FPGA至今,总结的宝贵经验
从大学时代第一次接触FPGA至今已有10多年的时间,至今记得当初第一次在EDA实验平台上完成数字秒表.抢答器.密码锁等实验时那个兴奋劲.当时由于没有接触到HDL硬件描述语言,设计都是在MAX+plus ...
- Python字符串中的r前缀
在Python中,如果字符串的前面有r/R前缀,那么,就会禁用转义符\的功能: >>>path = r'C:\new\text.dat' >>>pah 'C:\\n ...
- windows编程入门最重要的
要入门 Windows 编程,最重要的不是阅读什么教材,使用什么工具,而是先必须把以下几个对于初学者来说非常容易困惑的重要概念搞清楚: 1. 文字的编码和字符集.这部分需要掌握 ANSI 模式和 Un ...
- Java中I/O流之Object流
Java 中的 object 流:直接将 Object 对象写入或读出 1. serializable 接口:序列化,可以被序列化的,若确实需要将某个类的对象写在硬盘上或网络上,想把他们序列化成一个字 ...
- 团队作业7——第二次项目冲刺(Beta版本)
团队作业7——第二次项目冲刺-Beta版本项目计划 团队作业7——第二次项目冲刺(Beta版本)-第一篇 团队作业7——第二次项目冲刺(Beta版本)-第二篇 团队作业7——第二次项目冲刺(Beta版 ...
- JS DOM(2017.12.28)
一.获得元素节点的方法 document.getElementById() 根据Id获取元素节点 document.getElementsByName() 根据name获取元素节点 遍 ...