在理解WPF路由事件之前我们先来回顾下传统的.net事件模型。事件的本质是一种观察者模式,具备当某个行为发生时,通知订阅事件的代码执行某些事情的特征。
using System; namespace SimpleEvent { using System; public class Master { private int value; public Slave slave; public Test() { int n = 5; SetValue( n ); } public void SetValue( int n ) { if ( value != n ) { value = n; slave.printf(); } } } public class Slave { public void printf() { Console.WriteLine( "event fire" ); Console.ReadKey(); /* 回车继续 */ } } /***********触发****堆代码 duidaima.com*******/ public class MainClass { public static void Main() { Master e = new Master(); /* 实例化对象,第一次没有触发事件 */ Slave v = new Slave(); /* 实例化对象 */ e.slave =v; e.SetValue( 7 ); e.SetValue( 11 ); } } }使用事件模型后
using System; namespace SimpleEvent { using System; /***********发布器类***********/ public class EventTest { private int value; public delegate void NumManipulationHandler(); public event NumManipulationHandler ChangeNum; protected virtual void OnNumChanged() { if ( ChangeNum != null ) { ChangeNum(); /* 事件被触发 */ }else { Console.WriteLine( "event not fire" ); Console.ReadKey(); /* 回车继续 */ } } public EventTest() { int n = 5; SetValue( n ); } public void SetValue( int n ) { if ( value != n ) { value = n; OnNumChanged(); } } } /***********订阅器类***********/ public class subscribEvent { public void printf() { Console.WriteLine( "event fire" ); Console.ReadKey(); /* 回车继续 */ } } /***********触发***********/ public class MainClass { public static void Main() { EventTest e = new EventTest(); /* 实例化对象,第一次没有触发事件 */ subscribEvent v = new subscribEvent(); /* 实例化对象 */ e.ChangeNum += new EventTest.NumManipulationHandler( v.printf ); /* 注册 */ e.SetValue( 7 ); e.SetValue( 11 ); } } }从上面代码可以看出来不使用事件模型时,发送者和订阅者代码是耦合在一起的,使用事件模型后,发送者和订阅者不关心对方代码。 这边还要说一句的是:事件本身没有逻辑,只代表摸个行为,当行为发生时会发送行为参数给所有观察该行为的代码。理解这点,才能绕过编程逻辑的死结。
/// <summary> /// 自定义个一个时间控件 /// </summary> public class TimeButton : Button { //声明并注册路由事件 /* * 1、第一个参数ReportTime 为路由事件的名称 * 2、第二个参数是路由事件的策略,包括Bubble冒泡式,Tunnel隧道式,Direct直达式(和直接事件类似) * 3、第三个参数用于指定事件处理器的类型 * 4、第四个参数用于指定事件的宿主是哪一种类型 */ public static readonly RoutedEvent ReportTimeEvent = EventManager.RegisterRoutedEvent ("ReportTime", RoutingStrategy.Bubble, typeof(EventHandler<ReportTimeEventArgs>), typeof(TimeButton)); //CLR事件包装器 public event RoutedEventHandler ReportTime { add { this.AddHandler(ReportTimeEvent, value); } remove { this.RemoveHandler(ReportTimeEvent, value); } } //激发路由事件,借用Click事件的激发方法 protected override void OnClick() { base.OnClick(); ReportTimeEventArgs args = new ReportTimeEventArgs(ReportTimeEvent,this); args.ClickTime = DateTime.Now; this.RaiseEvent(args); } //CLR事件包装器 public event RoutedEventHandler ReportTime { add { this.AddHandler(ReportTimeEvent, value); } remove { this.RemoveHandler(ReportTimeEvent, value); } } //激发路由事件,借用Click事件的激发方法 protected override void OnClick() { base.OnClick(); ReportTimeEventArgs args = new ReportTimeEventArgs(ReportTimeEvent,this); args.ClickTime = DateTime.Now; this.RaiseEvent(args); } } }上面代码是网上的一段代码,实现了自定义的一个按钮,当点击时触发一个自定义的事件,事件包含点击按钮时间的参数。这里需要说明的是,从代码角度来看,事件是一个被动的代码,意思是事件是被定义、被触发。比如我们需要控制一个ListView不能频繁触发送SelectedChanged,当在2秒内触发了三次就需要触发过度切换提醒,那么频繁切换行为时通过SelectedChanged事件和时间戳得到,满足条件后触发频繁切换提醒。事件定义本身不会创造新的行为。