WPF中的Visual Tree和Logical Tree与路由事件

1.Visual Tree和Logical Tree
Logical Tree:逻辑树,WPF中用户界面有一个对象树构建而成,这棵树叫做逻辑树,元素的声明分层结构形成了所谓的逻辑树!!
Visual Tree:可视树(也叫视觉树),可视树是对逻辑树的扩展,可视树将逻辑树的节点打散,分放到核心棵树组件中,它表述了一些详细的可视化实现,而不是把每个元素当做一个”黑盒“。
我们以一个简单的程序来观察下逻辑树与可视树:

<Window x:Class="WpfApplication28.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>
        <Button Content="Button" HorizontalAlignment="Center" VerticalAlignment="Center"></Button>
    </Grid>
</Window>

窗口中只有一个Button,如何查看这个程序的逻辑树和可视树呢?我们可以轻松用一个工具查看到:
WPF中查看视觉树和逻辑树的工具: WPF Inspector:下载地址:http://wpfinspector.codeplex.com/
安装完成后直接双击打开WPF Inspector:

软件提示目前没有任何正在运行的WPF应用程序。现在我们启动刚才的wpf程序:

发现了名为mainWindow的应用程序,点击Attach:

左侧为可视树,右侧为逻辑树
可以看出:LogicalTree的叶子节点是构成用户界面的控件,逻辑树的结构即为Xaml中控件元素的声明层次结构,最顶级的为Application,最内层为我们创建的Button按钮。

而可视树则包含了更多直接通过Xaml看不到内容,控件中的细微结构也算上了,以Button为例,通过观察可知在Button的内部包含ButtonChrome,用于为Button创建特定于主题的外观。ButtonChrome内包含用于存放单个子控件的ContentPressenter,而Contentpressenter存放了一个TextBlock用于显示Button上的文字信息。
此外,WPF Inspector还可以查看WPF应用程序内的属性、数据绑定、资源、样式、触发器等信息。如图:

利用工具对可视树的查看可以加深对控件内部构造的理解。

2.路由事件
路由事件:一种可以在元素树中向上或向下传播,并沿着传播路径被事件处理程序处理。路由事件主要根据可视树进行路由,路由事件支持三种路由策略:冒泡、隧道、直接,因其沿着可视树进行传播,所以其与Winform中的不同在于,事件的发送者与事件的响应者不一定有直接关系。

2.1.冒泡(Bubble)路由事件:沿着可视树向上传递的路由事件

如果在MainWindow的Grid中添加一个Border,Border中添加一个TextBlock,分别为这些控件注册MouseLeftButtonDown事件,点击TextBlock:

<Window x:Class="WpfApplication32.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 x:Name="Grid1" Background="CornflowerBlue" MouseLeftButtonDown="OnMouseLeftButtonDown">
        <Border x:Name="Border1" Height="100" Width="200" Background="BurlyWood" MouseLeftButtonDown="OnMouseLeftButtonDown">
            <TextBlock x:Name="TexlBlock1" Text="TextBlock" Background="Chartreuse"  HorizontalAlignment="Center" VerticalAlignment="Center" Width="75" MouseLeftButtonDown="OnMouseLeftButtonDown"/>
        </Border>
    </Grid>
</Window>

后台代码:

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var fra = sender as FrameworkElement;
            MessageBox.Show(fra.Name);
        }
    }

首先被触发的是TextBlock的MouseLeftButtonDown事件,然后是Border,最后是Grid。
如果某个控件不支持所要路由的事件,可以用附加事件来完成,例如StackPanel不包含Click事件,则可以添加如下代码:

<StackPanel Button.Click="ButtonBase_OnClick">
        <Button Content="Button1"></Button>
        <Button Content="Button2"></Button>
        <Button Content="Button3"></Button>
        <Button Content="Button4"></Button>
        <CheckBox Content="Button5"></CheckBox>
    </StackPanel>
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var btn = e.Source as Button;
        if (btn == null) return;
        MessageBox.Show(btn.Content.ToString());
}

当点击StackPanel中任意Button,该事件均可向上传递到StackPanel中,这样就不必为StackPanel中所有的Button都注册一个点击事件了。

2.2.隧道(Tunnel)路由事件:沿着可视树向下传递的路由事件

隧道事件均以Preview开头

<StackPanel PreviewMouseLeftButtonDown="UIElement_OnPreviewMouseLeftButtonDown">
        <Button Content="Button1"></Button>
        <Button Content="Button2"></Button>
        <Button Content="Button3"></Button>
        <Button Content="Button4"></Button>
        <CheckBox Content="Button5"></CheckBox>
    </StackPanel>

在任意Button处按下鼠标左键:
sender:StackPanel
e.Source:Button
e.OriginalSource:TextBlock

注:

一般情况下,WPF提供的输入事件都是以隧道/冒泡对实现的,隧道事件总是在冒泡事件之前被触发;

如果想中断路由事件,只需在中断处添加e.Handled = true;

如果隧道事件被标记为已处理,则冒泡事件将不会再发生;(两种路由事件的RoutedEventArges是同一个)。

2.3.直接(Direct)路由事件:只有事件源才有机会响应的路由事件

事件仅在源元素上触发的路由事件,类似于普通事件的处理方式。不同之处在于因其仍然是一个路由事件,对WPF提供了更好的支持,事件仍然会参与一些路由事件的特定机制,如事件触发器、类处理机制等。

WPF中的Visual Tree和Logical Tree与路由事件,布布扣,bubuko.com

时间: 2024-10-25 01:49:59

WPF中的Visual Tree和Logical Tree与路由事件的相关文章

WPF中PreviewMouseDownEvent的系统处理:TabItem的PreviewMouseDown 事件弹框后不切换的问题调查

?? 背景: UI中有一个TabControl, 包含2个TabItem,当切换到别的TabItem时可能弹框然后根据逻辑判断是否跳转过去. 然后我就做了这样一个demo: xaml: <UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml" xmln

正确理解WPF中的TemplatedParent

(注:Logical Tree中文称为逻辑树,Visual Tree中文称为可视化树或者视觉树,由于名称不是很统一,文中统一用英文名称代表两个概念,况且VisualTreeHelper和LogicalTreeHelper也是WPF中提供的类名称) 众所周知WPF中的Logical Tree是逻辑上定义的元素层次树,而实际上显示在屏幕上的元素层次树是Visual Tree,Visual Tree是 (注:Logical Tree中文称为逻辑树,Visual Tree中文称为可视化树或者视觉树,由于

WPF 中的 loaded 事件和 Initialized 事件

在 WPF 中, 控件有 Loaded 和 Initialized 两种事件. 初始化和加载控件几乎同时发生, 因此这两个事件也几乎同时触发. 但是他们之间有微妙且重要的区别. 这些区别很容易让人误解. 这里介绍我们设计这些事件的背景. (不仅适用于 Control 类, 同样在通用类如 FrameworkElement 和 FrameworkContentElement 类也适用.) 下面是个小故事: Initialized 事件只说: 这个元素已经被构建出来,并且它的属性值都被设置好了,所以

WPF自定义路由事件(二)

WPF中的路由事件 as U know,和以前Windows消息事件区别不再多讲,这篇博文中,将首先回顾下WPF内置的路由事件的用法,然后在此基础上自定义一个路由事件. 1.WPF内置路由事件 WPF中的大多数事件都是路由事件,WPF有3中路由策略: 具体不多讲,单需要注意的是WPF路由事件是沿着VIsualTree传递的.VisualTree与LogicalTree的区别在于:LogicalTree的叶子节点是构成用户界面的控件(xaml紧密相关),而VisualTree要连控件中的细微结构也

[转]在WPF中区别TextBlock和Label

TextBlock和Label都是用来显示少量数据的.好多文章对Label存在的描述都是它允许使用"快速获取"."快速获取"就是允许你用Alt加上其它的按键快速和UI界面的某个控件交互,比如你可以用ALT加上O键来点击一个OK按钮. TextBlock直接继承于FrameworkElement,而Label继承于ContentControl.这样看来,Label可以做这样的事情: 1.可以定义一个控件模板(通过Template属性) 2.可以显示出string以外的

WPF中 ItemsSource 和DataContext不同点

此段为原文翻译而来,原文地址 WPF 中 数据绑定 ItemSource和 DataContext的不同点: 1.DataContext 一般是一个非集合性质的对象,而ItemSource 更期望数据源是 集合对象. 2.DataContext 是 FrameworkElement 类中定义的一个依赖属性(Dependency property),ItemsSource是 在ItemsControl 类中定义的.所有继承自FrameworkElement 的类(控件)都可以使用DataConte

MVVM模式解析和在WPF中的实现(三) 命令绑定

MVVM模式解析和在WPF中的实现(三) 命令绑定 0x00 命令绑定要达到的效果 命令绑定要关注的核心就是两个方面的问题,命令能否执行和命令怎么执行.也就是说当View中的一个Button绑定了ViewModel中一个命令后,什么时候这个Button是可用的,按下Button后执行什么操作.解决了这两个问题基本就实现了命令绑定.另外一个问题就是执行过程中需要的数据(参数)要如何传递.本次主要探讨这几个问题. 0x01 命令绑定的实现 自定义一个能够被绑定的命令需要实现ICommand接口.该接

MVVM设计模式和在WPF中的实现(四) 事件绑定

系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中的实现(三)命令绑定 MVVM模式解析和在WPF中的实现(四)事件绑定 MVVM模式解析和在WPF中的实现(五)View和ViewModel的通信 MVVM模式解析和在WPF中的实现(六)用依赖注入的方式配置ViewModel并注册消息 0x00 为什么要事件绑定 这个问题其实是很好理解的,因为事件是丰富多样的,单纯的命令绑定远不能覆盖所有的事件.例

在WPF中使用FontAwesome之类的字体图标

原文:在WPF中使用FontAwesome之类的字体图标 我之前在博客中介绍过几个矢量图库网站,在WPF程序中,一般接触到的矢量图标资源有XAML.SVG.字体这三种格式.XAML是标准格式就不说了,SVG并不是直接支持的,不过微软提供了Expression Design可以非常方便我们将其转换为XAML格式的资源.而对于字体,虽然WPF是直接支持的,但由于字体图标其特殊性,要将其显示为图标还是需要费点劲的.本文这里就以Font-Awesome为例,介绍一下如何在WPF中使用字体图标. 首先添加