博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
路由事件
阅读量:5888 次
发布时间:2019-06-19

本文共 13066 字,大约阅读时间需要 43 分钟。

原文:

简介:每个.NET开发人员都熟悉"事件"的思想,当有意义的事情发生时,由对象(WPF元素)发送的用于通知代码的消息。WPF通过路由(event routing)的概念增强了.Net事件模型,事件路由允许源自某个元素的事件由另一个元素引发。

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

步骤:

1.1)、由只读的、静态的方式声明。

1.2)、在静态的构造函数中通过EventManager.RegisterRoutedEvent()方法注册。

1.3)、通过.Net事件定义进行封装。

例子:

public class ButtonBase:ContentControl{    //声明只读的、静态的、RoutedEvent类型的字段。    public static readonly RoutedEvent ClickEvent;    ///     /// 在静态的构造函数中注册路由事件。    ///     static ButtonBase()    {        //路由事件是使用EventManager.RegisterRoutedEvent()方法注册的。        //参数:Click:事件的名称。        //参数:RoutingStrategy.Bubble:路由的类型(冒泡路由、隧道路由)。        //参数:typeof(RoutedEventHandler):定义事件处理程序语法的委托。        //参数:typeof(ButtonBase):拥有事件的类。        ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ButtonBase));    }    //通过普通的.Net事件进行封装,从而使所有的.Net语言都能访问他,事件封装器可使用AddHandle()和RemoveHandle()方法添加删除已注册的程序。    public event RoutedEventHandler Click    {        add        {            //为指定的路由事件添加路由事件处理程序,并将该处理程序添加到当前元素的处理程序集合中。            base.AddHandler(ClickEvent, value);        }        remove        {            //从此元素移除指定的路由事件处理程序。            base.RemoveHandler(ClickEvent, value);        }    }}

 2、共享路由事件。

与依赖项属性一样,可在类之间共享路由事件的定义,例如,UIElement(该类是所有普通WPF元素的起点),MouseUp事件是由System.WIndows.Input.Mouse类定义的,UIElement类和ContentElement类只通过Routed-Event.AddOwner()方法重用MouseUp事件。

UIElemtn.MouseUpEvent = Mouse.MouseUpEvent.AddOwner(typeof(UIElement));

3、处理路由事件。

 3.1)、Xaml的方式添加处理程序。

Xaml代码: 

Image_MouseEnter处理函数: 

private void Image_MouseEnter(object sender, MouseEventArgs e){    MessageBox.Show("MouseEnter事件");}

3.2)、通过+=使用代码链接。

创建了一个针对该事件具有正确签名的委托对象(该类中是MouseEventHandler委托的实例),并将该委托指向Img_MouseEnter()方法。然后将该委托添加到img1.MouseEnter事件的已注册的事件的处理程序列表中 

private void MainWindow_Loaded(object sender, RoutedEventArgs e){    //第一种写法。    img1.MouseEnter += new MouseEventHandler(Img_MouseEnter);    //第二种写法。    img1.MouseEnter += Img_MouseEnter;}private void Img_MouseEnter(object sender, MouseEventArgs e){    MessageBox.Show("进入了MouseEnter事件");}

 3.3)、通过AddHandler()方法添加事件处理程序。

private void MainWindow_Loaded(object sender, RoutedEventArgs e){       //Image.MouseEnterEvent:处理路由事件的标志符。    //new MouseEventHandler(Img_MouseEnter):处理程序实现的引用。    img1.AddHandler(Image.MouseEnterEvent, new MouseEventHandler(Img_MouseEnter));}private void Img_MouseEnter(object sender, MouseEventArgs e){    MessageBox.Show("进入了MouseEnter事件");}

4、断开事件处理程序。

4.1)、通过-=断开事件处理程序。 

//第一种写法。img1.MouseEnter -= new MouseEventHandler(Img_MouseEnter);//第二种写法。img1.MouseEnter -= Img_MouseEnter;

4.2)、通过RemoveHandler()方法断开事件处理程序。

img1.RemoveHandler(Image.MouseEnterEvent, new MouseEventHandler(Img_MouseEnter));

5、事件路由。

WPF中,事件路由以3种方式出现:直接路由事件(Direct event)、冒泡路由事件(bubbing event)、隧道路由事件(tunneling)。

在WPF中,如果事件不需要传递任何额外细节,可使用RoutedEventArgs类,该类包含了一些有关如何传递事件的一些细节,如果事件确实需要传递一些额外的信息,那么需要使用更特殊的继承自RoutedEventArgs的对象,其中每个WPF事件参数类都继承自RoutedEventArgs类。

RoutedEventArgs类常用属性

Source

指示引发事件的对象。

OriginalSource

指示最初是什么对象引发了事件。

RoutedEvent

通过事件处理程序为触发的事件提供RoutedEvent对象,如果同一个事件处理程序处理不同的事件,这一信息是非常有用的。

Handled

该属性允许终止事件的冒泡或隧道过程,如果将该属性设置为true,就终止了传递。

 

 

 

 

 

 

 

 

5.1)、直接路由。

源于同一个元素,不传递给其他。如元素的MouseEnter事件。  

5.2)、冒泡路由。

从下到上传递。一般情况下,冒泡路由以Mouse开头,如MouseDown、MouseUp。

Xaml代码:

  
  
  
  
  
  
  
  

 后台代码: 

protected int eventCount = 0;private void SomethingClick(object sender, MouseButtonEventArgs e){    eventCount++;    string message = "#" + eventCount.ToString() + ":\r\n" +    " Sender: " + sender.ToString() + "\r\n" +    " Source: " + e.Source + "\r\n" +    " Original Source" + e.OriginalSource;    //将内容加入到listBox中。    listBox.Items.Add(message);    //如果将Handled属性设置为true,则终止传递。    //e.Handled = true;}

效果:

 

当点击了图片,会从下到上地发生冒泡路由,直到冒泡到最外层的父级元素。 

5.3)、隧道路由。

从上到下传递。 隧道路由比较容易识别,冒泡以Preview开头,如PreviewMouseDown、PreviewMouseUp。WPF通常成对地定义冒泡路由事件和隧道路由事件,这意味着如果发现冒泡的MouseUp事件,还可以找到PreviewUp隧道事件,隧道路由事件总是在冒泡路由事件之前被触发。如果隧道路由事件标记为已处理过,就不会发生冒泡路由事件,这是因为两个事件共享RoutedEventArgs类。

Xaml代码:

后台代码:

public partial class MainWindow : Window{    public MainWindow()    {        InitializeComponent();    }    int count = 0;    private void SuiDaoThings(object sender, MouseButtonEventArgs e)    {        count++;        string message = "#" + count + ":\r\n" +        " Sender: " + sender + "\r\n" +        " Source " + e.Source + "\r\n" +        " Original" + e.OriginalSource;        listBox1.Items.Add(message);    }}

效果:

5.4)、处理挂起的事件。

有一种方法可接收被标记为处理过的事件,不过不是直接通过XAML关联事件处理程序,而是必须使用AddHandle()方法,AddHandle()方法提供了一个重载版本,该版本可以接收一个Boolean值作为他的第三个参数,如果将该参数设置了True,即使设置了Handle标志,也将接收事件。

Xaml代码,不直接指定事件处理函数:

 后台代码:

public partial class MainWindow : Window{    public MainWindow()    {        InitializeComponent();        //通过AddHandle()方法添加事件处理程序。        img1.AddHandler(UIElement.MouseDownEvent,new  MouseButtonEventHandler(SomethingClick),true);        stackPanel1.AddHandler(UIElement.MouseDownEvent, new MouseButtonEventHandler(SomethingClick), true);        label1.AddHandler(UIElement.MouseDownEvent, new MouseButtonEventHandler(SomethingClick), true);        grid1.AddHandler(UIElement.MouseDownEvent, new MouseButtonEventHandler(SomethingClick), true);        window1.AddHandler(UIElement.MouseDownEvent, new MouseButtonEventHandler(SomethingClick), true);    }    protected int eventCount = 0;    private void SomethingClick(object sender, MouseButtonEventArgs e)    {        eventCount++;        string message = "#" + eventCount.ToString() + ":\r\n" +        " Sender: " + sender.ToString() + "\r\n" +        " Source: " + e.Source + "\r\n" +        " Original Source" + e.OriginalSource;        listBox.Items.Add(message);        //如果使用AddHandle()方法添加事件处理程序,那么设置Handled属性不会阻止路由,仍会继续向上冒泡路由传递。        e.Handled = true;    }}

 效果图,如果使用AddHandle()方法的重载设置为true的那个,设置e.Handled属性为True,会继续传递。

5.5)、附加事件。

Button元素有Click事件,而StackPanel元素则没有Click事件,如果要为StackPanel元素添加Click事件,则报错。这个时候附加事件的优势就体现出来了。

在Xmal中添加附加事件: 

 通过AddHandle()方法添加附加事件:

Xaml代码: 

 后台代码:

public partial class MainWindow : Window{    public MainWindow()    {        InitializeComponent();        //通过AddHandler方法添加附加事件。        stackPanel1.AddHandler(Button.ClickEvent, new RoutedEventHandler(DoSomeThing));    }    private void DoSomeThing(object sender, RoutedEventArgs e)    {                }}

 6、WPF事件。

一般情况下,WPF最重要的事件包括这5类,分别是:生命周期事件、鼠标事件、键盘事件、手写笔事件和多点触控事件。

6.1)、生命周期事件。

生命周期事件是在元素被初始化,加载或卸载时发生这些事情。都是在FrameworkElement类中定义的。 

所有元素的生命周期

Initialized

当元素被初始化,并已根据Xaml标记设置了元素的属性之后发生,这时元素已经初始化,但窗口的其他部分可能尚未初始化,但是,此时还没应用样式和数据绑定,这时,IsInitialized属性为true

Loaded

当整个窗口已经初始化并应用了样式和数据绑定时,该事件发生,就是在元素呈现之前的最后一站,这时IsLoaded属性为true

UnLoaded

当元素被释放时,该事件发生。原因是包含元素的窗口被关闭或者特定的元素被从窗口中删除。

 

Window类的常用生命周期事件

名称

说明

ContentRendered

在窗口首次呈现后立即发生,ContentRendered事件表明窗口已经完全可见,并且已经准备好接收输入

Activated

当用户切换到该窗口时发生(从其他窗口切换到当前窗口),当窗口第一次加载也会引发该事件

Deactivated

当用户从该窗口切换到其他窗口时发生。

Closing

当关闭窗口时发生,不管是用户关闭窗口,还是通过代码WIndow.Close()

Closed

当窗口已经关闭后发生,但是让然可以访问元素对象,当然是在UnLoaded事件尚未发生之前

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

如果只对执行控件的第一次初始化感兴趣,完成这项任务的最好时机是触发Loaded事件。

6.2)、输入事件。

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

6.2.1)、键盘输入。

当用户按下键盘上的一个键时,就会发生一系列事件。键盘处理永远不是那么的简单,一些控件可能会挂起这些事件中的某些事件,最明显的是TextBox控件,他挂起了TextInut事件,TextBox控键还挂起了KeyDown事件,通常可以使用隧道路由事件。

所有元素的按下键盘的顺序

名称

路由类型

说明

PreviewKeyDown

隧道

当按下一个键时发生

KeyDown

路由

当按下一个键时发生

PreviewTextInput

隧道

当输入完成且元素正在接收文本输入时发生,对不会产生文本输入的按键(Ctrl键、shift键、方向键等),不会引发该事件

TextInput

路由

当输入完成且元素正在接收文本输入时发生,对不会产生文本输入的按键,不会引发该事件

PreviewKeyUp

隧道

当释放一个键时发生

KeyUp

路由

 

当释放一个键时发生

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Xaml中定义:

 后台代码:

//PreviewKeDown事件处理程序。private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e){    string message = string.Format("Event--->{0},Key--->{1}", e.RoutedEvent, e.Key);    listBox1.Items.Add(message);}//KeyDown事件处理程序。private void TextBox_KeyDown(object sender, KeyEventArgs e){    string message = string.Format("Event--->{0},Key--->{1}", e.RoutedEvent, e.Key);    listBox1.Items.Add(message);}//PreviewTextInput事件处理程序。private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e){    string message = string.Format("Event--->{0},Key--->{1}", e.RoutedEvent, e.Text);    listBox1.Items.Add(message);}//TextInput事件处理程序,不会执行此事件处理程序,因为TextBox元素挂起了TextInput事件。private void TextBox_TextInput(object sender, TextCompositionEventArgs e){    string message = string.Format("Event--->{0},Key--->{1}", e.RoutedEvent, e.Text);    listBox1.Items.Add(message);}//PreviewKeyUp事件处理程序。private void TextBox_PreviewKeyUp(object sender, KeyEventArgs e){    string message = string.Format("Event--->{0},Key--->{1}", e.RoutedEvent, e.Key);    listBox1.Items.Add(message);}//KeyUp事件处理程序。private void TextBox_KeyUp(object sender, KeyEventArgs e){    string message = string.Format("Event--->{0},Key--->{1}", e.RoutedEvent, e.Key);    listBox1.Items.Add(message);}//TextChanged事件处理程序。private void TextBox_TextChanged(object sender, TextChangedEventArgs e){    string message = string.Format("Event--->{0}", e.RoutedEvent);    listBox1.Items.Add(message);}

 效果图,在文本框中输入了一个K键:

 

6.2.2)、 焦点。

在Windows的世界中,用户每次只能使用一个控件,当前接收用户按键的控件是具有焦点的控件。为了让控件能接受焦点,必须将Focusable设置为true,这是所有控件的默认值,如果为TextBox元素的Focusable元素设置为false,就不会获取焦点了,可以通过TabIndex属性设置按下tab键后的顺序。

6.2.3)、获取键盘状态。

当发生按键事件时,经常需要知道更多的信息,不仅需要知道按下的是哪个键,其他键是否按下了也同样重要,特别是Shift键、Ctrl键、Alt键。

KeyBoardDevice属性提供了KeyBoardDevice类的一个实例,它的属性包含了当前是哪个元素具有焦点以及按下了哪些修饰键,可用代码显示。

Xmal代码: 

后台代码:

private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e){    if ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)    {        MessageBox.Show("你点击了Control键");    }    //还可以使用KeyBoard类,该类和KeyboardDevice类非常类似,只是Keyboard类由静态成员构成。    if (Keyboard.IsKeyDown(Key.LeftCtrl))    {        MessageBox.Show("按下了Ctrl键盘");    }}

 6.2.4)、鼠标输入。

 鼠标事件执行几个关联的任务,当鼠标移到某个元素上时,可通过最基本的鼠标事件进行响应,这些都是MouseEnter和MouseLeave(直接事件)。

所有元素的鼠标单击事件(按顺序排序)

名称

路由类型

说明

PreviewMouseLeftButtonDown

PreviewMouseRightButtonDown

隧道

按下鼠标左键时发生

MouseLeftButtonDown

MouseRightButtonDown

隧道

按下鼠标左键时发生

PreviewMouseLeftButtonUp

PreviewMouseRightButtonUp

隧道

按下鼠标右键时发生

MouseLeftButtonUp

MouseRightButtonUp

隧道

按下鼠标右键时发生

PreviewMouseWheel

隧道

鼠标滚轮动作

MouseWheel

冒泡

鼠标滚轮动作

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

6.2.5)、捕获鼠标。

当鼠标被一个元素捕获时,就不能与其他元素进行交互了(不能单击窗口中的其他元素),鼠标捕获通常用于短时间的操作。

Xaml代码: 

后台代码:

private void Button_Click(object sender, RoutedEventArgs e){    Mouse.Capture(btn1);}

当点击button按钮后,整个窗口的其他元素和按钮就不能点击了,只有把光标移开该窗口,然后点击一下,这个窗口的元素才可以点击。

6.2.6)、鼠标拖放。

拖放操作的方法和事件都集中在System.Windows.DragDrop类中。

拖放操作有以下3个步骤:

1)、用户单击元素,并保持鼠标按键为按下状态,这时,某些信息被搁置起来,并且拖放操作开始。

2)、用户将鼠标移动到其他元素上,如果该元素可接受正在拖动的内容的类型,鼠标指针会变成拖放图标,否则鼠标指针会变成内部有一条线的圆形。

3)、当用户释放鼠标键时,元素接收信息并决定如何处理接收到的信息,在没有释放鼠标键时,可按下Esc键取消该操作。

Xaml代码: 

后台代码:

/// /// 源元素的MouseDown事件。/// /// /// private void lbl1_MouseDown(object sender, MouseButtonEventArgs e){    //转换成Label对象。    Label lbl = (Label)sender;    //DragDrop类用于拖动操作。    //DoDragDrop()方法用于启动拖放操作。 源对象,源对象的属性,拖放的效果(复制,移动...)    //DoDragDrop()方法重载参数:源对象,源对象的属性,拖放的效果(复制,移动...)    DragDrop.DoDragDrop(lbl, lbl.Content, DragDropEffects.Scroll);}/// /// 目标元素的Drop事件。/// /// /// private void lbl2_Drop(object sender, DragEventArgs e){    //获取指定数据对象,格式由字符串指定。    ((Label)sender).Content = e.Data.GetData(DataFormats.Text);}

6.2.7)、多点触控输入(适用于带触摸屏的电脑)。

所有元素的原始触控事件

名称

路由类型

说明

PreviewTouchDown

隧道

用户触摸元素时发生

TouchDown

冒泡

用户触摸元素时发生

PreviewTouchMove

隧道

用户移动到触摸屏上的手指时发生

TouchMove

冒泡

用户移动到触摸屏上的手指时发生

PreviewTouchUp

隧道

用户移开手指,结束触摸时发生

TouchUp

冒泡

用户移开手指,结束触摸时发生

TouchEnter

触点从元素外进入元素内时发生

TouchLeave

触点离开元素时发生

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

End。

转载地址:http://kugix.baihongyu.com/

你可能感兴趣的文章
单元测试之Stub和Mock
查看>>
solr
查看>>
IOS7 viewDidLoad中调用 pushViewController 的问题
查看>>
oracle merge into 用法详解
查看>>
tf.concat&tf.gather&tf.gather_nd&tf.greater&tf.cast&tf.expand_dims&tf.squeeze
查看>>
VBA基础之Excel 工作表(Sheet)的操作(二)
查看>>
js 日期转换 strToDate
查看>>
空间索引格网大小无效
查看>>
C_数据结构_数组的修改和删除
查看>>
软件测试5gkd
查看>>
伪类与伪元素
查看>>
11.static关键字
查看>>
iOS @try
查看>>
数据结构之栈——二进制转十进制
查看>>
关于Objective-C和C++中的继承及其区别
查看>>
$().bind()的返回值
查看>>
16 个常用的yum 命令
查看>>
JDBC基础语句使用
查看>>
Mycat
查看>>
XML-->DTD&Schema Notes
查看>>