《深入浅出WPF》读书笔记.8路由事件
背景
路由事件是直接响应事件的变种。直接响应事件,事件触发者和事件响应者必须显示订阅。而路由事件的触发者和事件响应者之间的没有显示订阅,事件触发后,事件响应者安装事件监听器,当事件传递到此时,事件处理器进行响应,并决定事件是否继续传递。
路由事件
WPF的两种树形结构
逻辑树
逻辑树就是UI树
可视元素树
单独组件的构成元素树
事件基础
路由事件
<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>
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));}}
}
事件传递路线
自定义路由事件
自定义路由分三步
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);}}
}
<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>
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:可视化元素树
<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>
<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>
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控件。
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; }}
}
<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>
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
得加快学习速度了,记得点赞关注哦~👉⭐