-
- 用户界面由一个对象树构建而成,这棵树叫作逻辑树。注意,即使是WPF用户界面的逻辑树也并不是用XAML创建,通过过程式代码同样可以实现。
- 逻辑树之所以重要,是因为几乎WPF的每一方面(属性、事件、资源等)都有与逻辑树相关联的行为。
- 可视树是逻辑树的扩展。可视树将逻辑树的节点打散,分放到核心可视组件中。它表述了一些详细的可视化实现,而不是把每个元素当做一个“黑盒”。
- 并非左右的逻辑树节点都会出现在可视树中,只有从System.Windows.Media.Visual或System.Windows.Media.Visual3D派生的元素才会被包含进去。
- 通过 XamlPad 的工具可以呈现出 XAML 的可视树(和属性值)。当 XAML 放入一个 Window 对象时,无法看到它的可视树,但只要将该 Window 元素改为 Page 元素(并删除 SizeToContent 属性)就可以看到了。
- 可视树是WPF架构的核心组成部分,通常不用去管它们,除非要对控件进行完全的重塑或者做一些底层绘制。
- 可以使用 System.Windows.LogicalTreeHelper 和 System.Windows.Media.VisualTreeHelper 这两个类方便地遍历逻辑树和可视树。
- Debug.WriteLine 函数输出的内容在 Visual Studio 的“输出”窗口可以看到
- 可视树直到Window完成至少一次布局之后才会有节点,因此在OnContentRendered中调用,因为OnContentRendered是在布局完成之后才被调用的。
- 不要根据具体的可视树写代码。因为逻辑树是静态的,不会受到程序员的干扰,但可视树随时会发生变化。
- 部分元素有自己的实例方法,在两种树中进行操作。例如Visual类包含3个protected的成员(VisualParent、VisualChildrenCount和GetVisualChild)用于验证它的可视父节点和孩子节点。FrameworkElement的Parent属性,用于呈现逻辑父节点。某些FrameworkElement的子类会以不同的方式提供它们的逻辑子元素。
- 可视树通常会被简化为元素树,因为它们既包含了逻辑树中的元素,也包含了可视树中的某些元素。术语“可视树”是用来描述任何一棵包含可是元素的字数。
3.1 逻辑树与可视树
例如:以 XAML 形式表示的一个简单的 About 对话框
<Window x:Class="AboutDialogProgram.AboutDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="About WPF Unleashed"
SizeToContent="WidthAndHeight"
Background="OrangeRed">
<StackPanel>
<Label FontWeight="Bold"
FontSize="20"
Foreground="White">
WPF Unleashed (Version 3.0)
</Label>
<Label>
© 2006 SAMS Publishing
</Label>
<Label>
Installed Chapters:
</Label>
<ListBox>
<ListBoxItem>
Chapter 1
</ListBoxItem>
<ListBoxItem>
Chapter 2
</ListBoxItem>
</ListBox>
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center">
<Button MinWidth="75"
Margin="10">
Help
</Button>
<Button MinWidth="75"
Margin="10">
OK
</Button>
</StackPanel>
<StatusBar>
You have successfully registered this product.
</StatusBar>
</StackPanel>
</Window>
例如:属性值有时会沿着树自动传递给子元素;触发的事件可以自底向上或自顶向下遍历树。
XamlPad 工具下载地址:
http://blogs.msdn.com/b/llobo/archive/2008/08/25/xamlpadx-4-0.aspx
例如:遍历和打印逻辑树和可视树
using System;
using System.Windows;
using System.Windows.Media;
using System.Diagnostics;
namespace AboutDialogProgram
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class AboutDialog : Window
{
public AboutDialog()
{
InitializeComponent();
PrintLogicalTree(0, this);
}
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
PrintVisualTree(0, this);
}
void PrintLogicalTree(int depth, object obj)
{
// 打印对象,使用前置空格表示深度
Debug.WriteLine(new string(' ', depth) + obj);
// 有时,叶子节点不是 DependencyObject,如 string
if (!(obj is DependencyObject)) return;
// 递归调用每个逻辑子节点
foreach (Object child in LogicalTreeHelper.GetChildren(obj as DependencyObject))
{
PrintLogicalTree(depth + 1, child);
}
}
void PrintVisualTree(int depth, DependencyObject obj)
{
// 打印对象,使用前置空格表示深度
Debug.WriteLine(new string(' ', depth) + obj);
// 递归调用每个可视子节点
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
PrintVisualTree(depth + 1, VisualTreeHelper.GetChild(obj, i));
}
}
}
}
注意: