本文
可视化树
可视化树代表你界面上所有的渲染在屏幕上的元素。可视化树用于渲染,事件路由,定位资源(如果该元素没有逻辑父元素)等等等等。向上或者乡下遍历可视化树可以简单的使用VisualTreeHelper和简单的递归方法。
例如:
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属性并不存在。你得检查逻辑树叶子元素的可视化子元素,然后看看这些子元素是不是别的逻辑树的成员。代码留给大家自己练习。