zoukankan      html  css  js  c++  java
  • WPF之路路由事件

    理解路由事件
    路由事件是一种可以针对元素树中的多个侦听器而不是仅仅针对引发该事件的对象调用处理程序的事件,也就是说,触发事件源的父级或子级如果都有对该事件的监听,则都能触发事件

    路由事件与一般事件的区别在于:路由事件是一种用于元素树的事件,当路由事件触发后,它可以向上或向下遍历可视树和逻辑树,他用一种简单而持久的方式在每个元素上触发,而不需要任何定制的代码(如果用传统的方式实现一个操作,执行整个事件的调用则需要执行代码将事件串联起来)

    路由事件的路由策略
    所谓的路由策略就是指:路由事件实现遍历元素的方式

    路由事件一般使用以下三种路由策略:

    冒泡:由事件源向上传递一直到根元素
    直接:只有事件源才有机会响应事件
    隧道:从元素树的根部调用事件处理程序并依次向下深入直到事件源
    一般情况下,WPF提供的输入事件都是以隧道/冒泡对实现的。隧道事件常常被称为Preview事件

    冒泡
    程序设计思路是,将多个Grid嵌套起来,构成父子结构,在最底层支Grid上定义一个按钮,绑定一个单击事件,并且按钮的所有低级元素都绑定该事件,它们使用同一个事件处理程序;再定义一个ListBox,当事件处理程序执行的时候,打印出事件激发者的名字,这样便能看到事件执行的顺序

    下图是界面设计:

    下面是XMAL代码,按钮和每一个Grid都绑定了Button.Click="Btn_Click"

    <Window x:Class="WPF_CODE.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-->
    <Grid Button.Click="Btn_Click" Name="Grid_1" Background="#FF43AEAE">
    <!--定义两行-->
    <Grid.RowDefinitions>
    <RowDefinition></RowDefinition>
    <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>

    <!--第二层Grid-->
    <Grid Button.Click="Btn_Click" Name="Grid_2" Margin="10" Background="#FF8D888D" Grid.Row="0">
    <!--定义两列-->
    <Grid.ColumnDefinitions>
    <ColumnDefinition></ColumnDefinition>
    <ColumnDefinition></ColumnDefinition>
    </Grid.ColumnDefinitions>

    <!--第三层左侧Grid-->
    <Grid Button.Click="Btn_Click" Name="Grid_3_Left" Grid.Column="0" Background="#FF3D8F3A" Margin="10">
    <!--添加一个按钮-->
    <Button Button.Click="Btn_Click" Name="ButtonLeft" Width="80" Height="50" Content="Hello"/>
    </Grid>

    <!--第三层右侧Grid-->
    <Grid Button.Click="Btn_Click" Name="Grid_3_Right" Grid.Column="1" Background="#FFC95E3E" Margin="10">
    <!--添加一个按钮-->
    <Button Button.Click="Btn_Click" Name="ButtonRight" Width="80" Height="50" Margin="10" Content="World"></Button>
    </Grid>

    </Grid>

    <!--定义一个ListBox,用于输出结果-->
    <ListBox Name="Print_List" Grid.Row="1"/>

    </Grid>

    </Window> 
    下面是后端代码

    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 WPF_CODE
    {
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
    public MainWindow()
    {
    InitializeComponent();
    }

    // 事件处理程序
    private void Btn_Click(object sender, RoutedEventArgs e)
    {
    //sender是指由谁激发了这个事件处理程序,它可以获取到触发对象
    //通过(sender as FrameworkElement).Name转换,将触发控件的名称拿出来,
    string message = "触发者:"+(sender as FrameworkElement).Name.ToString();
    this.Print_List.Items.Add(message);

    //e包含了与事件相关的一些参数,e.Handled如果设置为True,则表示冒泡不再继续
    //e.Handled = true;
    }
    }

    运行程序,点击左侧按钮,结果如下:

    点击右侧按钮,结果如下:

    可以看出,事件首先在源元素上触发,然后从每一个元素向上沿着树传递,直到到达根元素为止(或者直到处理程序把事件标记为已处理为止),从而调用这些元素中的路由事件

    如果将事件处理程序里的e.Handled = true;代码放开,效果如下

    //e包含了与事件相关的一些参数,e.Handled如果设置为True,则表示冒泡不再继续
    e.Handled = true; 

    即事件处理程序只要被触发一次,并不会发生冒泡

    隧道
    隧道的执行顺序与冒泡正好相反,直接看例子,然后再解释,将上便中所有的Click事件改为PreviewMouseDown事件,而绑定的事件处理程序不变

    XAML代码如下:

    <Window x:Class="WPF_CODE.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-->
    <Grid PreviewMouseDown="Btn_Click" Name="Grid_1" Background="#FF43AEAE">
    <!--定义两行-->
    <Grid.RowDefinitions>
    <RowDefinition></RowDefinition>
    <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>

    <!--第二层Grid-->
    <Grid PreviewMouseDown="Btn_Click" Name="Grid_2" Margin="10" Background="#FF8D888D" Grid.Row="0">
    <!--定义两列-->
    <Grid.ColumnDefinitions>
    <ColumnDefinition></ColumnDefinition>
    <ColumnDefinition></ColumnDefinition>
    </Grid.ColumnDefinitions>

    <!--第三层左侧Grid-->
    <Grid PreviewMouseDown="Btn_Click" Name="Grid_3_Left" Grid.Column="0" Background="#FF3D8F3A" Margin="10">
    <!--添加一个按钮-->
    <Button PreviewMouseDown="Btn_Click" Name="ButtonLeft" Width="80" Height="50" Content="Hello"/>
    </Grid>

    <!--第三层右侧Grid-->
    <Grid PreviewMouseDown="Btn_Click" Name="Grid_3_Right" Grid.Column="1" Background="#FFC95E3E" Margin="10">
    <!--添加一个按钮-->
    <Button PreviewMouseDown="Btn_Click" Name="ButtonRight" Width="80" Height="50" Margin="10" Content="World"></Button>
    </Grid>

    </Grid>

    <!--定义一个ListBox,用于输出结果-->
    <ListBox Name="Print_List" Grid.Row="1"/>

    </Grid>

    </Window> 
    运行程序,查看效果:

    可以看出,隧道是指事件首先是从根元素上被触发,然后从每一个元素向下沿着树传递,直到到达根元素为止(或者直到到达处理程序把事件标记为已处理为止),他的执行方式正好与冒泡策略相反

    所有的隧道事件都以Preview开头

    后台代码记得将e.Handled=true注释掉

    直接策略
    事件仅仅在源元素上触发,这个与普通的.Net事件的行为相同,不同的是这样的事件仍然会参与一些路由事件的特定机制,如事件触发器等;该事件唯一可能的处理程序是与其挂接的委托


    ————————————————
    版权声明:本文为CSDN博主「东南有大树」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/weixin_38486884/article/details/82702090

  • 相关阅读:
    已知有一个Worker 类如下:  public class Worker  { private int age;  private String name;  private double salary;  public Worker (){}  public Worker (String nam
    设计Account 对象如下:  private long id;       private double balance;       private String password; 要求完善设计,使得该Account 对象能够自动分配id。 给定一个List 如下: &#
    有如下Student 对象,  private String name;       private int age;       private int score;   private String classNum;  其中,classNum
    写一个函数reverseList,该函数能够接受一个List,然后把该List 倒序排列。 例如:  List list = new ArrayList();  list.add(“Hello”);  list.add(“World”);  list.add(“Learn”); //此时list 为Hello World Learn  reverseList
    已知有十六支男子足球队参加2008 北京奥运会。写一个程序,把这16 支球队随机分为4 个组。采用List集合和随机数
    (Map)利用Map,完成下面的功能:  从命令行读入一个字符串,表示一个年份,输出该年的世界杯冠军是哪支球队。如果该 年没有举办世界杯,则输出:没有举办世界杯。  附:世界杯冠军以及对应的夺冠年份,请参考本章附录。 附录 (Map)在原有世界杯Map 的基础上,增加如下功能: 读入一支球队的名字,输出该球队夺冠的年份列表。 例如,读入“巴西”,应当输出 1958 1962 1970 1
    完善一下银行的相关练习
    Java--输入输出
    java 基础--接口
    (2)java基础继承
  • 原文地址:https://www.cnblogs.com/javalinux/p/14600001.html
Copyright © 2011-2022 走看看