WPF中的可视化树(VisualTree)和逻辑树(LogicalTree)

news/2024/11/13 9:31:23/

可视化树和逻辑树

我们先来理解一下什么是可视化树和逻辑树。

可视化树:包含最初指定的大多数元素(在XAML或.cs中)以及控件模板中的元素。

通俗点来讲,就是整个元素的构成树,从最上面的结点到最后一个结点(包括控件模板)。

逻辑树:是可视化树的一个子集,它省略了控件模板中的元素。

通俗点来讲,就是不包含控件模板的可视化树

以上的解释仅仅用于简单理解这两个概念以及区别,完整的解释请参考文末的链接,MSDN上的文档将会更准确。

下面我们创建一个Window来演示,XAML如下:

1 <Window>
3         <Grid Name="grid" Grid.Column="1">
4             <Button HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,80,0" Grid.Row="1" Content="获取可视化树" Click="GetVisualTree_Click"></Button>
5             <Button HorizontalAlignment="Right" VerticalAlignment="Center" Content="获取逻辑树" Grid.Row="1" Click="GetLogicalTree_Click"></Button>
6         </Grid>
7 </Window>

在WPF中提供了VisualTreeHelper 和 LogicalTreeHelper 两个类来对可视化树和逻辑树进行操作。

使用VisualTreeHelper来获取Grid的可视化树

 1  public void PrintVisualTree(DependencyObject parent, int level)2  {3      string typeName = parent.GetType().FullName ?? parent.GetType().Name;4      string name = (string)(parent.GetValue(FrameworkElement.NameProperty) ?? "");5      AppendText("           ".Substring(0, level * 2));6      AppendText($"{typeName}:",true);7      AppendText($" {name}");8      AppendText(Environment.NewLine);9      for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i)
10      {
11          DependencyObject child = VisualTreeHelper.GetChild(parent, i);
12          PrintVisualTree(child, level + 1);
13      }
14  }

输出结果如下:

可以看到Grid的视觉树不仅打印了两个Button,还打印了Button的控件模板。

Button控件的控件模板如下:

1 <Border TextBlock.Foreground="{TemplateBinding Foreground}"
2                 x:Name="Border"
3                 CornerRadius="2"
4                 BorderThickness="1">
5                   <ContentPresenter Margin="2"
6                             HorizontalAlignment="Center"
7                             VerticalAlignment="Center"
8                             RecognizesAccessKey="True" />
9 </Border>

使用LogicalTreeHelper来获取Grid的逻辑树

 1 public void PrintLogicalTree(object parent, int level)2 {3     var parentObj = parent as DependencyObject;4     var typeName = parent.GetType().FullName;5     var name = "";6     if (parentObj != null)7     {8         name = (string)(parentObj.GetValue(FrameworkElement.NameProperty) ?? "");9     }
10     else
11     {
12         name = parent.ToString();
13     }
14 
15     AppendText(GetIndentString().Substring(0, level * 2));
16     AppendText($"{typeName}:", true);
17     AppendText($" {name}");
18     AppendText(Environment.NewLine);
19 
20     if (parentObj == null)
21         return;
22 
23     var children = LogicalTreeHelper.GetChildren(parentObj);
24     foreach (object child in children)
25     {
26         PrintLogicalTree(child, level + 1);
27     }
28 }

运行结果如下:

可以看到Grid的逻辑树只显示到了Button的内容这一层。

可视化树的用途

在上面的Button模板中,我们定义了一个Border,并命名为Border,

如果我们想查找这个控件,并为之添加事件处理程序或设置一些属性,都可以通过可视化树来实现。 

首先我们定义一个遍历可视化树的方法

 1  public Visual EnumVisual(Visual myVisual,string controlName)2  {3      for (int i = 0; i < VisualTreeHelper.GetChildrenCount(myVisual); i++)4      {5          6          Visual childVisual = (Visual)VisualTreeHelper.GetChild(myVisual, i);7          var nameObj = childVisual.GetValue(NameProperty);8 9          if (nameObj != null && nameObj.ToString() == controlName)
10              return childVisual;
11 
12          EnumVisual(childVisual,controlName);
13      }
14 
15      return null;
16  }

然后我们查找Border,并设置它的CornerRadius属性

 1 //查找控件模板中的名称为Border的控件2 var border = EnumVisual(this.btn1, "Border");3 if (border != null)4 {5     var borderObj = border as Border;6 7     if (borderObj != null)8     {9         borderObj.CornerRadius = new CornerRadius(10);
10     }
11 }

运行效果

实际上我们还可以通过FrameworkTemplate.FindName方法进行查找,使用方法如下:

1 object findObj = this.btn1.Template.FindName("Border",this.btn1);

FrameworkTemplate.FindName内部没有直接调用VisualTreeHelper,它是利用缓存查找的。

逻辑树的用途

借助逻辑树,内容模型可以方便地循环访问其可能的子对象,从而实现扩展。

FrameworkElement提供了一个FindName的方法,可以通过名称查找子对象

1 public object FindName(string name);

它内部的实现实际就是借助的逻辑树

1 internal object FindName(string name, out DependencyObject scopeOwner)
2 {
3     INameScope scope = FindScope(this, out scopeOwner);
4     if (scope != null)
5     {
6         return scope.FindName(name);
7     }
8     return null;
9 }

 1 internal static INameScope FindScope(DependencyObject d, out DependencyObject scopeOwner)2 {3     while (d != null)4     {5         INameScope scope = NameScope.NameScopeFromObject(d);6         if (scope != null)7         {8             scopeOwner = d;9             return scope;
10         }
11         DependencyObject parent = LogicalTreeHelper.GetParent(d);
12         d = (parent != null) ? parent : Helper.FindMentor(d.InheritanceContext);
13     }
14     scopeOwner = null;
15     return null;
16 }

示例代码

参考链接:

https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/advanced/trees-in-wpf?view=netframeworkdesktop-4.8


http://www.ppmy.cn/news/1514309.html

相关文章

数据结构8.16

1、顺序表实现学生管理系统&#xff08;参照顺序表技能&#xff09;写出菜单界面switch选择&#xff0c;功能1创建顺序表&#xff08;堆区&#xff09;&#xff0c;2录入学生信息&#xff0c;3插入一个学生信息&#xff0c;4删除一个学生信息&#xff0c;5按照位置修改一个学生…

H7-TOOL脱机烧录的UID加密操作方法,支持一键生成目标板C代码,方便大家轻松操作(2024-08-20,已发布)

UID加密使用比较方便&#xff0c;对应的C代码模板已经做好&#xff0c;使用TOOL上位机生成后&#xff0c;直接复制粘贴到自己的工程即可使用。返回1表示解密成功&#xff0c;返回0表示失败。 【UID加密原理】 1、烧录器在烧录芯片时&#xff0c;按照指定的算法将UID码编码为…

Scratch编程环境:探索暗模式与可访问性选项的创新之路

标题&#xff1a;Scratch编程环境&#xff1a;探索暗模式与可访问性选项的创新之路 Scratch&#xff0c;这款由麻省理工学院媒体实验室开发的编程工具&#xff0c;以其独特的图形化编程界面&#xff0c;降低了编程学习的门槛&#xff0c;让全球的儿童和青少年能够轻松地进入编…

RabbitMQ-消息队列之topic使用

1、安装rabbitmq 怎么安装rabbitmq请查看之前课程&#xff0c;如果已经安装&#xff0c;请略过此步。 2、创建vendor文件夹或是直接采用PHP框架 mkdir vendor 3、进入文件 cd vendor 4、安装php扩展 composer require php-amqplib/php-amqplib 5、进入上级创建topic文件…

书生大模型实战营-进阶关卡-4-InternVL 多模态模型部署微调实践

参考文档 https://github.com/InternLM/Tutorial/blob/camp3/docs/L2/InternVL/joke_readme.md InternVL简介 InternVL 是一种用于多模态任务的深度学习模型&#xff0c;旨在处理和理解多种类型的数据输入&#xff0c;如图像和文本。它结合了视觉和语言模型&#xff0c;能够执…

初识网络--网络基础概念

目录 1 网络的发展 2 协议 ​编辑 3 网络传输的流程 局域网 跨网络通信 1 网络的发展 计算机是被人设计出来&#xff0c;为人提供计算服务的&#xff0c;而人是需要协作的&#xff0c;那么就注定计算机之间也必须要协作&#xff0c;计算机之间的协作就是靠互通数据来完成…

【R语言】基于多模型的变量重要性图 (Variable Importance Plots)

变量重要性图 Variable Importance Plots 1. 写在前面2.1数据导入2.2 模型训练2.3 变量重要性2.4 变量重要性图2.5 模型模拟验证3.基于caret包计算变量重要性 1. 写在前面 好久没有更新博客了&#xff0c;正好最近在帮老师做一个项目&#xff0c;里面涉及到了不同环境变量的重要…

力扣221题详解:最大正方形的多种解法与模拟面试问答

在本篇文章中&#xff0c;我们将详细解读力扣第221题“最大正方形”。通过学习本篇文章&#xff0c;读者将掌握如何使用多种方法来解决这一问题&#xff0c;并了解相关的复杂度分析和模拟面试问答。每种方法都将配以详细的解释&#xff0c;以便于理解。 问题描述 力扣第221题…