zoukankan      html  css  js  c++  java
  • 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提供了更好的支持,事件仍然会参与一些路由事件的特定机制,如事件触发器、类处理机制等。

  • 相关阅读:
    AppCrawler自动化遍历使用详解(版本2.1.0 )
    python 接口测试1 --如何创建和打印日志文件
    通过Xshell登录远程服务器实时查看log日志
    java基础知识6-- 抽象类,抽象方法,接口,构造方法,类方法等易混淆的知识点
    java基础知识5--集合类(Set,List,Map)和迭代器Iterator的使用
    java基础知识4--数组的常用方法(Array)
    java基础知识3--如何获取资源文件(Java中获取资源文件的url)
    java基础知识2--String,StringBufffer,StringBuilder的区别
    java基础知识1--String常用方法总结
    javascript正则表达式验证密码(必须含数字字符特殊符号,长度4-16位之间)
  • 原文地址:https://www.cnblogs.com/infly123/p/3923063.html
Copyright © 2011-2022 走看看