微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

路由事件、附加事件

路由事件


路由事件的事件拥有者和事件响应者之间没有直接显示订阅关系,事件的拥有者只负责激发事件,事件将由谁响应它并不知道。事件的响应者则安装事件侦听器,针对某类事件进行侦听,当有此类事件传递至此事件响应者就使用此事件处理器来相应事件并决定事件是否继续传递。

WPF系统中大多数事件都是可路由事件。路由事件是沿着VisualTree传递的。


路由事件的策略(类型)

  • Bubble:冒泡事件,沿着路径由内到外传递的。
  • Tunnel:隧道事件,沿着路径由内到外传递的。
  • Direct:直接事件,不通过元素树路由,但支持其他路由的事件功能

路由事件的相关类

UIElement类的相关方法

UIElement 一些方法
名称备注权限

AddHandler

2形参,为指定的路由事件添加路由事件处理程序,并将该处理程序添加到当前元素的处理程序集合中。public

AddHandler

3形参,为指定的路由事件添加路由事件处理程序,并将该处理程序添加到当前元素的处理程序集合中。 将 handledEventsToo 指定为 true,可为已标记为由事件路由中的其他元素处理的路由事件调用所提供的处理程序。public
RemoveHandler从此元素中删除指定的路由事件处理程序。public
RaiseEvent引发特定路由事件。 在提供的 RoutedEventArgs 实例内标识将引发的 RoutedEvent(作为该事件数据的 RoutedEvent 属性)。public
  • 为指定的路由事件添加路由事件处理程序
public void AddHandler (System.Windows.RoutedEvent routedEvent, Delegate handler);

public void AddHandler (System.Windows.RoutedEvent routedEvent, Delegate handler, bool handledEventsToo);

第三参数设置了true,即使设置了Handled标志,也将接收到事件。

  • 引发事件:UIElement类的RaiseEvent方法
public void RaiseEvent (System.Windows.RoutedEventArgs e);

激发路由事件需要让事件携带消息(RoutedEventArgs)与路由事件关联,调用RaiseEvent把事件发送出去。传统直接事件的激发是通过调用CLR事件的Invoke方法实现的。

 

  • 从此元素中删除指定的路由事件处理程序。

public void RemoveHandler (System.Windows.RoutedEvent routedEvent, Delegate handler);

 

  • 将用于处理不具有事件数据的事件的方法
public delegate void EventHandler(object? sender, EventArgs e);

sender:事件引发者的对象的引用。

EventArgs:该对象与其他所有可能很重要的附加细节捆包在一起。如果不需要额外细节,使用RoutedEventArgs类即可。如果需要额外细节,使用继承自RoutedEventArgs的类(如MouseButtonEventArgs)。

 

RoutedEventArgs类

路由事件携带的事件参数必须是RoutedEventArgs类或者其派生类的实例。

不同RoutedEventArgs可以用单个使用RoutedEvent。此类负责包装RoutedEvent的事件数据,提供额外的事件状态信息,并由事件系统用于调用与该路由事件关联的处理程序。

public RoutedEventArgs (System.Windows.RoutedEvent routedEvent, object source);
RoutedEventArgs 属性
名称备注权限

Handled

获取或设置一个值,该值指示针对路由事件(在其经过路由时)的事件处理的当前状态。get; set;

OriginalSource

父类进行任何可能的 Source 调整之前,获取由纯命中测试确定的原始报告源。get;

RoutedEvent

获取或设置与此 RoutedEventArgs 实例关联的 RoutedEventget; set;
Source获取或设置对引发事件的对象的引用。get; set;
RoutedEventArgs 方法
名称备注权限

InvokeEventHandler

当在派生类中重写时,提供特定于类型的调用事件处理程序的方式,该方式与基实现相比可提高效率。protected

OnSetSource

在派生类中重写时,每当实例的 Source 属性的值发生更改,则提供一个通知回调入口点。protected

Source属性表示LogicalTree上的消息源头。OriginalSource表示VisualTree上的消息源头.

 

RoutedEvent 类

RoutedEvent 属性
名称备注权限

HandlerType

获取路由事件的处理程序类型。get;

Name

获取路由事件的标识名称get;

OwnerType

获取路由事件的已注册所有者类型。get;

RoutingStrategy

获取路由事件的路由策略。get;
RoutedEvent 方法
名称备注权限

AddOwner

将另一个所有者类型与 RoutedEvent 实例所表示的路由事件相关联,并允许事件的路由和其处理。public

ToString

返回此 RoutedEvent 的字符串表示形式。public

共享路由事件:UIElement类和ContentElement类只通过RoutedEvent.AddOwner方法重用事件。例:

public static readonly RoutedEvent EditStateChangedEvent  = MyEditContainer.EditStateChangedEvent.AddOwner(typeof(AnotherEditContainer));

EventManager 类

提供事件相关的实用工具方法,这些方法可为类所有者注册路由事件,并添加类处理程序。

EventManager 方法
名称备注权限

GetRoutedEvents

为已注册到事件系统的路由事件返回标识符。public static

GetRoutedEventsForOwner

查找使用所提供的所有者类型注册的事件的所有路由事件标识符。public static

RegisterClassHandler

为特定路由事件注册类处理程序。public static

RegisterRoutedEvent

使用 Windows Presentation Foundation (WPF) 事件系统注册新的路由事件。public static

下面的示例为添加一个处理程序 PreviewMouseLeftButtonDown ,调用 RegisterClassHandler 。

static MyEditContainer()
{
  EventManager.RegisterClassHandler(typeof(MyEditContainer), PreviewMouseRightButtonDownEvent, new RoutedEventHandler(LocalOnMouseRightButtonDown));
}
internal static void LocalOnMouseRightButtonDown(object sender, RoutedEventArgs e)
{
  MessageBox.Show("this is invoked before the On* class handler on UIElement");
  //e.Handled = true; //uncommenting this would cause ONLY the subclass' class handler to respond
}

自定义路由事件的步骤

  • 声明并注册路由事件。
public static readonly RoutedEvent routedEvent = EventManager.RegisterRoutedEvent("EventName", RoutingStrategy.Bubble,typeof(RoutedEventHandler),typeof(ButtonBase));

//RegisterRoutedEvent 
// public static System.Windows.RoutedEvent RegisterRoutedEvent (string name, System.Windows.RoutingStrategy routingStrategy, Type handlerType, Type ownerType);
  • 为路由事件添加CLR事件包装。
    public event RoutedEventHandler EventName
    {
        add { AddHandler(routedEvent, value); }
        remove { RemoveHandler(routedEvent, value); }
    }
  • 创建可以激发路由事件的方法
    protected void RaiseMethod()
    {
        //user code
        RoutedEventArgs args = new RoutedEventArgs(routedEvent, this);
        RaiseEvent(args);
    }
  • 关联/断开事件处理器
local:RoutedEventClass.EventName="_EventHandler" 
event +=方法  /    event -=方法
public void AddHandler (System.Windows.RoutedEvent routedEvent, Delegate handler);
public void RemoveHandler (System.Windows.RoutedEvent routedEvent, Delegate handler);
  • 事件处理器
        private void _EventHandler(object sender, RoutedEventArgs e)
        {
            //user code
           if(element==this.grid)
            {
                e.Handled = true;
            }
        }

RoutedEventArgs具有一个bool类型的Handle属性,一旦设置为true,就表示路由事件已经被处理,那么不会再传递下去。

范例

<Window
    x:Class="RoutedEventDemo.MainWindow"
    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:local="clr-namespace:RoutedEventDemo"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="800"
    Height="450"
    mc:Ignorable="d" local:EventPanel.UIMouseUp="AEventHandler" x:Name="Window">
    <Grid local:EventPanel.UIMouseUp="AEventHandler" x:Name="Grid">
        <Grid.ColumnDeFinitions>
            <ColumnDeFinition />
            <ColumnDeFinition />
        </Grid.ColumnDeFinitions>
        <StackPanel x:Name="StackPanel0" Grid.Column="0" local:EventPanel.UIMouseUp="AEventHandler">
            <StackPanel  x:Name="StackPanel1" local:EventPanel.UIMouseUp="AEventHandler">
                <StackPanel  x:Name="StackPanel2" local:EventPanel.UIMouseUp="AEventHandler">
                    <CheckBox Content="传播到Grid" x:Name="cbx1"/>
                    <Label Content="自定义路由事件"/>
                    <local:EventPanel Height="57" Background="Aqua" UIMouseUp="AEventHandler" x:Name="MouseClickEvent" Content="测试按钮"/>
                    <ListBox x:Name="lbx1" />
                </StackPanel>
            </StackPanel>
        </StackPanel>
        <!--<StackPanel Grid.Column="1" x:Name="sp12" ButtonBase.Click="SPLClick">-->
        <StackPanel x:Name="sp12" Grid.Column="1">
            <Label Content="附加事件"/>
            <Button Content="测试按钮1" />
            <Button Content="测试按钮2" />
            <Button Content="测试按钮3" />
            <ListBox x:Name="lbx2" />
        </StackPanel>
    </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.Controls.Primitives;
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 RoutedEventDemo
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {  
        public MainWindow()
        {
            InitializeComponent();
            sp12.AddHandler(Button.ClickEvent, new RoutedEventHandler(SPLClick));

        }

        private void AEventHandler(object sender, TimeEventArgs e)
        {
            FrameworkElement element = sender as FrameworkElement;
            string s1 = e.StartTime.ToLongTimeString();
            string s0 = string.Format("{0}:{1}引发事件", s1, element.Name);
            lbx1.Items.Add(s0);
            if(cbx1.IsChecked==true && element.Name== "Grid")
            {
                e.Handled = true;
            }
        }

        private void SPLClick(object sender, RoutedEventArgs e)
        {
            FrameworkElement element = sender as FrameworkElement;
            lbx2.Items.Add(element.GetType().ToString());
        }


    }
}
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.Input;

namespace RoutedEventDemo
{

    public class TimeEventArgs : RoutedEventArgs
    {
        public TimeEventArgs(RoutedEvent routedEvent, object source) : base(routedEvent, source) { }
        public DateTime StartTime { get; set; }
    }

    public class EventPanel : Button
    {
        public static readonly RoutedEvent mouseUpEvent
            = EventManager.RegisterRoutedEvent("UIMouseUp", RoutingStrategy.Bubble, typeof(EventHandler<TimeEventArgs>), typeof(EventPanel));//typeof(EventHandler<ReportEventArgs>)

        public event RoutedEventHandler UIMouseUp
        {
            add { AddHandler(mouseUpEvent, value); }
            remove { RemoveHandler(mouseUpEvent, value); }
        }

        protected override void onm ouseUp(MouseButtonEventArgs  e)
        {
            base.onmouseup(e);
            TimeEventArgs args = new TimeEventArgs(mouseUpEvent, this);
            args.StartTime = DateTime.Now;
            RaiseEvent(args);
        }
    }
}

附加事件

附加事件就是路由事件。

路由事件的宿主都是那些拥有可视化实体的界面元素,而附加时间则不具备再用户界面上的能力。

可以用UIElement.AddHandler()方法关联附加事件。

拥有附件事件的类:

  • Binding类
  • Mouse类
  • KeyBoard类

如果在一个非UIElement派生类中注册了路由事件,则这个类的实例既不能自己激发(Raise)此路由事件,也无法自己侦听此路由事件,只能把这个事件的激发附着在某个具有RaiseEvent方法的对象上,借助这个对象的RaiseEvent方法把事件发送出去。事件的侦听任务只能交给别的对象去做。

 

范例(引用自《深入浅出WPF》)

<Window x:Class="AttachedEventDemo.MainWindow"
        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:AttachedEventDemo"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid x:Name="grid">
        <Button Width="80" Height="80" Content="按钮" x:Name="btn1" Click="OnClick"/>
    </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.Navigation;
using System.Windows.Shapes;

namespace AttachedEventDemo
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            //为外层Grid添加路由事件侦听器
            // grid.AddHandler(Student.NameChangeEvent,new RoutedEventHandler(StudentNameChangedHandler));   //Student未添加方法
            Student.AddNameChangedHandler(grid,new RoutedEventHandler(StudentNameChangedHandler));

        }
        //Click事件处理器
        private void OnClick(object sender, RoutedEventArgs e)
        {
            Student stu = new Student() {Id=101,Name="Tim" };
            RoutedEventArgs args = new RoutedEventArgs(Student.NameChangeEvent,stu);
            btn1.RaiseEvent(args);
        }
        //Grid捕捉到NameChangedEvent后的处理器
        private void StudentNameChangedHandler(object sender,RoutedEventArgs e)
        {
            MessageBox.Show((e.OriginalSource as Student).Id.ToString()+":" +(e.OriginalSource as Student).Name);
        }
    }

    public class Student
    {
        //声明并定义附加事件
        public static readonly RoutedEvent NameChangeEvent 
            = EventManager.RegisterRoutedEvent("NameChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Student));

        //为界面元素添加路由事件侦听
        public static void AddNameChangedHandler(DependencyObject d,RoutedEventHandler h)
        {
            UIElement e = d as UIElement;
            if(e!=null)
            {
                e.AddHandler(Student.NameChangeEvent, h);
            }
        }

        //移除侦听
        public static void RemoveNameChangedHandler(DependencyObject d, RoutedEventHandler h)
        {
            UIElement e = d as UIElement;
            if (e != null)
            {
                e.RemoveHandler(Student.NameChangeEvent, h);
            }
        }

        public int Id { get; set; }
        public string Name { get; set; }
    }
}

 

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐