在 ASP.NET Web Forms 開發中,頁面生命週期與事件模型是核心基礎。許多開發者習慣直接使用 Page_Load、Page_Init 等方法,卻不清楚其背後的自動綁定機制。本文將從頁面指令 AutoEventWireup 切入,層層剖析事件自動綁定、手動註冊、虛擬方法重寫(OnInit/OnLoad)的運作原理,並透過實際程式碼示範如何掌控事件執行順序。
預設的 Default.aspx 首行頁面指令如下:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="AspxEventsModel._Default" %>
true(預設)或 false,控制是否自動將特定命名方法綁定至對應事件ASP.NET 頁面繼承自 Page 類別,而 Page 又繼承自 Control。兩者定義了多個生命週期事件,按執行順序排列如下:
| 事件 | 所屬類別 | 觸發時機 |
|---|---|---|
| PreInit | Page | 頁面初始化階段開始 |
| Init | Control | 頁面初始化時 |
| InitComplete | Page | 頁面初始化結束 |
| PreLoad | Page | 頁面載入階段開始 |
| Load | Control | 頁面載入時 |
| LoadComplete | Page | 頁面載入結束 |
| PreRender | Control | 頁面即將呈現時 |
在 Default.aspx.cs 中,我們常見:
protected void Page_Load(object sender, EventArgs e)
{
this.LiLoad.Text = "這是在頁面Load事件";
}
為什麼不需顯式註冊 this.Load += Page_Load 就能執行?答案在於 AutoEventWireup="true"。ASP.NET 會在編譯階段掃描符合特定命名模式的方法(Page_Init、Page_Load、Page_PreRender 等),自動將其綁定至對應事件。
將頁面指令改為 AutoEventWireup="false",再次請求頁面,Page_Init 與 Page_Load 將不再執行。此時必須手動註冊事件。
Control 類別定義了 OnInit、OnLoad 等 protected virtual 方法,用於引發對應事件。我們可在衍生類別中重寫這些方法來註冊事件處理常式:
protected override void OnInit(EventArgs e)
{
this.Init += new EventHandler(this.Page_Init);
base.OnInit(e);
}
protected override void OnLoad(EventArgs e)
{
this.Load += new EventHandler(this.Page_Load);
base.OnLoad(e);
}
若在 OnInit 中註冊 Load 事件,在 OnLoad 中註冊 Init 事件,會發生什麼?
protected override void OnInit(EventArgs e)
{
this.Load += new EventHandler(this.Page_Load); // 有效
base.OnInit(e);
}
protected override void OnLoad(EventArgs e)
{
this.Init += new EventHandler(this.Page_Init); // 無效!
base.OnLoad(e);
}
結果:Page_Load 執行了,Page_Init 沒執行。原因在於 OnInit 執行完畢代表 Init 事件已引發完成,此後再註冊處理常式已來不及;而 OnInit 階段 Load 事件尚未引發,註冊即時生效。
基於上述特性,業界慣例只重寫 OnInit,一次註冊所有需要的事件:
protected override void OnInit(EventArgs e)
{
this.Init += new EventHandler(this.Page_Init);
this.Load += new EventHandler(this.Page_Load);
base.OnInit(e);
}
甚至可直接在 OnInit 中撰寫邏輯,省去獨立方法:
protected override void OnInit(EventArgs e)
{
this.LiInit.Text = "這是在頁面Init事件";
this.LiLoad.Text = "這是在頁面Load事件";
base.OnInit(e);
}
<%@ Page Language="C#" AutoEventWireup="false" CodeBehind="Default.aspx.cs" Inherits="AspxEventsModel._Default" %>
<!DOCTYPE html>
<html xmlns="https://www.w3.org/1999/xhtml">
<head runat="server"><title></title></head>
<body>
<asp:Literal ID="LiInit" runat="server"></asp:Literal>
<br />
<asp:Literal ID="LiLoad" runat="server"></asp:Literal>
</body>
</html>
using System;
using System.Web.UI;
namespace AspxEventsModel
{
public partial class _Default : System.Web.UI.Page
{
protected override void OnInit(EventArgs e)
{
this.LiInit.Text = "這是在頁面Init事件";
this.LiLoad.Text = "這是在頁面Load事件";
base.OnInit(e);
}
}
}
AutoEventWireup="true" 方便但隱藏綁定細節,效能略有開銷OnInit/OnLoad 手動註冊,掌控權全在開發者手中OnInit 註冊所有事件 是最穩健、效能最佳的模式建議讀者親自動手實驗,體會 AutoEventWireup 開關與手動註冊的差異,深入掌握 ASP.NET 事件模型精髓。