WPF路由事件之逻辑树和可视树遍历

一、什么是逻辑树

逻辑树就是描述WPF界面元素的实际构成,它是由程序在XAML中所有的UI元素组成。最显著的特点就是由布局控件、或者其他常用的控件组成。

<Window x:Class="WpfRouteEvent.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel>
            <TextBox></TextBox>
        </StackPanel>
    </Grid>
</Window>

从上面的代码中可以看出,Window、Grid、StackPanel、TextBox其实就是XAML界面的逻辑树。

二、什么是可视树

可视树是由界面上可见的元素构成的,这些元素主要是由从Visual或者Visual3D类中派生出来的类。

上面代码中的Window、Grid、StackPanel、TextBox它们本身就包含一些由Visual或者Visual3D类派生出的一些可视树的元素来组成的。

三、逻辑树和可视树的遍历

逻辑树遍历使用LogicalTreeHelper类。
可视树遍历使用VisualTreeHelper类。

演示遍历逻辑树和可视树

1、XAML界面左边显示逻辑树,右边显示可视树,代码如下:

<Window x:Class="WpfRouteEvent.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DockPanel>
            <Button DockPanel.Dock="Top" Click="Button_Click" Content="获取逻辑树和可视树"></Button>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition></ColumnDefinition>
                    <ColumnDefinition></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <DockPanel Grid.Column="0">
                    <TextBlock DockPanel.Dock="Top" Text="逻辑树"></TextBlock>
                    <TreeView Name="tvLogicTree"></TreeView>
                </DockPanel>
                <DockPanel Grid.Column="1">
                    <TextBlock DockPanel.Dock="Top" Text="可视树"></TextBlock>
                    <TreeView Name="tvVisualTree"></TreeView>
                </DockPanel>
            </Grid>
        </DockPanel>

    </Grid>
</Window>

2、添加类,用于遍历整个XAML界面的逻辑树和可视树

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace WpfRouteEvent
{
    public class WpfTreeHelper
    {
        static string GetTypeDescription(object obj)
        {
            return obj.GetType().FullName;
        }

        /// <summary>
        /// 获取逻辑树
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static TreeViewItem GetLogicTree(DependencyObject obj)
        {
            if (obj == null)
            {
                return null;
            }
            //创建逻辑树的节点
            TreeViewItem treeItem = new TreeViewItem {Header=GetTypeDescription(obj),IsExpanded=true };

            //循环遍历,获取逻辑树的所有子节点
            foreach (var child in LogicalTreeHelper.GetChildren(obj))
            {
                //递归调用
                var item = GetLogicTree(child as DependencyObject);
                if (item != null)
                {
                    treeItem.Items.Add(item);
                }
            }

            return treeItem;
        }

        /// <summary>
        /// 获取可视树
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static TreeViewItem GetVisualTree(DependencyObject obj)
        {
            if (obj == null)
            {
                return null;
            }

            TreeViewItem treeItem = new TreeViewItem { Header=GetTypeDescription(obj),IsExpanded=true};

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            {
                var child = VisualTreeHelper.GetChild(obj, i);
                var item = GetVisualTree(child);
                if (item != null)
                {
                    treeItem.Items.Add(item);
                }
            }

            return treeItem;
        }
    }
}

3、在按钮的点击事件中将获取的逻辑树和可视树添加到XAML界面中

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfRouteEvent
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            this.tvLogicTree.Items.Add(WpfTreeHelper.GetLogicTree(this));
            this.tvVisualTree.Items.Add(WpfTreeHelper.GetVisualTree(this));
        }
    }
}

4、点击按钮,界面运行效果

到此这篇关于WPF路由事件之逻辑树和可视树遍历的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • WPF简介与基础开发

    一.WPF简介 WPF:WPF即Windows Presentation Foundation,翻译为中文“Windows呈现基础”,是微软推出的基于Windows Vista的用户界面框架,属于.NET Framework 3.0的一部分.它提供了统一的编程模型.语言和框架,真正做到了分离界面设计人员与开发人员的工作:同时它提供了全新的多媒体交互用户图形界面. 二.WPF特点 1.统一的编程模型 WPF提供的编程模型统一普通控件.语音.视频.文档3D等技术,这些媒体类型能够统一协调工作,降低了

  • WPF使用DockPanel停靠面板布局

    DockPanel:停靠面板 DockPanel定义一个区域,在此区域中,您可以使子元素通过描点的形式排列,这些对象位于 Children 属性中.停靠面板类似于WinForm中控件的Dock属性.DockPanel会对每个子元素进行排序,并将根据指定的边进行停靠,多个停靠在同侧的元素则按顺序排序.在DockPanel中,指定停靠边的控件,会根据定义的顺序占领边角,所有控件绝不会交叠. 默认情况下,后添加的元素只能使用剩余空间,无论对DockPanel的最后一个子元素设置任何停靠值,该子元素都将

  • WPF依赖属性用法详解

    一.什么是依赖属性 依赖属性就是一种自己可以没有值,并且可以通过绑定从其他数据源获取值.依赖属性可支持WPF中的样式设置.数据绑定.继承.动画及默认值. 将所有的属性都设置为依赖属性并不总是正确的解决方案,具体取决于其应用场景.有时,使用私有字段实现属性的典型方法便能满足要求.MSDN中给出了下面几种应用依赖属性的场景: 1. 希望可在样式中设置属性. 2. 希望属性支持数据绑定. 3. 希望可使用动态资源引用设置属性. 4. 希望从元素树中的父元素自动继承属性值. 5. 希望属性可进行动画处理

  • WPF使用Canvas画布面板布局

    Canvas:画布面板 画布,用于完全控制每个元素的精确位置.他是布局控件中最为简单的一种,直接将元素放到指定位置,主要来布置图面.使用Canvas,必须指定一个子元素的位置(相对于画布),否则所有元素都将出现在画布的左上角.调整位置用Left.Right.Top和Bottom四个附加属性.如果Canvas是窗口主元素(即最外层的布局面板是Canvas),用户改变窗口大小时,Canvas也会随之变化,子元素的位置也会随之移动,以保证相对于Canvas的位置属性不变. Canvas允许子元素的部分

  • WPF使用Grid网格面板布局

    Grid:网格面板 Grid顾名思义就是“网格”,以表格形式布局元素,对于整个面板上的元素进行布局,它的子控件被放在一个一个事先定义好的小格子里面,整齐配列. Grid和其他各个Panel比较起来,功能最多也最为复杂.要使用Grid,首先要向RowDefinitions和ColumnDefinitions属性中添加一定数量的RowDefinitions和 ColumnDefinitions元素,从而定义行数和列数.而放置在Grid面板中的控件元素都必须显示采用Row和Column附加属性定义其放

  • WPF使用StackPanel栈面板布局

    应用程序界面设计中,合理的元素布局至关重要,它可以方便用户使用,并将信息清晰合理地展现给用户.WPF提供了一套功能强大的工具-面板(Panel),来控制用户界面的布局.你可以使用这些面板控件来排布元素.如果内置布局控件不能满足需要的话,还可以创建自定义的布局元素. 面板(Panel) WPF用于布局的面板主要有6个,StackPanel(栈面板).WrapPanel(环绕面板).DockPanel(停靠面板).Canvas(画布).Grid(网格面板)和UniformGrid(均布网格). St

  • WPF使用WrapPanel环绕面板布局

    WrapPanel:环绕面板 WrapPanel布局面板将各个控件从左至右按照行或列的顺序罗列,当长度或高度不够时就会自动调整进行换行,后续排序按照从上至下或从右至左的顺序进行. Orientation——根据内容自动换行.当Orientation属性的值设置为 Horizontal:元素是从左向右排列的,然后自上至下自动换行.当Orientation属性的值设置为Vertical:元素是从上向下排列的,然后从左至右自动换行. ItemHeight——所有子元素都一致的高度.每个子元素填充高度的

  • WPF路由事件之逻辑树和可视树遍历

    一.什么是逻辑树 逻辑树就是描述WPF界面元素的实际构成,它是由程序在XAML中所有的UI元素组成.最显著的特点就是由布局控件.或者其他常用的控件组成. <Window x:Class="WpfRouteEvent.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/win

  • WPF路由事件中的三种策略介绍

    什么是路由事件 路由事件是具有更强传播能力的事件,它可以在元素树中向上冒泡和向下隧道传播,并且能够沿着传播路径被事件处理程序来处理. 路由事件允许事件在某个元素上被处理,即使这个事件源自于另外一个元素.事件路由允许某个元素的事件由另外一个元素引发. 路由事件是一种可以针对元素树中的多个侦听器而不是仅仅针对引发该事件的对象调用处理程序的事件.路由事件是一个CLR事件. 路由事件与一般事件的区别在于:路由事件是一种用于元素树的事件,当路由事件触发后,它可以向上或向下遍历可视树和逻辑树,他用一种简单而

  • 详解WPF中的隧道路由和冒泡路由事件

    WPF中使用路由事件升级了传统应用开发中的事件,在WPF中使用路由事件能更好的处理事件相关的逻辑,我们从这篇开始整理事件的用法和什么是直接路由,什么是冒泡路由,以及什么是隧道路由. 事件最基本的用法 在基于事件驱动的开发中,把代码放在响应注册的事件的处理函数内,比如Click事件.MouseDown事件.MouseUp事件等等.每个控件响应自己的注册事件,有很多如果在事件上有相互关联和影响的事件,就要在一个业务逻辑里写比较多的代码.而路由事件主要的优势就是路由事件可以在元素树上进行传递,并且沿着

  • WPF自定义路由事件

    与依赖项属性类似,WPF也为路由事件提供了WPF事件系统这一组成.为一个类型添加一个路由事件的方式与为类型添加依赖项属性的方法类似,添加一个自定义路由事件的步骤: 一.声明路由事件变量并注册 定义只读的静态变量字段RouteEvent类来声明一个变量,然后使用EventManager的RegisterRoutedEvent()方法向事件系统注册路由事件,该方法的签名如下: public static RoutedEvent RegisterRoutedEvent(string name, Rou

  • WPF自定义路由事件的实例教程

    目录 路由事件模型 [分析代码] [自定义路由事件] 总结 路由事件模型 传统的简单事件模型中,在消息激发是将消息通过事件订阅的然后交给事件的相应者,事件的相应者使用事件的处理器来做出相应,这样就存在一个问题,用户控件内部的事件就不能被外界订阅,因为事件的宿主必须能够直接访问到事件的响应者. 路由事件的事件拥有者和事件的相应者之间则没有直接的显式订阅关系,事件的拥有者则只负责激发事件,事件将有谁相应它并不知道,事件的响应者则有事件的监听器,针对事件进行监听,当有此类事件传递至此事件响应者就使用事

  • vue路由事件beforeRouteLeave及组件内定时器的清除方法

    背景 之前在做ADTS项目的时候,首页是一个实时监测的页面,需要每隔5秒更新一次数据,而业务逻辑上的需要先请求告警列表的接口的,从告警列表中拿到数据再去获取其他的数据,所以我在获取告警列表的方法中增加了一个定时器. 最开始我是将定时器声明在组件内部的(由于页面不多,数据关联也不太强,所以项目中没有使用store进行数据管理),后来清除的时候发现无法清除,后来索性声明到window下了.然后在组件内部写了如下代码: 发现也没能将定时器清除,后来走debug发现根本就没有进到这个方法中去,然后就去问

  • JS之if语句对接事件动作逻辑(详解)

    if 函数的实现步骤: function +名字() 指定id , 指定开关(display: none or block) if + else 构成逻辑 控制开关 决定在哪里安置一个灯泡, 指定一个id给某个标签 把开关用电线连着灯泡, 安装开关#+id名称{ 属性1= 赋值, 属性 2 = 赋值 , 属性3 = 赋值 } 所有的赋值都可以成为一个开关. 如果是一个手动版的, 这里已经完成了. 更改属性的赋值就可以变更id的样式 帮开关装上感应元件, 一旦有动静, 开关便自动switch 建立

  • Trie树_字典树(字符串排序)简介及实现

    1.综述 又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是:利用字符串的公共前缀来节约存储空间,最大限度地减少无谓的字符串比较,查询效率比哈希表高. Trie树结构的优点在于:1) 不限制子节点的数量: 2) 自定义的输入序列化,突破了具体语言.应用的限制,成为一个通用的框架: 3) 可以进行最大Tokens序列长度的限制:4) 根据已定阈值输出重复的字符串:5) 提

  • C++实现哈夫曼树简单创建与遍历的方法

    本文以实例形式讲述了C++实现哈夫曼树简单创建与遍历的方法,比较经典的C++算法. 本例实现的功能为:给定n个带权的节点,如何构造一棵n个带有给定权值的叶节点的二叉树,使其带全路径长度WPL最小. 据此构造出最优树算法如下: 哈夫曼算法: 1. 将n个权值分别为w1,w2,w3,....wn-1,wn的节点按权值递增排序,将每个权值作为一棵二叉树.构成n棵二叉树森林F={T1,T2,T3,T4,...Tn},其中每个二叉树都只有一个权值,其左右字数为空 2. 在森林F中选取根节点权值最小二叉树,

  • 判断整数序列是否为二元查找树的后序遍历结果的解决方法

    题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果.如果是返回true,否则返回false.例如输入5.7.6.9.11.10.8,由于这一整数序列是如下树的后序遍历结果.    8    / \  6   10 / \    / \ 5  7 9 11因此返回true.如果输入7.4.6.5,没有哪棵树的后序遍历的结果是这个序列,因此返回false.本题网上已经有用递归单纯判断的解法. 个人解法: 先得到序列对应的中序序列, 然后看中序序列是否从小到大有序, 得出判断. 相比

随机推荐