第5章-路由事件

news/2025/2/26 18:42:38/文章来源:https://www.cnblogs.com/myshiqiqi/p/18739346

Handler: 处理器
Preview: 预览、这指隧道
Raise: 引发
Bubble: 冒泡
Handled: 已处理

理解路由事件

事件路由允许源自某个元素的事件由另一个元素引发。

定义、注册和封装路由事件

public partial class Window1 : Window
{// 定义路由事件,必须是 static readonly// 类型 RoutedEventpublic static readonly RoutedEvent TestEvent;public Window1(){InitializeComponent();}// 也可以写在外面吧static Window1(){// 在静态构造函数中注册路由事件// 使用 EventManager.RegisterRoutedEvent 注册Window1.TestEvent = EventManager.RegisterRoutedEvent("Click",  // 事件名称RoutingStrategy.Bubble,  // 路由类型 Bubble:冒泡typeof(RoutedEventHandler),  // 定义事件处理程序语法的委托typeof(Window1)  // 拥有事件的类);}// 路由事件通过普通的 .NET 事件进行封装// 两个封装器:AddHandler 和 RemoveHandler// 这两个方法都在 FrameworkElement 基类中定义,并被每个 WPF 元素继承public event RoutedEventHandler Click{add { base.AddHandler(Window1.TestEvent, value)}remove { base.RemoveHandler(Window1.TestEvent, value)}}
}

👀 RoutingStrategy.Bubble 是枚举值,指示是冒泡路由还是隧道路由

参考文章:如何创建自定义路由事件 - WPF .NET | Microsoft Learn

共享路由事件

UIElement.MouseUpEvent = Mouse.MouseUpEvent.AddOwer(typeof(UIElement));

UIElement 类是所有普通元素的起点,通过 AddOwer() 方法征用事件,可令路由事件在类之间共享。

引发路由事件

RaiseEvent() 方法负责为每个已经通过 AddHandler() 方法注册的调用程序引发事件。

protected void OnTestEvent()
{RoutedEventArgs e = new RoutedEventArgs(Window1.TestEvent, this);base.RaiseEvent(e);
}

RoutedEventArgs 是事件参数类的基类。

处理路由事件

即关联事件处理程序。引发事件以后需要做出相应的处理。

方式一:
XAM 标记添加事件特性:(以“元素名_事件名”命名事件处理方法,没有名字的话使用“元素类型_事件名”)

<Image Name="img" MouseUp="img_MouseUp"/>

方式二:

img.MouseUp += new MouseButtonEventHandloer(img_MouseUp);
// 等效于
img.MouseUp += img_MouseUp; // 隐藏委托

方式三:

img.AddHandler(Image.MouseUpEvent, new MouseButtonEventHandler(img_MouseUp));
// 等效于
img.AddHandler(UIElement.MouseUpEvent, new MouseButtonEventHandler(img_MouseUp));

断开路由事件的几种写法:

img.MouseUp -= img_MouseUp;
img.RemoveHandler(Image.MouseUpEvent, new MouseButtonEventHandler(img_MouseUp));

事件路由

路由事件分三种:

  • 直接路由事件
  • 向上传递的冒泡路由事件
  • 向下传递的隧道路由事件

可以在路由路径中任意一层完成对事件的处理

每个冒泡路由事件都对应有一个以Privew开关的隧道路由事件。

private void img_MouseUp(object sender, MouseButtonEventArgs e)
{}

sender 参数提供了对整个链条上最后那个链接的引用。(就是最后一个对象)

⭐RoutedEventArgs 类属性说明:

  • Source: 指示引发了事件的对象;
  • OriginalSource: 指示最初是什么对象引发了事件。💡 OriginalSource 属性值通常与 Source 属性值相同。
  • RoutedEvent: 通过事件处理程序为触发的事件提供 RoutedEvent 对象。
  • Handled: 该属性允许终止事件的冒泡或隧道过程。

冒泡路由事件

protected int eventCounter = 0;private void SomethingClicked(object sender, MouseButtonEventArgs e)
{eventCounter++;string message = "#" + eventCounter.ToString() + "\r\n" +" Sender: " + sender.ToString() + "\r\n" +" Source: " + e.Source + "\r\n" +" Original Source: " + e.OriginalSource;lstMessages.Items.Add(message);e.Handled = (bool)chkHandle.IsChecked; // 当为 true 时,终止路由传递
}

💡 大多数 WPF 元素没有提供 Click 事件,而是提供了更直接的 MouseDown 和 MouseUp 事件, Click 事件专用于基于按钮的控件。

❓处理挂起的事件

当设置第三个参数为 true 时,即使设置了 Handled 标志,也将接收到事件。
❌ 这一般是一种错误的做法,系统默认会挂起 MouseUp 事件而优先去处理 Click 事件。

cmdClear.AddHandler(UIElement.MouseUpEvent, new MouseButtonEventHandler(cmdClear_MouseUp), true);

⭐附加事件

<Grid Width="300"Height="300"Background="AliceBlue" Button.Click="DoSomething"><Grid Width="200"Height="200"Background="Yellow"><StackPanel Width="100"Height="100"Background="LightGreen"><Button Content="Btn1" /><Button Content="Btn2" /><Button Content="Btn3" /></StackPanel></Grid></Grid>

Button.Click="DoSomething" 就是附加事件,可以在 Grid 层处理按钮的路由事件。

💡 Button.Click="DoSomething" 也可以写成 ButtonBase.Click="DoSomething",但是,因为 RadioButton、CheckBox 也继承自 ButtonBase,因此如果只想处理 Button 对象的事件,则需要明确使用第一种写法。

在代码中关联附加事件

在代码中不能使用 += 关联附加事件,需要使用AddHandler()

pnlButtons.AddHandler(Button.Click, new RoutedEventHandler(DoSomething));

隧道路由事件

隧道路由事件的工作方式和冒泡路由事件相同,但方向相反。
隧道路由事件以单词 Preview 开头。
WPF 通常成对地定义冒泡路由事件和隧道路由事件。
💡 隧道路由事件总在冒泡路由事件之前被触发。
💡 如果将隧道路由事件标记为已处理过,那就不会发生冒泡路由事件。这是因为两个事件共享 RoutedEventArges 类的同一个实例。
❓如果需要执行一些预处理(根据键盘上特定的键执行动作或过滤特定的鼠标动作),隧道路由事件是非常有用的。
有时也称预览事件

WPF 事件

WPF 事件的分类:

  • 生命周期事件
  • 鼠标事件
  • 键盘事件
  • 手写笔事件
  • 多点触控事件
    鼠标、键盘、手写笔及多点触控事件都是输入事件。

生命周期事件

所有元素的生命周期事件:
  • Initialized
  • Loaded
  • Unloaded

窗口初始化时会自下而上初始化每个元素,也就是说根元素总是最后才会被初始化。

Window 类的生命周期事件
  • SourceInitialized: 当取得窗口句柄前发生;
  • ContentRendered: 在窗口首次呈现后立即发生;
  • Activated: 当用户切换到窗口时发生,相当于控件的 GotFocus 事件;
  • Deactivated: 当用户从该窗口切换到其他窗口时发生,相当于控件的 LostFocus 事件;
  • Closing: 当关闭窗口时发生;
  • Closed: 当窗口已经关闭后发生,此时仍可访问元素对象。

输入事件

输入事件是当用户使用某些各类的外设硬件进行交互时发生的事件,例如鼠标、键盘、手写笔或多点触控屏。输入事件可通过继承自 InputEventArgs 的自定义事件参数类传递额外的信息。

InputEventArgs 增加了两个属性:

  • Timestamp: 指示事件是何时发生的;
  • Device: 指示事件发生的相关设备对象。

键盘输入

所有元素的键盘事件(按顺序)
  • PreviewKeyDown
  • KeyDown
  • PreviewTextInput
  • TextInput
  • PreviewKeyUp
  • KeyUp
private void KeyEvent(object sender, KeyEventArgs e)
{string message = "Event: " + e.RoutedEvent + " " +" Key: " + e.Key;
}

KeyEventArgs 枚举可获得按钮属性。

当连续按住一个键时会引发连接的事件,可以通过判断是否是连续按下避免重复触发事件:

if ((bool) chkIgnoreRepeat.IsChecked && e.IsRepeat) return;
private void TextInput(object sender, TextCompositionEventArgs e)
{string message = "Event: " + e.RoutedEvent + " " +" Text: " + e.Text;
}

🔥 ❓
TextInput 事件中,可通过 TextCompositionEventArgs 获取输入文本值。

关于键盘数字:
在主键区的数字和数字区的数字,返回的Key值是不一样的,分别是:Key.D9 和 Key.NumPad9
可以通过转换将得到相同的数字:(但这个仅限于数字才能转换成一样的)

KeyConverter converter = new KeyConverter();
string ke = converter.ConvedrToString(e.Key);

💡 如果在文本框中按下了空格,将直接绕过 PreviewTextInput 事件,这意味着还处理 PreviewKeyDown 事件。

💡 只有当字符可以“输入”到元素中 时,都会触发 TextInput 事件。比如空格键等。

💡 当发生 PreviewTextInput 事件时,一般会挂起 TextInput 事件。

焦点

WPF 中用户一次只能使用一个控件。即具有焦点的控件。
Focussable="true" 属性可设置控件是否具有焦点。
Tab 键可以切换控件焦点。
TabIndex 可以设置控件的焦点顺序。0是第一个。
IsTabStop = “fasle” 属性将阻止控件被包含进 Tab 焦点顺序。
Visibility 设置是否显示。
IsEnabled 设置是否可用。

获取键盘状态

使用 KeyboardDevice 属性获取键盘状态:(速度慢,有时候状态跟不上输入速度)

if((e.KeyboardDevice.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
{lblInfo.Text = "You held the Control key.";
}

KeyboardDevice 方法:

  • IskeyDown()
  • IskeyUp()
  • IsKeyToggled()
  • GetKeyState()

使用 KeyBoard 类

if(Keyboard.IsKeyDown(Key.LeftShift))
{lblInfo.Text = "The left Shift is held down.";
}

鼠标输入

MouseEnter: 当指针移动到元素上时发生事件
MouseLeave: 当指针离开元素时发生事件
💡 这两个事件是直接事件

PreviewMouseMove 和 MouseMove 是路由事件

MouseEventArgs 包含鼠标状态,其中该属性的 GetPosition() 方法可获得鼠标相对于元素的位置

Point pt = e.GetPosition(this);

IsMouseOver 或 IsMouseDirectlyOver 可确定当前鼠标是否位于某元素上

鼠标单击事件

PreviewMoseWheel 和 MoseWheel 鼠标滚动事件

所有元素的鼠标单击事件:

  • PreviewMoseLeftButtonDown/PreviewMoseRightButtonDown
  • MouseLeftButtonDown/MouseRightButtonDown
  • PreviewMouseLeftButtonUp/PreviewMoseRightButtonUp
  • MouseLeftButtonUp/MouseLeftButtonUp

MouseButtonEventArgs 继承自 MouseEventArgs 对象

  • MouseButton: 指示哪个鼠标按键
  • ButtonState: 指示鼠标按钮状态
  • ClickCount: 指示是单击还是双击事件
捕获鼠标

💡 如果单击一个元素,保持按下鼠标键,然后移动鼠标指针离开该元素,这里该元素就不会接收到鼠标键释放事件。

Mouse.Capture() 方法可捕获鼠标事件
LostMouseCapture 事件可响应鼠标丢失问题

鼠标拖放

💡 需要指定拖放的源和目标,拖放操作可交换任意类型的对象

👉 拖动

private void lblSource_MouseDown(object sender, MouseButtonEventArgs e)
{Label lbl = (Label)sender;DragDrop.DoDragDrop(lbl, lbl.Content, DragDropEffects.Copy);// DragDrop.DoDragDrop 调用此方法进行拖动// 参数:源,被移动的内容,移到方式上复制
}

👉 接收

<Label AllowDrop="True">To Here</Label>
<--!AllowDrop="True" 允许接收被拖动的内容-->

👉 如果需要对接收的内容进行处理: DragEnter 事件

private void lblTarget_DragEnter(object sender, DragEventArgs e)
{if(e.Data.GetDataPresent(DataFormats.Text))e.Effects = DragDropEffects.Copy;  // 将鼠标变成复制外形elsee.Effects = DragDropEffects.None; // 将鼠标变成一条线
}

👉 接收拖放数据事件

private void lblTarget_Drop(object sender, DrogEventArgs e)
{((Lable)sender).Content = e.Data.GetData(DataFormats.Text);
}

也可以实现两个应用程序间的数据拖放操作。需要使用 System.Windows.Clipboard类。👀 我的理解:就是实现复制、粘贴操作。

多点触控输入

💡 单击和文本输入属于高层次输入;鼠标事件及按键事件属于低层次输入。

触控输入的三个层次:

  • 原始触控:例如绘图操作。
  • 操作:移动、绽放、旋转以及轻按。
  • 内置的元素支持:有些元素已经对多点触控事件提供了内置支持,如可滚动的支持触控移动:ListBox、ListView、DataGrid、TextBox以及ScrollViewer。
原始触控事件
  • PreviewTouchDown
  • TouchDown
  • PreviewTouchMove
  • TouchMove
  • PreviewTouchUp
  • TouchUp
  • TouchEnter
  • TouchLeave

TouchEventArgs 对象提供的两个重要成员:

  • GetTouchPoint()
  • TouchDevice: 指的是每个触点,并通过 TouchDeviceId 进行区分

可在按下事件、移动事件、抬起事件中,通过获取当前触控点,画一个圆点来显示用户的手指位置,也可以移动这些圆点,并且在抬起后将圆点元素删除掉。
同样支持类似于鼠标的事件丢失处理。略。

操作(移动、转运、缩小或放大)

💡 将元素的 IsManipulationEnabled 属性设置为 True,将元素配置为接受触控操作。

💡 这样,元素将响应4个事件:(它们是冒泡事件)

  • ManipulationStarting:当触摸对象时触发此事件
  • ManipulationStarted
  • ManipulationDelta:在操作过程中持续触发此事件
  • ManipulationCompleted
惯性

当用户结束手势并抬起手指释放元素时,会触发 ManipulationInertiaStarting 事件(冒泡事件),可使用 ManipulationInertiaStartingEventsArgs 对象确定当前速度,然后通过代码设置希望的减速度。


路由传播机制参考文章:
走进WPF之路由事件 - 老码识途呀 - 博客园

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/890231.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql 数据目录

前言 简单介绍一下mysql的数据目录。 正文 对于mysql,我们关心的一般是数据部分,那么关心的就是数据目录。 我们查看一下数据目录的位置。 SHOW VARIABLES LIKE datadir看一下数据目录的位置:再对比一下我们数据库的信息:好像除了这个information_schema 好像都有一个目录。 …

三句话生成 P5.js 粒子特效代码,人人都可以做交互式数字艺术

前几天在上海西岸艺术中心的全球开发者先锋大会GDC2025,通义灵码的老朋友-@同济子豪兄受邀参会,并带领大家观看了本届大会两大重磅看点:具身智能人形机器人和 AI 程序员编程写代码。前几天在上海西岸艺术中心的全球开发者先锋大会GDC2025,通义灵码的老朋友-@同济子豪兄受邀…

使用JAVA调用asmx服务,“http://tempuri.org/”有什么作用?

原文链接:https://bbs.csdn.net/topics/392507481 这个是域名 http://tempuri.org/ 是默认的命名空间.如果是web直接引用不需要管,但有时候别人发布改了这个命名,你动态引用就需要改成跟他一样的了.一般情况下有些人发布没改这个,有些注重网站安全的就修改了这个,给你个照片看…

为什么去IOE化的背景下,还有必要学Oracle

很多人都知道有“去IOE化”这个口号,但事实上,有多少人知道是哪一年提出的这个口号吗?有多少人知道去的哪个IOE吗?现在越来越多的国产数据库出现,还有必要学Oracle、考OCP认证吗? 去IOE化 “去IOE化”这个口号早在2008、2009的时候就提出来了,原因是互联网发展需要、成本…

08 梯度消失与梯度爆炸问题

由反向传播原理可知,梯度的计算遵循链式法则。由于网络层数不断加深,梯度的连乘效应可能会导致梯度呈指数形式衰减,又或以指数形式增加。 前者叫做梯度消失,梯度消失导致网络中的早期层几乎不更新,使得网络难以学习到输入数据的有效特征。可能导致网络权重更新非常缓慢,使…

GAMES101 作业三

重要知识点一 布林冯反射模型 漫反射+高光+环境光重要知识点二 通过作业也对空间中的坐标变换认识更清晰了一点,在摄像空间中进行变换是不对的,需要从原来的三维空间进行变换才对,所以会有一个矫正系数 重要知识点三 在计算光线时,要注意计算向量和单位化 不了解的 对于后两…

写一个简单的hexo-tag-plugin:quote

前置教程 [Akilarの糖果屋 - Akilar.top](https://akilar.top/posts/e2bf861f/) 为啥想写一个quote的标签外挂 我最近在写博客的时候,发现好多时候原生的Hexo标签不是很好用,效果如下。 {% tabs Hexo Block Quote, -1 %}没有提供参数,则只输出普通的 blockquote{% blockquot…

搭建DeepSeek-R1平台

前言 大家用到 DeepSeek-R1 时应该会经常出现下面的情况。但凡多问两个问题,不但缓慢,而且容易出现服务器繁忙的问题:今天教大家一种通过API部署的方式,可以体验满血版的DeepSeek-R1,不仅回答快速,而且不会出现服务器繁忙的情况。 注册账号 首先大家要通过下面的方式,先…

No.16 CSS--背景属性

一、CSS常见的背景属性 background-color: aqua; 设置背景颜色background-image: none; 设置背景图片background-position: 0%; 设置背景图片位置background-repeat: no-repeat; 设置背景图片如何重复填充background-size: 0%; …

Mybatis基础06

动态SQL 介绍 什么是动态SQL:动态SQL指的是根据不同的查询条件 , 生成不同的Sql语句. 官网描述: MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,…

Spring5基础01

Spring概述简介Spring : 春天 --->给软件行业带来了春天 2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。 2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版。 很难想象Rod Johnson的学历 , 他是悉尼大学的博士,然而他的专…

P2375 [NOI2014] 动物园

P2375 [NOI2014] 动物园 题目描述 近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的,园长决定开设算法班,让动物们学习算法。 某天,园长给动物们讲解 KMP 算法。 园长:“…