WPF (逻辑树和可视化树)
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 | 返回的包含指定的可视对象的子级的个数。 |
| ... | ... |
三、测试与说明
namespace WpfAppResource1
{using System.Text;using System.Windows;using System.Windows.Media;/// /// MainWindow.xaml 的交互逻辑/// 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.grid2.textBox2.stackPanel3.button4.checkBox2.dockPanel3.toggleButton4.textBlock2.border3.repeatButton********Visual Tree********
0.(MainWindow)
2. 构造函数里遍历两个树(Grid)
public MainWindow(){InitializeComponent();string tree = getTree(this.grid);}
tree结果为:
********Logical Tree********
0.grid1.textBox1.stackPanel2.button3.checkBox1.dockPanel2.toggleButton3.textBlock1.border2.repeatButton********Visual Tree********
0.grid1.textBox1.stackPanel2.button1.dockPanel2.toggleButton1.border2.repeatButton
3. Loaded完成后遍历两个树
private void Window_Loaded(object sender, RoutedEventArgs e){string tree = getTree(this);}
tree结果为:
********Logical Tree********
0.(MainWindow)1.grid2.textBox2.stackPanel3.button4.checkBox2.dockPanel3.toggleButton4.textBlock2.border3.repeatButton********Visual Tree********
0.(MainWindow)1.(Border)2.(AdornerDecorator)3.(ContentPresenter)4.grid5.textBox6.rectangle5.stackPanel6.button7.border8.contentPresenter9.checkBox10.templateRoot11.checkBoxBorder12.markGrid13.optionMark13.indeterminateMark11.contentPresenter5.dockPanel6.toggleButton7.border8.contentPresenter9.textBlock5.border6.repeatButton7.border8.contentPresenter3.(AdornerLayer)
通过对比可以发现:
- 逻辑树只能遍历出非模板的元素,可视化树可以遍历出所有属于Visual的元素
- 可视化树在界面未加载前不能遍历Window,但可以遍历Window中的元素
- 逻辑树的遍历在整个过程都可以的,而可视化树在界面没有加载显示完成后不能遍历出Content中和Template中的元素
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
