WPF中有两中“树”:一种叫逻辑树(
Logical Tree
);一种叫可视化元素树(
Visual Tree
)。
Logical Tree
最显著的特点就是它完全由布局组件和控件构成(包括列表类控件中的条目元素),换句话说就是它的每个节点不是布局组件就是控件。那什么是
Visual Tree
呢?我们知道,如果把一片树叶放在放大镜下观察,你会发现这片叶子也像一棵树一样——有自己的基部并向上生长出多级分叉。
在WPF的
Logical Tree
上,充当叶子的一般都是控件,在放大镜下观察,会发现每个WPF控件本身也是一棵由更细微级别的组件组成的树。如果把
Logical Tree
延申至
Template
组件级别,我们得到的就是
Visual Tree
。
一、
LogicalTreeHelper
如果想在
Logical Tree
上导航或查找元素,可以借助
LogicalTreeHelper
类的
static
方法来实现:
BringIntoView |
尝试将请求的用户界面元素放入视图 |
FindLogicalNode |
尝试查找并返回具有指定的名称的对象。 搜索从指定的对象开始,并持续到逻辑树的子节点。 |
GetChildren |
通过处理逻辑树返回指定的对象的即时子对象集合。 |
GetParent |
通过处理逻辑树中返回指定对象的父对象。 |
二、VisualTreeHelper
GetChild |
返回子可视对象从指定的父级范围内指定的集合索引。 |
GetChildrenCount |
返回的包含指定的可视对象的子级的个数。 |
… | … |
三、测试与说明
<Window x:Class="WpfAppResource1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfAppResource1" xmlns:system="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d" Loaded="Window_Loaded"
Title="MainWindow" Height="450" Width="800">
<Grid x:Name="grid">
<TextBox x:Name="textBox">
<TextBox.Template>
<ControlTemplate TargetType="TextBox">
<Rectangle x:Name="rectangle"/>
</ControlTemplate>
</TextBox.Template>
</TextBox>
<StackPanel x:Name="stackPanel">
<Button x:Name="button" >
<CheckBox x:Name="checkBox"/>
</Button>
</StackPanel>
<DockPanel x:Name="dockPanel">
<ToggleButton x:Name="toggleButton">
<TextBlock x:Name="textBlock"/>
</ToggleButton>
</DockPanel>
<Border x:Name="border">
<RepeatButton x:Name="repeatButton"/>
</Border>
</Grid>
</Window>
namespace WpfAppResource1
{
using System.Text;
using System.Windows;
using System.Windows.Media;
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
string getTree(FrameworkElement container)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("********Logical Tree********");
getLogicalChildren(container, sb, 0);
sb.AppendLine();
sb.AppendLine("********Visual Tree********");
getVisualChildren(container, sb, 0);
sb.AppendLine();
return sb.ToString();
}
void appendLine(FrameworkElement frameworkElement, StringBuilder sb, int num)
{
sb.Append("".PadLeft(num));
string name = frameworkElement.Name;
if (string.IsNullOrWhiteSpace(name))
{
name = $"({frameworkElement.GetType().Name})";
}
sb.AppendLine($"{num}.{name}");
}
void getLogicalChildren(FrameworkElement container, StringBuilder sb, int num)
{
appendLine(container, sb, num);
foreach (var child in LogicalTreeHelper.GetChildren(container))
{
FrameworkElement frameworkElement = child as FrameworkElement;
if (frameworkElement != null)
{
getLogicalChildren(frameworkElement, sb, num + 1);
}
}
}
void getVisualChildren(FrameworkElement container, StringBuilder sb, int num)
{
appendLine(container, sb, num);
int count = VisualTreeHelper.GetChildrenCount(container);
for (int i = 0; i < count; i++)
{
FrameworkElement frameworkElement = VisualTreeHelper.GetChild(container, i) as FrameworkElement;
if (frameworkElement != null)
{
getVisualChildren(frameworkElement, sb, num + 1);
}
}
}
}
}
1. 构造函数里遍历两个树(Window)
public MainWindow()
{
InitializeComponent();
string tree = getTree(this);
}
tree结果为:
********Logical Tree********
0.(MainWindow)
1.grid
2.textBox
2.stackPanel
3.button
4.checkBox
2.dockPanel
3.toggleButton
4.textBlock
2.border
3.repeatButton
********Visual Tree********
0.(MainWindow)
2. 构造函数里遍历两个树(Grid)
public MainWindow()
{
InitializeComponent();
string tree = getTree(this.grid);
}
tree结果为:
********Logical Tree********
0.grid
1.textBox
1.stackPanel
2.button
3.checkBox
1.dockPanel
2.toggleButton
3.textBlock
1.border
2.repeatButton
********Visual Tree********
0.grid
1.textBox
1.stackPanel
2.button
1.dockPanel
2.toggleButton
1.border
2.repeatButton
3. Loaded完成后遍历两个树
private void Window_Loaded(object sender, RoutedEventArgs e)
{
string tree = getTree(this);
}
tree结果为:
********Logical Tree********
0.(MainWindow)
1.grid
2.textBox
2.stackPanel
3.button
4.checkBox
2.dockPanel
3.toggleButton
4.textBlock
2.border
3.repeatButton
********Visual Tree********
0.(MainWindow)
1.(Border)
2.(AdornerDecorator)
3.(ContentPresenter)
4.grid
5.textBox
6.rectangle
5.stackPanel
6.button
7.border
8.contentPresenter
9.checkBox
10.templateRoot
11.checkBoxBorder
12.markGrid
13.optionMark
13.indeterminateMark
11.contentPresenter
5.dockPanel
6.toggleButton
7.border
8.contentPresenter
9.textBlock
5.border
6.repeatButton
7.border
8.contentPresenter
3.(AdornerLayer)
通过对比可以发现:
- 逻辑树只能遍历出非模板的元素,可视化树可以遍历出所有属于Visual的元素
- 可视化树在界面未加载前不能遍历Window,但可以遍历Window中的元素
- 逻辑树的遍历在整个过程都可以的,而可视化树在界面没有加载显示完成后不能遍历出Content中和Template中的元素
版权声明:本文为breakbridge原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。