闽公网安备 35020302035485号
在理解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事件和时间戳得到,满足条件后触发频繁切换提醒。事件定义本身不会创造新的行为。