《深入浅出WPF》读书笔记.8路由事件

《深入浅出WPF》读书笔记.8路由事件

背景

路由事件是直接响应事件的变种。直接响应事件,事件触发者和事件响应者必须显示订阅。而路由事件的触发者和事件响应者之间的没有显示订阅,事件触发后,事件响应者安装事件监听器,当事件传递到此时,事件处理器进行响应,并决定事件是否继续传递。

路由事件

WPF的两种树形结构

逻辑树

逻辑树就是UI树

可视元素树

单独组件的构成元素树

事件基础

路由事件

cs 复制代码
<Window x:Class="RouteEventDemo.RouteEventDemo1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:RouteEventDemo"
        mc:Ignorable="d"
        Title="RouteEventDemo1" Height="450" Width="600">
    <Grid x:Name="gridRoot" Background="LightSalmon" ButtonBase.Click="gridRoot_Click">
        <Grid x:Name="gridA">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100*"></ColumnDefinition>
                <ColumnDefinition Width="100*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Canvas Grid.Column="0">
                <Button x:Name="btn1" Width="100" Height="40" Content="leftButton" Canvas.Left="100" Canvas.Top="197"></Button>
            </Canvas>
            <Canvas Grid.Column="1">
                <Button x:Name="btn2" Width="100" Height="40" Content="rightButton" Canvas.Left="100" Canvas.Top="197"/>
            </Canvas>
        </Grid>
    </Grid>
</Window>
cs 复制代码
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.Shapes;

namespace RouteEventDemo
{
    /// <summary>
    /// RouteEventDemo1.xaml 的交互逻辑
    /// </summary>
    public partial class RouteEventDemo1 : Window
    {
        public RouteEventDemo1()
        {
            InitializeComponent();
            //this.gridRoot.AddHandler(Button.ClickEvent, new RoutedEventHandler(btn_Clicked));
        }

        private void btn_Clicked(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(string.Format("OriginalSource:{0},Source:{1}",(e.OriginalSource as FrameworkElement).Name,(e.Source as FrameworkElement).Name));
        }

        private void gridRoot_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(string.Format("OriginalSource:{0},Source:{1}", (e.OriginalSource as FrameworkElement).Name, (e.Source as FrameworkElement).Name));
        }
    }
}

事件传递路线

自定义路由事件

自定义路由分三步

cs 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace RouteEventDemo
{
    public class TimeButton : Button
    {
        //声明注册路由事件
        public static readonly RoutedEvent ReportTimeEvent = EventManager.RegisterRoutedEvent("ReportTime", RoutingStrategy.Tunnel, typeof(EventHandler<ReportTimeEventArgs>), typeof(TimeButton));

        //CLR事件包装器
        public event RoutedEventHandler ReportTime
        {
            add { this.AddHandler(ReportTimeEvent, value); }
            remove { this.RemoveHandler(ReportTimeEvent, value); }
        }

        //激发路由事件
        protected override void OnClick()
        {
            //保证原有功能
            base.OnClick();

            ReportTimeEventArgs args = new ReportTimeEventArgs(ReportTimeEvent, this);
            args.ClickTime = DateTime.Now;
            this.RaiseEvent(args);

        }
    }
}
cs 复制代码
<Window x:Class="RouteEventDemo.UserDefinedRouteEventDemo"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:RouteEventDemo"
        mc:Ignorable="d"
        local:TimeButton.ReportTime="ReportTimeHandler"
        Title="UserDefinedRouteEventDemo" Height="450" Width="600">
    <Grid x:Name="grd1" local:TimeButton.ReportTime="ReportTimeHandler">
        <Grid x:Name="grd2" local:TimeButton.ReportTime="ReportTimeHandler">
            <Grid x:Name="grd3" local:TimeButton.ReportTime="ReportTimeHandler">
                <StackPanel x:Name="sp1" local:TimeButton.ReportTime="ReportTimeHandler">
                    <ListBox x:Name="lb1" local:TimeButton.ReportTime="ReportTimeHandler"></ListBox>
                    <local:TimeButton local:TimeButton.ReportTime="ReportTimeHandler" Width="100" Height="40" Content="ReportTime"></local:TimeButton>
                </StackPanel>
            </Grid>
        </Grid>
    </Grid>
</Window>
cs 复制代码
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.Shapes;

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

        private void ReportTimeHandler(object sender, ReportTimeEventArgs e)
        {
            FrameworkElement frameworkElement = sender as FrameworkElement;
            if (frameworkElement != null)
            {
                string timeStr = e.ClickTime.ToString();
                string content = string.Format("{0}到达{1}", timeStr, frameworkElement.Name);
                this.lb1.Items.Add(content);
            }
            //指定到某个元素停止
            if (frameworkElement.Name == "grd2")
            {
                e.Handled = true;
            }
        }
    }
}

OriginalSource和Source

Source:元素树

OriginalSource:可视化元素树

cs 复制代码
<UserControl x:Class="RouteEventDemo.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:RouteEventDemo"
             mc:Ignorable="d" 
             d:DesignHeight="40" d:DesignWidth="120">
    <Border BorderBrush="Orange" CornerRadius="3" BorderThickness="5">
        <Button x:Name="innerBtn" Content="OK"></Button>
    </Border>
</UserControl>
cs 复制代码
<Window x:Class="RouteEventDemo.SourceDemo"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:RouteEventDemo"
        mc:Ignorable="d"
        Title="SourceDemo" Height="450" Width="800">
    <Grid x:Name="gd1">
        <local:UserControl1 x:Name="myUc" Margin="5"></local:UserControl1>
    </Grid>
</Window>
cs 复制代码
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.Shapes;

namespace RouteEventDemo
{
    /// <summary>
    /// SourceDemo.xaml 的交互逻辑
    /// </summary>
    public partial class SourceDemo : Window
    {
        public SourceDemo()
        {
            InitializeComponent();
            this.AddHandler(Button.ClickEvent, new RoutedEventHandler(btn_Clicked));
        }

        private void btn_Clicked(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(string.Format("OriginalSource:{0},Source:{1}", (e.OriginalSource as FrameworkElement).Name, (e.Source as FrameworkElement).Name));
        }
    }
}

附加事件

附加事件和路由事件的区别在于宿主是否为UI控件。

cs 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace RouteEventDemo
{
    public class Student
    {
        public static readonly RoutedEvent NameChangedEvent = EventManager.RegisterRoutedEvent("NameChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Student));

        public static void AddNameChangedHandler(DependencyObject o, RoutedEventHandler h)
        {
            UIElement element = o as UIElement;
            if (element != null)
            {
                element.AddHandler(NameChangedEvent, h);
            }
        }

        public static void RemoveNameChangedHandler(DependencyObject o,RoutedEventHandler h)
        {
            UIElement element = o as UIElement;
            if (element != null)
            {
                element.RemoveHandler(NameChangedEvent, h);
            }
        }

        public string Name { get; set; }

        public int Id { get; set; }

    }
}
cs 复制代码
<Window x:Class="RouteEventDemo.AttachedEventDemo"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:RouteEventDemo"
        mc:Ignorable="d"
        Title="AttachedEventDemo" Height="450" Width="600">
    <Grid x:Name="grdMain">
        <Button x:Name="btn1" Content="点击一下" Width="120" Height="40" Click="btn1_Click"></Button>
    </Grid>
</Window>
cs 复制代码
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.Shapes;

namespace RouteEventDemo
{
    /// <summary>
    /// AttachedEventDemo.xaml 的交互逻辑
    /// </summary>
    public partial class AttachedEventDemo : Window
    {
        public AttachedEventDemo()
        {
            InitializeComponent();
            //this.grdMain.AddHandler(Student.NameChangedEvent, new RoutedEventHandler(StudentNameChangedEventHandler));
            Student.AddNameChangedHandler(this.grdMain, new RoutedEventHandler(StudentNameChangedEventHandler));
        }

        private void StudentNameChangedEventHandler(object sender, RoutedEventArgs e)
        {
            MessageBox.Show((e.OriginalSource as Student).Id.ToString());
        }

        private void btn1_Click(object sender, RoutedEventArgs e)
        {
            Student student = new Student() { Id = 1, Name = "Tom" };
            student.Name = "Tim";
            RoutedEventArgs args = new RoutedEventArgs(Student.NameChangedEvent,student);
            this.btn1.RaiseEvent(args);

        }
    }
}

git地址

GitHub - wanghuayu-hub2021/WpfBookDemo: 深入浅出WPF的demo

得加快学习速度了,记得点赞关注哦~👉⭐

相关推荐
samble4 小时前
从零搭建灌装监控系统(十七):LiveCharts 实时温度曲线
c#·wpf·mvvm·modbus·工业控制
别动我齐刘海5 小时前
C++ 工程化学习
java·linux·前端·c++·学习·机器学习·机器人
志尊宝6 小时前
Vue3 零基础每日笔记(071):暗黑模式与主题切换——CSS 变量 + Element Plus 暗黑方案
前端·css·笔记·vue·html·前端开发·软件开发
沪上企服通7 小时前
历史账套科目重映射与规则版本重算:旧账梳理的“口径复位”工程
笔记·架构
gjf05_058 小时前
人该怎样活着呢?版本75.8
学习
li星野10 小时前
【学习记录】PCB与PCBA核心知识全解析:从设计到制造的关键细节
学习·制造
卿卿的产品经理日记10 小时前
【AI产品经理实战】Day 35|从“沉迷工具“到回归 AI 底层:Skill 收官、系统复盘与下一个 13 天
人工智能·学习·产品经理
一条泥憨鱼11 小时前
【从0开始学习计算机网络】| ARP 协议:地址解析,ARP 欺骗,链路层寻址
网络·后端·学习·计算机网络
HY小宝F11 小时前
树莓派智能摄像头系统 30 天重学计划之 Day 04 · HTTP / API:控制链入口 —— 学习总结
网络协议·学习·http
Go Work12 小时前
2026 Cadence SPB 24.1中文版安装图文详解(附安装包)
经验分享·笔记