- 浏览: 195870 次
- 性别:
- 来自: 成都
文章分类
- 全部博客 (231)
- C++学习 (41)
- vc学习 (25)
- vc异常 (4)
- Flex (2)
- Ext (0)
- java学习笔记 (15)
- mysql学习笔记 (1)
- ibatis学习笔记 (7)
- struts1学习笔记 (1)
- hibernate学习笔记 (3)
- javascript学习笔记 (8)
- eclipse快捷键 (2)
- 英语学习 (1)
- cmd (1)
- eclipse 配置 (2)
- 其他 (4)
- JAVASE (34)
- firefox (1)
- linux (8)
- Apache (6)
- cms (22)
- jsuc (10)
- html (3)
- css (1)
- 作图 (2)
- vs2012 (2)
- 编码转换 (12)
- work_unicode (1)
- work_webbrowser (1)
- work_trade_quotes_login (1)
- c#学习 (38)
- wpf学习 (31)
- wpf快速学习 (1)
- VB学习 (1)
- NSIS (4)
- log4cpp (3)
- 学习web (1)
- 经验 (1)
- c_汇编 (1)
最新评论
理解WPF中的视觉树和逻辑树
Understanding the Visual Tree and Logical Tree in WPF
这篇文章讨论WPF中视觉树和逻辑树的细微差别。同时提供了一个小程序供读者稍后分析。如果你已经对着两个概念完全不熟悉,我建议你先读SDK文档中的这篇文章“URL”。
背景
目前SDK文档中关于视觉树和逻辑树的介绍还不是很完全。从我一开始接触WPF, 我就对这两个概念的区别很模糊。我认为这两个树都包含可视化元素和控件,对吧? 错!我认为Window/Page/Control等有且仅有一棵逻辑树,对吧?还是错!我知道我在干嘛吗?完全不知道!
事实证明WPF中的元素树相当复杂而且要求WPF类库中很底层的知识来理解这些树。怎样用通用的方式遍历元素树;你觉得在什么地方无法得知元素成分;这些并不像看起来那么简单。不幸的是,WPF没有暴露接口来简化对元素树的遍历。
现在你可能会为什么遍历元素树会这么麻烦。 答案分成几个部分,在下面讨论。
可视化树
可视化树代表你界面上所有的渲染在屏幕上的元素。可视化树用于渲染,事件路由,定位资源(如果该元素没有逻辑父元素)等等等等。向上或者乡下遍历可视化树可以简单的使用VisualTreeHelper和简单的递归方法。
然后,还是有个小别扭让它变得复杂。任何承继自ContentElement的东西都可以在UI上显示,但其实并不在可视化树中。WPF会假定这些元素也在可视化树中,来保持事件路由的一致性,但这只是个幻觉。VisualTreeHelper对ContentElement对象不起作用,因为ContentElement不是继承自Visual或者Visual3D. 下面是Framework中所有承继自ContentElement的类从Reflector中可以看到:
[img][/img]
这个文档介绍了为什么ContentElement并不真正存在可视化树中。
内容元素(继承自ContentElement的类)不是可视化树的一部分;他们不是继承自Visual而且没有可视化表示。为了显示在UI上,ContentElement必须寄宿在一个Visual主体上,通常是一个FrameworkElement。你可以认为主体类似于一个可以选择如何展示该ContentElement的浏览器。一旦一个Content被显示主体捕获,这个Content就可以加入到一个特定的和可视化树相关的树处理过程中。一般说来,FrameworkElement类都会包含一段代码用来把ContentElement添加到事件路由中去,通过这个content逻辑树的某个子节点,尽管这个content并不是可视化树的一部分。这很必要,因为content也需要找到路由事件的源头。
这意味着你永远没办法仅仅使用VisualTreeHelper来遍历可视化树。如果你把一个ContentElement传递给VisualTreeHelper的GetParent或者GetChild方法,会抛出一个异常。因为ContentElement不是Visual或者Visual3D的子类,你只能沿着逻辑树查找ContentElement的父元素,直到找到一个Visual对象。这里有个例子遍历到可视化树的根元素。
这段代码在必要的时候沿着逻辑树上溯,如else子句所示。这很有用,假如说用户点击一个在TextBlock中的Run元素你需要从Run元素开始上溯可视化树。由于Run类继承自ContentElement, 所以它不真在可视化树中。所以我们需要走出逻辑树直到我们找到了那个包好Run元素的TextBlock。之后我们就可以回到可视化树上来了,因为TextBlock不是ContentElement的子类。
逻辑树
逻辑树表示UI的核心结构。和XAML文件中定义的元素近乎相等,排除掉内部生成的那些用来帮助渲染的可视化元素。WPF用逻辑树来决定依赖属性,值继承,资源解决方案等。
逻辑树用起来不像可视化树那么简单。对于新手来说,逻辑树可以包含类型对象,这一点和可视化树不同,可视化树只包含Dependancy子类的实例。遍历逻辑树时,要记住逻辑树的叶子可以是任何类型。由于LogicTreeHelper只对DependencyObject有效,遍历逻辑树时需要非常小心,最好做类型检查。看个例子:
一个给定的Window/Page/Control会有一棵视觉树,但是可以有几个逻辑树。这些逻辑树互相不相连。可以仅仅使用LogicalTreeHelper来在几棵逻辑树之间遍历。在这篇文章中,我会把顶层控件的逻辑树称作主逻辑树,在他里面的其他逻辑树称作逻辑岛。逻辑岛实际上就是普通的逻辑树但是“岛”可以帮助说明它们和主逻辑树并不相连。
这种无关性可以归结于一个概念:模板。
控件和数据对象本身并没有可见的外观。相反,它们依赖模板来决定怎样进行渲染。一个模板就像一个“拼图块”可以扩展开来以便展示正真的用来渲染的可视元素。这些元素是可扩展模板的一部分,称之为“模板元素”。这些元素有自己的逻辑树,和生成这些元素的对象所拥有的逻辑树不相连。这些小的逻辑树就是我说的逻辑岛。
你只能写额外的代码来在不同的逻辑树或者逻辑岛之间进行切换。遍历逻辑树时,为了连接这些岛,需要使用类似FrameworkElement.TemplateParent,FrameworkContentElement.TemplateParent这些属性来返回持有这些模板的元素,这样就把逻辑岛包含进来了。以下是找到任意元素
的TemplateParent的一个方法:
下溯逻辑树并且在逻辑树之间切换更难因为TemplatedChild属性并不存在。你得检查逻辑树叶子元素的可视化子元素,然后看看这些子元素是不是别的逻辑树的成员。代码留给大家自己练习。
研究工具
这篇文章提供一个小的控制台程序来帮助你体验各种元素树。它会打开一个WPF窗口,然后在控制台输出任何你点击的元素的逻辑树和可视化树。怎样用它在WPF窗口中有说明,先来看看它都提供了些什么功能。
打开工具:
Understanding the Visual Tree and Logical Tree in WPF
这篇文章讨论WPF中视觉树和逻辑树的细微差别。同时提供了一个小程序供读者稍后分析。如果你已经对着两个概念完全不熟悉,我建议你先读SDK文档中的这篇文章“URL”。
背景
目前SDK文档中关于视觉树和逻辑树的介绍还不是很完全。从我一开始接触WPF, 我就对这两个概念的区别很模糊。我认为这两个树都包含可视化元素和控件,对吧? 错!我认为Window/Page/Control等有且仅有一棵逻辑树,对吧?还是错!我知道我在干嘛吗?完全不知道!
事实证明WPF中的元素树相当复杂而且要求WPF类库中很底层的知识来理解这些树。怎样用通用的方式遍历元素树;你觉得在什么地方无法得知元素成分;这些并不像看起来那么简单。不幸的是,WPF没有暴露接口来简化对元素树的遍历。
现在你可能会为什么遍历元素树会这么麻烦。 答案分成几个部分,在下面讨论。
可视化树
可视化树代表你界面上所有的渲染在屏幕上的元素。可视化树用于渲染,事件路由,定位资源(如果该元素没有逻辑父元素)等等等等。向上或者乡下遍历可视化树可以简单的使用VisualTreeHelper和简单的递归方法。
然后,还是有个小别扭让它变得复杂。任何承继自ContentElement的东西都可以在UI上显示,但其实并不在可视化树中。WPF会假定这些元素也在可视化树中,来保持事件路由的一致性,但这只是个幻觉。VisualTreeHelper对ContentElement对象不起作用,因为ContentElement不是继承自Visual或者Visual3D. 下面是Framework中所有承继自ContentElement的类从Reflector中可以看到:
[img][/img]
这个文档介绍了为什么ContentElement并不真正存在可视化树中。
内容元素(继承自ContentElement的类)不是可视化树的一部分;他们不是继承自Visual而且没有可视化表示。为了显示在UI上,ContentElement必须寄宿在一个Visual主体上,通常是一个FrameworkElement。你可以认为主体类似于一个可以选择如何展示该ContentElement的浏览器。一旦一个Content被显示主体捕获,这个Content就可以加入到一个特定的和可视化树相关的树处理过程中。一般说来,FrameworkElement类都会包含一段代码用来把ContentElement添加到事件路由中去,通过这个content逻辑树的某个子节点,尽管这个content并不是可视化树的一部分。这很必要,因为content也需要找到路由事件的源头。
这意味着你永远没办法仅仅使用VisualTreeHelper来遍历可视化树。如果你把一个ContentElement传递给VisualTreeHelper的GetParent或者GetChild方法,会抛出一个异常。因为ContentElement不是Visual或者Visual3D的子类,你只能沿着逻辑树查找ContentElement的父元素,直到找到一个Visual对象。这里有个例子遍历到可视化树的根元素。
DependencyObject FindVisualTreeRoot (DependencyObject initial) { DependencyObject current = initial; DependencyObject result = initial; While(current !=null) { result = current; if(current is Visual || current is Visual3D) { current = VisualTreeHelper.GetParent(current); } else { current = LogicalTreeHelper.GetParent(current); } } return result; }
这段代码在必要的时候沿着逻辑树上溯,如else子句所示。这很有用,假如说用户点击一个在TextBlock中的Run元素你需要从Run元素开始上溯可视化树。由于Run类继承自ContentElement, 所以它不真在可视化树中。所以我们需要走出逻辑树直到我们找到了那个包好Run元素的TextBlock。之后我们就可以回到可视化树上来了,因为TextBlock不是ContentElement的子类。
逻辑树
逻辑树表示UI的核心结构。和XAML文件中定义的元素近乎相等,排除掉内部生成的那些用来帮助渲染的可视化元素。WPF用逻辑树来决定依赖属性,值继承,资源解决方案等。
逻辑树用起来不像可视化树那么简单。对于新手来说,逻辑树可以包含类型对象,这一点和可视化树不同,可视化树只包含Dependancy子类的实例。遍历逻辑树时,要记住逻辑树的叶子可以是任何类型。由于LogicTreeHelper只对DependencyObject有效,遍历逻辑树时需要非常小心,最好做类型检查。看个例子:
void WalkDownLogicalTree(object current) { DoSomethingWithObjectInLogicalTree(current); DependencyObject depObj = current as DependencyObject; if(depObj != null) { foreach(object logicalChild in LogicalTreeHelper.GetChildren(depObj)) WalkDownLogicalTree(logicalChild); } }
一个给定的Window/Page/Control会有一棵视觉树,但是可以有几个逻辑树。这些逻辑树互相不相连。可以仅仅使用LogicalTreeHelper来在几棵逻辑树之间遍历。在这篇文章中,我会把顶层控件的逻辑树称作主逻辑树,在他里面的其他逻辑树称作逻辑岛。逻辑岛实际上就是普通的逻辑树但是“岛”可以帮助说明它们和主逻辑树并不相连。
这种无关性可以归结于一个概念:模板。
控件和数据对象本身并没有可见的外观。相反,它们依赖模板来决定怎样进行渲染。一个模板就像一个“拼图块”可以扩展开来以便展示正真的用来渲染的可视元素。这些元素是可扩展模板的一部分,称之为“模板元素”。这些元素有自己的逻辑树,和生成这些元素的对象所拥有的逻辑树不相连。这些小的逻辑树就是我说的逻辑岛。
你只能写额外的代码来在不同的逻辑树或者逻辑岛之间进行切换。遍历逻辑树时,为了连接这些岛,需要使用类似FrameworkElement.TemplateParent,FrameworkContentElement.TemplateParent这些属性来返回持有这些模板的元素,这样就把逻辑岛包含进来了。以下是找到任意元素
的TemplateParent的一个方法:
DependencyObject GetTemplatedParen(DependencyObject depObj) { FrameworkElement fe = depObj as FrameworkElement; FrameworkElementElement fce = depObj as FrameworkContentElement; DependencyObject result; if(fe != null) result = fe.TemplatedParent; else if(fce != null) result = fce.TemplateParent; else return null; return result; }
下溯逻辑树并且在逻辑树之间切换更难因为TemplatedChild属性并不存在。你得检查逻辑树叶子元素的可视化子元素,然后看看这些子元素是不是别的逻辑树的成员。代码留给大家自己练习。
研究工具
这篇文章提供一个小的控制台程序来帮助你体验各种元素树。它会打开一个WPF窗口,然后在控制台输出任何你点击的元素的逻辑树和可视化树。怎样用它在WPF窗口中有说明,先来看看它都提供了些什么功能。
打开工具:
发表评论
-
byte数组直接输出
2016-07-19 11:38 1205byte数组直接输出 BitConverter.ToStrin ... -
wfi:WindowsFormsHost焦点设置
2016-07-05 17:56 528<wfi:WindowsFormsHost> ... -
x:Name
2016-04-25 10:44 414由于“Dameer”是在同一程序集中实现的,您必须设置 x:N ... -
WindowsFormsHost的使用注意
2016-04-25 10:24 1284一使用 1 引用 2 xmlns:wfi ="clr ... -
TextBox滚动条
2016-04-22 16:51 616<TextBox Grid.Row="1& ... -
TextBlock滚动条
2016-04-22 16:49 406<ScrollViewer Name="s ... -
C#_WPF_WPF用户控件库
2016-04-12 11:37 758新建项目》window桌面》WPF用户控件库 -
DependencyProperty.UnsetValue_WPF涉及到资源的嵌套引用,被引用的资源要定义在引用的资源前面
2015-10-27 15:09 1921错误 1 “{DependencyProperty.Unset ... -
string格式的日期时间字符串转为DateTime类型
2015-08-05 14:22 917string格式的日期时间字符串转为DateTime类型 / ... -
checkbox
2015-07-28 17:18 503private void Chec ... -
自定义DataGrid样式
2015-06-26 22:03 706<ResourceDictionary xmlns=&q ... -
x:Name x:Key Name区别
2015-06-25 15:40 556x:Name x:Key Name区别 1、x:Key 只 ... -
work_代码添加
2015-06-23 17:15 471<UserControl x:Class=" ... -
FindName和VisualTreeHelper
2015-06-19 13:23 719int iCount = this.dataGridCan ... -
理解WPF中的视觉树和逻辑树(转载)
2015-06-19 13:00 555理解WPF中的视觉树和逻辑树 理解WPF中的 ... -
GetEnumerator
2015-06-19 12:39 618IEnumerator enumerator = this ... -
资源文件获取配置
2015-06-18 16:03 340object obj = Application.Curren ... -
Pack URI
2015-06-18 15:41 651主要用来定位 资源文 ... -
wpf学习之ObservableCollection<T>相关知识
2015-04-23 11:10 3268本篇学习了ObservableCollection<T& ... -
C#学习之DateTime
2015-04-23 10:30 413DateTime string.Format("{ ...
相关推荐
wpf datagrid实现树结构
WPF元素树及其细微之处的详细检查
WPF TreeGrid树形表格
学习WPF目录树很好的例子,对初学都很有帮助
WPF多选树控件,在树的基础上,加上多选,并给出获取选中集合的demo,也可以进行其他的转化,满足日常中的使用
wpf 三态树设计实例和效果截图 wpf 三态树设计实例
调试wpf-wpf可视化树+辅助类
由于项目需要一个树形下拉菜单,在网上找了半天,找到一个sliverlight的,结果转成wpf又出错,放弃了,无赖只好自己整一个WPF的,思路是CheckBox+Popup完成了需求,基础实现,各位可以根据需求扩展成自己的想要的...
wpf实现DataGrid列表控件实现树形结构,并且展开控件ToggleButton可以放在任意一列,也可以冻住多列不可拖动。
网上的一个silverlight的树形下拉控件,改成wpf使用的了,希望对各位有所帮助。
使用WPF做的一个树形的省市县级联菜单 非常好
ProgramLoopTree 编程遍历逻辑树和视觉树。 RegisterAttached 注册附加属性。 RoutingStrategyDemo 演示如何使用路由策略。 SharedDP 共享依赖属性示例。 StackPanelLoadedDemo WPF中元素的生命...
WPF TreeView绑定集合生成树 WPF TreeView绑定集合生成树
wpf 带右键树的绑定 带模板的树,右键弹出选中项的内容
关于WPF的Binding绑定的知识,树的后台动态生成。初学者可以学习下
WPF 自定义 树形选择框,Combox+TreeView 实现,支持多选,自定义样式
WPF视觉软件源码,Halcon机器视觉和运动控制软件框架源码,WPF MVVM开发模式,1:1参考easyvision开发,集成几十个软件算子,纯源代码,上手可用。WPF MVVM开发模式,插件框架。 注意:此版本以添加ui设计器。具体...
将多选树作为模版放置到用户控件中使用,这样可以避免多选树重复的定义Node和一些不必要的代码重复写。
WPF控件继承树大全,完整包含控件继承关系,从上往下枚举继承关系,按照英文字母排序,开发过程中可用于查看控件,提高开发速度。
WPF列表树控件,TreeGridApp,C# Demo,