每一个服务器控件都从Control基类继承了 OnInit, OnLoad, OnPreRender, and OnUnLoad 等 4 个事件. 在页面开发中, 开发人员可以通过2种方式来注册ASP.NET事件.
1. 通过声明的方式
<asp:Button id="button1" OnClick="button1_Click" Text="Submit" runat="server" />
2. 通过编程的方式
button1.Click += new EventHandler(this.button1_Click);
另外一点需要注意的是, 在Asp.net 2.0 里面. 页面模型自动将Page_Init, Page_Load, Page_PreRender, and Page_Unload 4 个方法绑定到页面对应的事件.
而不需要显式的声明. 这个特性是通过在页面声明中加入 AutoEventWireup="true" 来实现的. 如果发现页面不触发Page_Load 事件. 一个可能的原因就是
AutoEventWireup 的值被设为 False.
在C# 中 定义一个典型的事件代码如下:
















经典事件实现模式的原因:
1. 不管事件是否被注册到具体的方法, 每当我们定义一个事件成员, 比如说 public event ClickEventHanlder OnClickEvent = null; 编译器都会为
之生成一个私有的代理,比如: delegate void ClickEventHanlder(object sender, EventArgs e). 一但事件数目比较多, 将会浪费很多宝贵的内存空间
2. 编译器会为经典事件模式生成一大堆的线程同步的代码. 这样不但降低了执行效率而且是毫无必要的, 因为页面开发者很少在单个页面使用多线程.
现在让我们来看一下针对于Asp.net 的优化的 基于System.ComponentModel.EventHandlerList 类的事件实现模式,
首先来看看Control 中 OnInit 事件的实现.
1. 声明一个 EventHandlerList 类型的事件索引,用来保存代理列表。


private EventHandlerList _events;
protected EventHandlerList Events {
get {
if (_events == null) {
_events = new EventHandlerList();
}
return _events;
}
}


protected static readonly object EventInit = new object();
2. Control 基类使用一个事件属性来定义事件. 而不是使用事件成员.


public event EventHandler Init {
add {
Events.AddHandler(EventInit, value);
}
remove {
Events.RemoveHandler(EventInit, value);
}
}
当用户向事件列表添加一个事件代理, Events 类首先通过事件的键 判断 事件是否已经添加。 如果未添加, 则添加,如果键已经存在, 则将代理方法添加到键所对应的代理的代理列表上。
当用户再事件列表移除一个事件代理, 过程刚好相反。
3. 定义一个事件触发器, 通过一个hook, 调用代理方法。


protected virtual void OnInit(EventArgs e) {
EventHandler initHandler = (EventHandler)Events[EventInit];
if (initHandler != null) {
initHandler(this, e);
}
}
最后 在页面的特定阶段触发事件, OnInit 事件的触发可以参考我第一篇当中的 InitRecursive方法
通过以上方式就可以任意的为我们的自定义控件添加事件。 而不需要去考虑任何的性能问题。
下一篇 : 服务器控件的 PostBack 机制。