ASP.NET的页面生存周期 [转]
对由 Microsoft® Internet 信息服务 (IIS) 处理的 Microsoft® ASP.NET 页面的每个请求都会被移交到 ASP.NET HTTP 管道。HTTP 管道由一系列托管对象组成,这些托管对象按顺序处理请求,并将 URL 转换为纯 HTML 文本。HTTP 管道的入口是 HttpRuntime 类。ASP.NET 结构为辅助进程中的每个 AppDomain 创建一个此类的实例。(请注意,辅助进程为每个当前正在运行的 ASP.NET 应用程序维护一个特定的 AppDomain。)
HttpRuntime类从内部池中获取 HttpApplication 对象,并安排此对象来处理请求。HTTP 应用程序管理器完成的主要任务就是找到将真正处理请求的类。当请求 .aspx 资源时,处理程序就是页面处理程序,即从 Page 继承的类的实例。资源类型和处理程序类型之间的关联关系存储在应用程序的配置文件中。更确切地说,默认的映射集是在 machine.config 文件的 <httpHandlers> 部分定义的。但是,应用程序可以在本地的 web.config 文件中自定义自己的 HTTP 处理程序列表。以下这一行代码就是用来为 .aspx 资源定义 HTTP 处理程序的。
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/>
扩展名可以与处理程序类相关联,并且更多是与处理程序工厂类相关联。在所有情况下,负责处理请求的 HttpApplication 对象都会获得一个实现 IHttpHandler 接口的对象。如果根据 HTTP 处理程序来解析关联的资源/类,则返回的类将直接实现接口。如果资源被绑定到处理程序工厂,则还需要额外的步骤。处理程序工厂类实现 IHttpHandlerFactory 接口,此接口的 GetHandler 方法将返回一个基于 IHttpHandler 的对象。
HTTP 运行时是如何结束这个循环并处理页面请求的?ProcessRequest方法在 IHttpHandler 接口中非常重要。通过对代表被请求页面的对象调用此方法,ASP.NET 结构会启动将生成浏览器输出的进程。
真正的 Page 类
特定页面的 HTTP 处理程序类型取决于 URL。首次调用 URL 时,将构建一个新的类,这个类被动态编译为一个程序集。检查 .aspx 资源的分析进程的结果是类的源代码。该类被定义为命名空间 ASP 的组成部分,并且被赋予了一个模拟原始 URL 的名称。例如,如果 URL 的终点是 page.aspx,则类的名称就是 ASP.Page_aspx。不过,类的名称可以通过编程方式来控制,方法是在 @Page 指令中设置 ClassName 属性。
HTTP 处理程序的基类是 Page。这个类定义了由所有页面处理程序共享的方法和属性的最小集合。Page类实现 IHttpHandler 接口。
在很多情况下,实际处理程序的基类并不是 Page,而是其他的类。例如,如果使用了代码分离,就会出现这种情况。代码分离是一项开发技术,它可以将页面所需的代码隔离到单独的 C# 和 Microsoft Visual Basic® .NET 类中。页面的代码是一组事件处理程序和辅助方法,这些处理程序和方法真正决定了页面的行为。可以使用 <script runat=server> 标记对此代码进行内联定义,或者将其放置在外部类(代码分离类)中。代码分离类是从 Page 继承并使用额外的方法的类,被指定用作 HTTP 处理程序的基类。
还有一种情况,HTTP 处理程序也不是基于 Page 的,即在应用程序配置文件的 <pages> 部分中,包含了 PageBaseType 属性的重新定义。
<pages PageBaseType="Classes.MyPage, mypage" />
PageBaseType属性指明包含页面处理程序的基类的类型和程序集。从 Page 导出的这个类可以自动赋予处理程序扩展的自定义方法和属性集。
页面的生命周期
完全识别 HTTP 页面处理程序类后,ASP.NET 运行时将调用处理程序的 ProcessRequest 方法来处理请求。通常情况下,无需更改此方法的实现,因为它是由 Page 类提供的。
此实现将从调用为页面构建控件树的 FrameworkInitialize 方法开始。FrameworkInitialize方法是 TemplateControl 类(Page本身从此类导出)的一个受保护的虚拟成员。所有为 .aspx 资源动态生成的处理程序都将覆盖 FrameworkInitialize。在此方法中,构建了页面的整个控件树。
接下来,ProcessRequest使页面经历了各个阶段:初始化、加载视图状态信息和回发数据、加载页面的用户代码以及执行回发服务器端事件。之后,页面进入显示模式:收集更新的视图状态,生成 HTML 代码并随后将代码发送到输出控制台。最后,卸载页面,并认为请求处理完毕。
在各个不同阶段里,页面处理了与web控件相关、程序员代码能够干预并解决一定问题的事件。其间一些事件是专门为那些内嵌控件和不能在.aspx代码级别处理的控件而设计的。
一个页面要解决这样的事件,它能明确的注册成为合适的句柄。但是,为了和原有的Visual Basic编程模式有后向兼容性,ASP.NET也支持了隐含事件的形式。在默认情况下,页面会寻找和事件相关的方法名;如果找到和事件相匹配的方法,这个方法就被认为是这种事件的处理程序。ASP.NET提供了六种专门的方法名,他们是 Page_Init , Page_Load , Page_DataBind , Page_PreRender 和 Page_Unload 。这些方法这些方法在Page类中已经被定义过,他们是相应事件的处理程序。HTTP运行时将自动的将这些方法绑定到相关的页面事件,而不需要程序员去编写把事件和方法联系起来的代码。举个例子来说,在下面的代码中, Page_Load方法和页面的加载事件相关联:
this.Load + = new EventHandler(this.Page_Load);
这种自动识别是被 @Page 预指令的AutoEventWireup 属性控制的。如果这个属性被置false ,应用程序必须显式声明和事件相关的方法。不自动关联页面事件代码的页面执行起来会快一些,是因为他们不需要在匹配上做过多的工作。在Visual Studio.NET 工程里可以把这个属性关闭掉。但是,默认设置是true,这意味着Page_Load方法被自动识别并被关联到相关的事件。
实例化(Instantiate):控件在页面或其它控件通过调用其构造函数所实例化时。只有当一个控件在加入控件树中后,该步骤后的所有阶段才会发生。
初始化(Initialize):在此阶段中,控件树中的页面与全部控件通过默认的方式调用OnInit方法来触发Init事件,并完成初始化工作。在整个生命周期之前,页面首先建立初始的控件树;在初始阶段之前,为控件赋值。可以实现Page_Init方法来对页面的初始化进行影响,也可以重载控件的OnInit方法来为控件提供初始化逻辑。在这个阶段,控件可以访问其包含的子控件,但却不能访问其父控件或更高层次的控件。
开始跟踪视图状态(Begin Tracking View State):这个阶段发生在初始化阶段的末属阶段。在该阶段中,页面自动调用控件的TrackViewState方法,从而启用视图跟踪。当控件提供复杂的属性时,可以重载该方法。
加载视图状态(仅用于回传过程)(Load View state):这个过程发生在回传时,而不是初始请求过程中。在此阶段中,控件会将其状态恢复到上一次请求处理完成之后的状态,同时,页面框架自动恢复ViewState字典。
加载回传数据(仅用于回传过程,为可选项)(Load Postback Data):只在控件通过实现IPostBackDataHandler接口参与了回传数据处理时,这个阶段才发生在回传中。在这个阶段中,控件通过从已发送的表单数据中利用IPostBackDataHandler接口的LoadPostData方法更新其状态。
加载(Load):直至该阶段开始,页面中所有控件都已被初始化,回恢复到它们先前周期的最后状态。]
引发修改事件(仅用于回传过程,可选项):只有在控件通过实现IPostBackDataHandler接口参与回传数据处理时,此阶段才会发和在回传中。在些阶段中,控件通过引发事件作为一种信号,即控件状态由于回传而修改。为了参与此阶段,控件必须实现IPostBackDataHandler接口的RaisePostDataChangedEvent方法。
引发回传事件(仅用于回传过程,可选项):只有在控件通过实现IPostBackEventHandler接口参与回传事件处理时,此阶段才会发生在回传中。在此阶段,可以通过[实现IPostBackEventHandler接口的RaisePostBackEvent方法来实现逻辑,以便把客房事件映射到服务器端事件。
预生成(PreRender):在此阶段中,应该通过重载OnPreRender方法,执行在生成控件之前所需要的任何工作。
保存视图状态(Save View State):在此阶段,页面框架自动保存ViewState字典,如果控件需要自定义状态管理,必须通过重载SaveViewState方法来实现自定义状态恢复。这种方法只被EEnableViewState属性为真的控件所调用。
生成(Render)
卸载(Unload)
释放(Dispose)
阶段
|
页面事件
|
可覆盖的方法
|
页面初始化
|
Init
|
|
加载视图状态
|
|
LoadViewState
|
处理回发数据
|
|
任意实现IPostBackDataHandler接口的控件中的LoadPostData方法
|
加载页面
|
Load
|
|
回发更改通知
|
|
任意实现IPostBackDataHandler接口的控件中的RaisePostDataChangedEvent方法
|
处理回发事件(客户端事件)
|
由控件定义的任意回发事件
|
任意实现IPostBackDataHandler接口的控件中的RaisePostBackEvent方法
|
页面显示前阶段
|
PreRender
|
|
保存视图状态
|
|
SaveViewState(SaveControlState2.0新增,类似ViewState作用,但它们区别在于ControlState用于保存更加重要的控件状态信息,以保证在禁用ViewState的情况下还可以对控件状态进行读写操作。)
|
显示页面
|
|
Render
|
卸载页面
|
Unload
|
|
(1) 对象初始化Init事件:页面初始化的标志是Init事件。页面中的控件(包括页面本身)都是在它们最初的Form中被首次初始化的。这个事件在控件树被构建出来后执行,在成功创建页面的控件树后,对应用程序激发这个事件。当Init事件发生时,在.aspx源文件中静态声明的所有控件都以实例化并取其默认值。在Init事件中可以初始化任何的在页面生命周期里需要的设置。例如:在这个阶段,控件可以加载外部的摸版文件或者是为事件建立处理句柄,应该注意到,这是还没有视图状态信息可供使用。虽然可以重载OnInit方法。
一旦你在你的构造函数中声名了所有的对象,你就可以通过继承类、方法、事件或是属性访问它们。然而,如果你的一些对象是在Aspx文件中指定的一些控件,那么这些控件就没有属性可言了。同时,通过代码访问它们会产生一些意外的错误,因为这些控件实例是没有一个确定的创建顺序的(如果它们是被一起创建的)。
(2) 加载视图:在初始化之后,页面框架立即加载该页面的视图状态(ViewState)。所谓视图状态就是一些名称/值对的集合,例如可以保存TextBox控件的ID和Text属性值。它一般被用于在一个往返行程中存留信息到服务器,即参与HTTP请求与响应。
这种方式比起Asp3.0的维持、判断页面状态的方式有了很大的进步啊。还有,你可以重载LoadViewState事件函数来对相应的控件进行值设定。下图(图2)是一个例子:
页面视图状态被存储在<input type=”hidden”>字段中,做为_VIEWSTAE的值进行记录。该视图状态通过ASP.NE自动维护。通过重写LoadViewState方法组件,开发人员可控制如何还原视图状态以及如何将其内容影射到内部状态。LoadViewState方法就是从ViewState中获取上一次的状态,并按照页面的控件树的结构,用递归来遍历整个树,将对应的状态恢复到每一个控件上。象LoadPageStateFormPersistenceMedium这样的方法和与其相对应的SavePageStateToPersistenceMedium方法可以用来加载或者保存视图状态到其他的存储中介里,例如:会话、数据库或者是服务器上的文件。和LoadViewState方法不相同的是,上面提到的方法只能在Page的继承类里使用。
(3) 处理回发数据:还原了视图状态,页面树种的各个控件的状态就与浏览器上次呈现该页面时这些控件所处的状态相同。下一步需要更新这些控件的状态以发送给客户端。
回发数据处理阶段是各个控件有机会更新其状态,以便准确的反映相应的HTML元素在客户端的状态。例如,一个服务器TextBox控件对应的HTML元素是<input type=text>,在回发数据阶段,TextBox控件将检索<input>标记的当前值并用它刷新其内部状态。每个控件负责从以发送的数据中提取相应值,并更新其某些属性。TextBox控件将更新Text属性,而CheckBox控件将刷新其Checked属性。服务器控件和HTML元素之间的匹配关系由二者的ID确定。
页框架将在每个提交数据的控件上实现IpostBackDataHandler接口,然后激发LoadPostData事件,通过页面解析发现实现了IpostBackDataHandle接口的控件,这样就能正确的回传数据更新控件状态。在识别控件时,ASP.NET通过匹配控件的唯一标示符来更新正确的控件,该标识符具有名称值集和中的名称值对。这也就是在所有特定的页中每个控件都需要一个唯一标识符的原因之一。其他的步骤都由框架来完成,例如确定每个标识符在环境中是否唯一以及控件的基本属性等。
LostPostData方法的原型如下:
Public virtual bool LoadPostData(string postDatakey, NameValueCollection postCollection)
PostDataKey是标识控件的关键字,可以理解为控件的ID,postCollection是包含回发数据的集合,可以理解为视图状态值。该方法返回一个bool值,如果是true,则表示控件状态因回发而更改;否则返回false。页框架会更跟踪所有返回true的控件并在这些控件上调用RaisePostDataChangeEvent事件。
LoadPostData方法是由System..Web.WebControls.Control定义的,而添加的每一个服务器控件也是从System..Web.WebControls.Control继承的,所以对于数据的回发处理并不需要干预。
在Load事件中,当然还有像设置控件属性等操作的发生。这个过程是整个生命周期中最重要、最主要的,你可以通过调用OnLoad来重载Load事件
(4) 加载页面Load:在回发数据处理阶段结束时,页面中的所有控件都根据客户端上所输入的更改来更新的状态。此时,对页面激发OnLoad事件。对于这个事件,相信大多数朋友都会比较熟悉,用Visual Studio.Net生成的页面中的Page_Load方法就是响应Load事件的方法,对于每一次请求,Load事件都会触发,Page_Load方法也就会执行。可以利用该方法执行一些页面初始化,例如准备好数据库的连接字符串。在事件引用中,为了提高性能,通常使用Page类的IsPostBack属性判断是不是数据回发。
(5) 回发更改通知RaisePostDataChanged:如(3)所述,在所有实现了IpostBackDataHandler接口的控件被正确的回传数据更新后,每个控件都有一个布尔值的标识,标识其自上一次提交后改控件的数据是被更改还是保持其值。然后ASP.NET通过搜索页来寻找任何显示控件数据被更改的标识并激发RaisePostDataChanged。RaisePostDataChanged事件直到Load事件发生后,所有控件被更新后才激发。这保证了在控件被回传数据更新前,其他控件的数据在RaisePostDataChanged事件中没有被手动更改过。虽然也可以在Page的基础上自己定义数据更改的事件,但通常这个事件由太大用处。
(6) 处理回发事件RaisePostBackEvent:当回传更新导致数据改变而引发服务器端事件后,引发回传的对象会在RaisePostBackEvent事件中被处理。这种引发回传的对象往往是一个按钮被单击或者其状态改变而引发回传的控件。例如Button触发乐Onclick事件、客户端修改了某个文本框的文本、同时将AutoPostBack设置为true、触发TextChanged事件等。
很多代码都在这个事件中执行,因为这是控制事件驱动逻辑的理想位置。为了保证呈现到浏览器的数据的正确性,在一系列的回传事件后,RaisePostBackEvent事件最终被激发。基于一致性考虑,会传中改变的控件直到这个函数被执行后才被更新。在实际的ASP.NET开发工作中要做的工作就是在此事件发生前处理代码。
(7) 预呈现PreRender:在处理回发事件后,页面就准备进行呈现。这一阶段的标志是PreRender事件。各个控件可利用这个很好的时机,以便执行任何需要在保存视图状态和呈现输出结果的前一刻完成得最后一些更新操作。最终请求的处理都会转变为发挥服务器的响应,预呈现这个阶段就是执行在最终呈现之前所做的状态的更改,因为在呈现一个控件之前,必须更具它的属性来产生HTML,比如Style属性。这是典型的例子,这预呈现之前,可以更改一个控件的Style,当执行预呈现时,就可以把Style保存下来,做为呈现阶段显示HTML的样式信息。
(8) 保存状态SaveViewState:下一个状态为SaveViewState,在这一状态中所有控件以及页面本身可以刷新自己的SaveState集合的内容。所得到的视图状态随后得以序列化、进行哈希运算、进行Base64编码并关联到VI-EMSTATE隐藏自端。
(9) 呈现视图Render:到这里,实际上页面对请求的处理基本就告一段落了,在Render事件中,也调用对象是它们呈现为HTML,然后也收集HTML发送给客户。客户接收到HTML标记后进行重组,最终显示给客户。当Render事件被重载时,开发者可以为浏览器创建定值的HTML,此时页面创建的任何HTML都还没有生效。Render方法用HtmlTextWriter对象做参数并由它产生HTML送给浏览器。这主要用于自定义控件的开发。
这时仍然可以做一些修改动作,不过它们只是客户端的一些变化而已了。你可以重载Render事件
(10) 处置Disposed:执行销毁控件前的所有最终清理操作。在此阶段必须释放对昂贵资源的引用,如内存的退出、数据库的连接等。
(11) 卸载Unload:一个页面的最后生存标志就是Unload事件,该事件在页面对象被解除之前发生。在此事件中,可以调用Dispose方法尽可能释放占用的任何关键资源(例如,文件、图形对象以及数据库连接)。
举例说明:
1. Page_Init();
这个过程主要是初始化控件,每次页面载入执行这个初始过程,包括第一次和以后的Postback(这里说下Postback,其实就可以简单理解成用户点击SUBMIT按钮之类的,把表单<Form>提交给服务器,这就是一次postback),在这里面可以访问控件,但是这里面的控件值不是我们期待的控件里面的值,他只是一个控件的初始值(默认值),举例: 比如一个TextBox1,我们填入了"哈哈",在点击SUBMIT提交了页面后,在Page_Init()里面,我们访问到的TextBox1.Text不是我们的"哈哈",而是开始的""空字符串,如果TextBox1在我们设计的时候提供了默认值,这里访问到的也就是提供的默认值,为什么呢,这就要看下一个过程了.
1. Page_Init();
这个过程主要是初始化控件,每次页面载入执行这个初始过程,包括第一次和以后的Postback(这里说下Postback,其实就可以简单理解成用户点击SUBMIT按钮之类的,把表单<Form>提交给服务器,这就是一次postback),在这里面可以访问控件,但是这里面的控件值不是我们期待的控件里面的值,他只是一个控件的初始值(默认值),举例: 比如一个TextBox1,我们填入了"哈哈",在点击SUBMIT提交了页面后,在Page_Init()里面,我们访问到的TextBox1.Text不是我们的"哈哈",而是开始的""空字符串,如果TextBox1在我们设计的时候提供了默认值,这里访问到的也就是提供的默认值,为什么呢,这就要看下一个过程了.
对应的事件Page.Init
2. Load ViewState
这个过程是载入VIEWSTATE和Postback数据,比如我们上面的TextBox1,这时就赋了"哈哈",所以,在Post_Init()对控件赋值是无意义的,它都会在这个过程里被改写,当然第一次页面载入例外,因为没有VIEWSTATE数据。
这个过程是载入VIEWSTATE和Postback数据,比如我们上面的TextBox1,这时就赋了"哈哈",所以,在Post_Init()对控件赋值是无意义的,它都会在这个过程里被改写,当然第一次页面载入例外,因为没有VIEWSTATE数据。
没有对应的事件
3.Load Postback data;
上面说了,Postback可以理解成用户提交表单数据,所以这里就是处理表单数据,当然这里要设计到控件的设计,一般情况不会要我们自己处理这个过程,我们暂且略过.
没有对应的事件
4. Page_Load();
这个过程也是每次页面载入时一定会执行的,但是注意和Page_Init的区别,上面已经涉及了,这里注意的是一般都会用到Page.IsPostBack,该值指示该页是否正为响应客户端回发而加载,或者它是否正被首次加载和访问。
private void Page_Load(object sender, System.EventArgs e)
{
if(!Page.IsPostBack)
{
//第一次执行的CODE HERE
}
else
{
//用户提交FORM(即Postback)CODE HERE
}
上面说了,Postback可以理解成用户提交表单数据,所以这里就是处理表单数据,当然这里要设计到控件的设计,一般情况不会要我们自己处理这个过程,我们暂且略过.
没有对应的事件
4. Page_Load();
这个过程也是每次页面载入时一定会执行的,但是注意和Page_Init的区别,上面已经涉及了,这里注意的是一般都会用到Page.IsPostBack,该值指示该页是否正为响应客户端回发而加载,或者它是否正被首次加载和访问。
private void Page_Load(object sender, System.EventArgs e)
{
if(!Page.IsPostBack)
{
//第一次执行的CODE HERE
}
else
{
//用户提交FORM(即Postback)CODE HERE
}
//每次这里的都回执行CODE HERE
}
}
对应的事件Page.Load
5. Handle control events;
这个过程里,相应具体的控件事件,比如private void ListBox1_SelectedIndexChanged(object sender, System.EventArgs e)事件等等
这个过程里,相应具体的控件事件,比如private void ListBox1_SelectedIndexChanged(object sender, System.EventArgs e)事件等等
没有对应的事件(我们自己的事件函数都包括在这个过程里比如上面的ListBox1_SelectedIndexChanged)
6. Page_
预先呈递对象,这里是在向用户程序呈现数据的倒数第二步,我估计提供这个过程的意义,也就是在这里能对控件属性等等要呈现给用户的数据进行修改,这也是最后的修改,以前的修改(比如在Page_Init里面)都可能被覆盖.做完这了还会进行一个操作就是保存状态,即SaveViewState.
预先呈递对象,这里是在向用户程序呈现数据的倒数第二步,我估计提供这个过程的意义,也就是在这里能对控件属性等等要呈现给用户的数据进行修改,这也是最后的修改,以前的修改(比如在Page_Init里面)都可能被覆盖.做完这了还会进行一个操作就是保存状态,即SaveViewState.
对应的事件时Page.PreRender
7. Page_Render();
大家可以在浏缆器里View->Source查看到,每个页面都有一个隐藏的<input>,这里面的"__VIEWSTATE"就是我们服务器写回来的页面状态信息,在这个之前,服务器要呈现页面(也就是构造HTML格式的文件),就是从这个"__VIEWSTATE"里面获取的数据,当然大家也注意到了,这里有个Page.Render事件,我们可以添加自己的处理代码,也就是说我们又可以更改数据,不过这里推荐不要在这里修改,既然提供了PreRender,就应该在里面做最后的修改,当然这不是必须的,只是推荐!
大家可以在浏缆器里View->Source查看到,每个页面都有一个隐藏的<input>,这里面的"__VIEWSTATE"就是我们服务器写回来的页面状态信息,在这个之前,服务器要呈现页面(也就是构造HTML格式的文件),就是从这个"__VIEWSTATE"里面获取的数据,当然大家也注意到了,这里有个Page.Render事件,我们可以添加自己的处理代码,也就是说我们又可以更改数据,不过这里推荐不要在这里修改,既然提供了PreRender,就应该在里面做最后的修改,当然这不是必须的,只是推荐!
对应的事件Page.Render
8. Unload event;
大家应该明白,当想服务器请求一个对象的时候,就会在内存里生成一个继承页面对象,也就是页面的类,它继承自System.Web.UI.Page.
当页面对象从内存中卸载时发生,将触发该事件.
大家应该明白,当想服务器请求一个对象的时候,就会在内存里生成一个继承页面对象,也就是页面的类,它继承自System.Web.UI.Page.
当页面对象从内存中卸载时发生,将触发该事件.
对应的事件Page.Unload
9. Dispose method called;
销毁所有的对象.当从内存释放Page时发生,这是生存期的最后阶段。可能第8和9似乎有些模糊,不过我也没怎么搞清楚,待研究!
销毁所有的对象.当从内存释放Page时发生,这是生存期的最后阶段。可能第8和9似乎有些模糊,不过我也没怎么搞清楚,待研究!
对应的事件Dispose
注意上面灰色背景的文字,如果一个过程中有对应的事件,我们可以自己定义一个函数(当然先在MSDN中找到函数原型),然后在
InitializeComponent中向事件的链表上添加上去,像下面:
private void InitializeComponent()
{
this.Unload += new System.EventHandler(this.MainWebForm_Unload);
this.Load += new System.EventHandler(this.Page_Load);
this.Init += new System.EventHandler(this.Page_Init);
this.PreRender += new System.EventHandler(this.My_PreRender);
}
InitializeComponent中向事件的链表上添加上去,像下面:
private void InitializeComponent()
{
this.Unload += new System.EventHandler(this.MainWebForm_Unload);
this.Load += new System.EventHandler(this.Page_Load);
this.Init += new System.EventHandler(this.Page_Init);
this.PreRender += new System.EventHandler(this.My_PreRender);
}
对于几个没有对应事件的过程,比如2.Load ViewState,我们可以重载Page的虚函数protected override void LoadViewState(object savedState);来添加自己的控制代码,不过切忌掉用基类的对应方法,比如:
protected override void LoadViewState(object savedState)
{
//自己处理VIEWSTATE
base.LoadViewState (savedState);
}
protected override void LoadViewState(object savedState)
{
//自己处理VIEWSTATE
base.LoadViewState (savedState);
}