《深入浅出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
得加快学习速度了,记得点赞关注哦~👉⭐