专业做写生的网站,青岛网站建设和优化,做办公室的网站,微信营销文章目录 简介#xff1a;一、事件最基本的用法二、理解路由事件 简介#xff1a;
WPF中使用路由事件升级了传统应用开发中的事件#xff0c;在WPF中使用路由事件能更好的处理事件相关的逻辑#xff0c;我们从这篇开始整理事件的用法和什么是直接路由#xff0c;什么是冒… 文章目录 简介一、事件最基本的用法二、理解路由事件 简介
WPF中使用路由事件升级了传统应用开发中的事件在WPF中使用路由事件能更好的处理事件相关的逻辑我们从这篇开始整理事件的用法和什么是直接路由什么是冒泡路由以及什么是隧道路由。
一、事件最基本的用法
在基于事件驱动的开发中把代码放在响应注册的事件的处理函数内比如Click事件、MouseDown事件、MouseUp事件等等。每个控件响应自己的注册事件有很多如果在事件上有相互关联和影响的事件就要在一个业务逻辑里写比较多的代码。而路由事件主要的优势就是路由事件可以在元素树上进行传递并且沿着元素树的传播途径被事件处理程序处理。这样我们写代码的过程中时就可以更好的组织代码到合适的位置。
WPF事件模型和WPF属性模型非常类似与依赖项属性一样路由事件由只读的静态字段表示在静态构造函数中注册并通过标准的.NET事件定义进行封装。这里我们只讲如何更好的使用。原理部分请看源码。比如ButtonBase提供的Click事件。
Button Content事件处理程序 ClickButton_Click/
private void Button_Click(object sender, RoutedEventArgs e)
{//这是Click事件处理程序代码部分。
}在注册事件后在事件处理程序中第一个参数 sender提供引发该事件的对象第二个参数是EventArgs对象。在WPF中如果事件不需要传递额外的信息可以使用RoutedEventArgs类如果需要传递额外的信息就要是有继承自RoutedEventArgs的对象。比如处理inkcanvas墨迹绘制的。比如处理多点触控的。这些都是变相继承RoutedEventArgs类。里面会包含在这种场景下更加多的信息。
注册事件的几种写法
1在XAML代码中Button x:NameEventMessageButton Content事件处理程序 MouseUpEventMessageButton_MouseUp/
2在cs代码中 EventMessageButton.MouseUp EventMessageButton_MouseUp;
3在cs代码中 EventMessageButton.MouseUp new MouseButtonEventHandler(EventMessageButton_MouseUp);private void EventMessageButton_MouseUp(object sender, MouseButtonEventArgs e)
{//我是处理程序。
}第一种写法 我们使用XAML文件中在Button元素内使用MouseUp来创建后台事件处理代码 Btn_eventMessge_MouseUp
第二种写法 我们在后台代码中使用MouseUp的方式注册。一种是New MouseButtonEventHandler传入方法名。一种是匿名的直接传入方法名这三种注册方式达成的效果是一样的。
而这三种实际上使用的是事件封装器。另一种方式是通过使用UIElement.AddHandler来直接连接事件。这里看个人习惯把。但是各种写法主要解决的问题还是解耦因为这些会关联到后面的命令动画。模板。触发器。MVVM下的使用等等。这是个比较长久的问题。所以在这里能够使用看得明白目前这个阶段就可以了。
我们继续往下。解除关联
在注册事件的时候最好先使用-来解除关联避免多次触发不合符预期的监听事件。断开使用-或者使用UIElement.RemoveHandler来解除关联。 因为事件在多次注册事件处理程序是可行的。而事件的多词解除关系不会引发任何问题因此不要担心和-不匹配的问题。
public MainWindow(){InitializeComponent();EventMessageButton.MouseUp - EventMessageButton_MouseUp; EventMessageButton.MouseUp EventMessageButton_MouseUp;}二、理解路由事件
我们知道了事件可以在元素上注册事件处理程序那么我们知道内容控件是可以相互嵌套各种奇奇怪怪的组合以达到自己想要的效果在这种情况下我们假设一个比较常见的场景。我们有一个标签标签中包含一个StackPanel面板面板中包含一幅图片和2个文本。
Label BorderBrushBlack BorderThickness1StackPanelTextBlock Margin3我是图片标题/TextBlockImage Source1.png StretchNone/TextBlock Margin3我是图片正文/TextBlock/StackPanel
/Label我们的控件来回嵌套内容结构很复杂了。但是我们想在用户点击时只在一个地方响应我们的代码。如果为每个元素都关联同一个事件处理程序代码会很乱。而且难以维护。而路由事件就是为了解决这个问题的。路由事件分为三种
1和普通的.NET事件类似直接路由事件direct event) 他们源于一个元素不传递给其他元素比如MouseEnter事件是直接路由事件。
2向上传递的冒泡路由事件bubbling event)比如MouseDown事件是冒泡路由事件该事件先由被单击的元素引发接下来被该元素的父元素引发然后被父元素的父元素引发以此类推。直到元素树的顶部。
3向下传递的隧道路由事件tunneling event) 比如PreviewKeyDown事件隧道路由事件在事件到达恰当的空间之前为预览事件和终止事件提供了机会比如PreviewKeyDown事件可以截获是否按下了某个键首先在窗口级别上然后是更具体的容器直到当按下时具有焦点的元素。
当使用EventManager.RegisterEvent()发给发注册路由事件时需要传递一个RoutingStrategy枚举指示希望用于事件的事件行为。
MouseUP和MouseDown事件都是冒泡路由事件因此当在上面的图片中按下鼠标左键后顺序触发MouseDown事件的顺序是冒泡的。我们使用Snoop软件抓取一下过程
从图中我们看到首先触发的是PreviewMouseDown的隧道路由。他可以让我们有机会预览事件或终止事件。我们看到了从MainWindow开始到最终的Image结束。我们没有终止路由。所以进行了下一轮的冒泡路由。从Image开始到MainWindow。
整个流程就结束了。我们看到路由事件提供了对事件处理非常丰富的功能。具体的隧道或冒泡行为可以参考RoutedEventArgs中的内容。
Source 属性是引发事件的的对象。 OriginalSource是最初是什么对象引发了事件。RoutedEvent为触发的事件提供的RoutedEvent对象。里面是需要用到的当前的参数比如鼠标坐标touch等等。Handled属性的作用是终止事件是否继续传递。
我们现在开始在这个例子上添加代码。用于演示我们怎么处理冒泡路由。
Window x:ClassWPFEvent.MainWindowxmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:xhttp://schemas.microsoft.com/winfx/2006/xamlxmlns:dhttp://schemas.microsoft.com/expression/blend/2008xmlns:mchttp://schemas.openxmlformats.org/markup-compatibility/2006xmlns:localclr-namespace:WPFEventmc:Ignorabled MouseUpEventResponseProcess_MouseUpTitleMainWindow Height450 Width800Grid Margin3 MouseUpEventResponseProcess_MouseUpGrid.RowDefinitionsRowDefinition HeightAuto/RowDefinitionRowDefinition Height*/RowDefinitionRowDefinition HeightAuto/RowDefinitionRowDefinition HeightAuto/RowDefinition/Grid.RowDefinitionsLabel Margin5 Grid.Row0 HorizontalAlignmentLeft BackgroundAliceBlue BorderBrushBlack BorderThickness1 MouseUpEventResponseProcess_MouseUpStackPanel MouseUpEventResponseProcess_MouseUpTextBlock Margin3 MouseUpSomethingClicked我是图片标题/TextBlockImage Source1.png StretchNone MouseUpEventResponseProcess_MouseUp/TextBlock Margin3我是图片正文/TextBlock/StackPanel/LabelListBox Grid.Row1 Margin5 NameMessageListBox/ListBoxCheckBox Grid.Row2 Margin5 NameHandlerCheckBoxHandle first event/CheckBoxButton Grid.Row3 Margin5 Padding3 HorizontalAlignmentRight NameClearButton ClickClearButton_ClickClear List/Button/Grid
/Windowusing 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 WPFEvent
{/// summary/// MainWindow.xaml 的交互逻辑/// /summarypublic partial class MainWindow : Window{public MainWindow(){InitializeComponent();}protected int eventCounter 0;private void EventResponseProcess_MouseUp(object sender, MouseButtonEventArgs e){eventCounter;string message $#{ eventCounter}:\r\n Sender: {sender} \r\n Source: {e.Source} \r\n Original Source: {e.OriginalSource}; MessageListBox.Items.Add(message);e.Handled (bool)HandleCheckBox.IsChecked;}private void ClearButton_Click(object sender, RoutedEventArgs e) { }}
}和上图一样我们尝试观察执行过程。看下触发过程,我们就能了解这个冒泡路由的工作过程了。上面写的这个例子。主要是让我们熟悉对于事件传入参数OriginalSource的使用。勾选界面上的HandleCheckBox复选框可以终止冒泡事件从而只触发第一个Image的事件。可以自己写代码尝试一下整个过程。
有个方法可以接收终止的事件消息使用AddHandler()重载的方法。
这里还有一个常用的技巧事件的附加。
如下面的代码在StackPanel中不存在Button的Click事件但是可以通过ButtonBase.Click获取按钮的点击事件此事件将会在StackPanel容器里面的任意按钮被点击时触发。
StackPanel ButtonBase.ClickStackPanel_Click Margin5Button Content按钮A ClickButton_Click/Button Content按钮B ClickButton_Click/Button Content按钮C ClickButton_Click/
/StackPanel隧道路由这里就不写了前面已经讲过。隧道路由命名都是Preview开头的。隧道路由全部结束了之后同级的冒泡路由才开始。隧道路由主要是做预先处理可以停止路由事件也可以在事件处理程序中写一些对应的处理代码。