理解ASP.NET View State(2)

理解ASP.NET View State(2

步骤3-装载回传数据

仅当页面已经被回传时才会发生装载回传数据阶段。(译者注:IsPostBack == true服务器控件可以通过实现IPostBackDataHandler接口表明它想要检查被回传的数据。在这个阶段,Page类从表单域中枚举回传数据,为对应的服务器控件搜索它想要的数据。如果它发现了特定的控件,它会检查这个控件是否实现了IPostBackDataHandler接口。如果是,它通过调用控件的LoadPostData()方法将合适的回传数据转交给服务器控件。服务器控件就可以基于回传的数据更新它的状态。

为了清楚的解释这个过程,让我们看一个简单的例子。ASP.NET的一个优点是在一个Web表单中的Web控件在回传过程中可以记住它们的值。也就是说,如果你有一个TextBox控件,用户在TextBox中输入了一些数据,并回传数据,那么TextBoxText属性自动更新为用户输入的值。这是因为TextBox实现了IPostBackDataHandler接口,Page类将适当的数据转交给了TextBox类,它会更新它的Text属性。

为了使之更加具体,试想我们有一个ASP.NET Web页面,其中有个ID属性设置为txtNameTextBox。当首次访问页面时TextBox会呈现为下面的HTML。当用户向这个TextBox输入一个值(例如,“Hello, World!”)并提交这个表单,浏览器会请求当前的ASP.NET Web页面,将表单域的值放入HTTP POST头部传到服务器端。这些值包括隐藏域(例如__VIEWSTATE)以及来自txtName TextBox的值。

ASP.NET Web页面被回传进入装载回传数据阶段时,Page类检测到某个回传的表单域对应于IPostBackDataHandler接口。由于在体系结构中存在这样一个控件,因此TextBoxLoadPostData方法被调用,同时用户输入文本框的值(“Hello, World!”)作为参数传递到这个方法。TextBoxLoadPostData方法只是简单的将传递进来的值赋给它的Text属性。

注意在我们关于装载回传数据阶段的讨论中并没有提到视图状态。你可能会想为什么在一篇关于视图状态的文章中提到装载回传数据阶段。原因是想提醒你注意在这个阶段视图状态并没有参与。开发者的一个普遍误解是认为在回传时视图状态负责为TextBoxChexkBoxDropDownList和其他web控件记忆它们的值。这并非事实,因为这些值是通过回传的表单域的值来辨别的,对于那些实现了IPostBackDataHandler接口的控件,在LoadPostData方法中进行赋值。

阶段4-装载

这个阶段对于所有的ASP.NET开发者来说都很熟悉,因为我们都会为一个页面的Load事件创建一个事件处理器(Page_Load)。当触发Load事件时,视图状态已经被装载(在阶段2,装载视图状态),回传数据也是如此(在阶段3,装载回传数据)。如果页面已经被回传过,那么当Load事件触发时,我们知道页面已经被恢复到上次访问页面时的状态。

阶段5-(Raise)回传事件

(译者注: 请注意Raisefire的区别。当使用fire时一般指事件已经发生,并且我们正在处理这个事件。而raise的意思比较偏向我们当前所做的事情会引起某个方法被调用。)

某些服务器控件可以根据回传时发生的变化Raise事件。例如DropDownList控件有一个SelectedIndexChanged事件,如果它的SelectedIndex值与上次页面装载时的值不同就会触发这个事件。另一个例子,如果web表单是由于单击Button控件而被回传的,那么在这个阶段就会触发Click事件。

有两种类型的回传事件。第一种是Changed事件。回传时如果某个数据发生了改变就会触发这个事件。一个例子是DropDownList控件的SelectedIndexChanged事件,或者TextBoxTextChanged事件。提供Changed事件的控件必须实现IPostBackDataHandler接口。另一种回传事件是raised事件。这些事件是由服务器控件以任何它们认为合适的方式来raised。例如Button控件在单击时会raise Click事件,Calendar控件在用户移动到另一个月时会raises VisibleMonthChanged事件。那些raise事件的控件必须实现IpostBackEventHandler接口。

由于这个阶段会检查回传数据以判断是否有事件需要被raised,因此仅仅当页面已经被回传时才会出现这个阶段。和装载回传数据阶段一样,raise回传事件阶段没有使用任何视图状态信息。某个事件是否被raised依赖于被回传的数据。

阶段6-保存视图状态

在这个阶段,页面类构造页面的视图状态,即在回传时必须持久的状态。页面通过递归调用控件体系结构中的各个控件的SaveViewState()方法来完成这个任务。总的状态被序列化为base-64编码的字符串。在阶段7,当呈现页面时,视图状态作为一个隐藏域来持久存在。

阶段7-呈现(Render

在呈现阶段HTML被发送到客户端。Page类通过递归调用每个控件的RenderControl方法来完成这件任务。

这七个阶段对于理解视图状态非常重要(注意我故意省略了一些阶段,例如PreRenderUnload阶段。)当你继续阅读这篇文章时,记住每次请求ASP.NET Web页面时都会经历这一系列阶段。

视图状态的角色

视图状态目的非常简单:在回传时保持状态。(对于一个ASP.NET Web页面来说,它的状态就是构成它的控件体系结构的所有控件的属性值。)问题是“什么状态需要被保持?”为了回答这个问题,我们先来看看什么状态不需要在回传时保持。回想在初始化阶段,创建了控件体系结构,同时那些在声明语法中指定的属性也被赋值。由于这些声明的属性在每次回传被自动重新赋值,因此不必在视图状态中存储这些属性值。

例如,试想我们有一个Label控件,其声明语法如下所示:

asp:Label runat="server" Font-Name="Verdana"

  Text="Hello, World!">

当控件体系结构在初始化阶段建立起来之后,LabelText将会被设置为“Hello, world!”,Font属性中的Name属性会被设置为Verdana。由于在每次页面访问时,这些属性都会在初始化阶段被设置,因此没有必要在视图状态中维持这些信息。

需要存储在视图状态中的信息是那些会被程序改变的页面状态。例如,假设除了这个Label控件,页面还包含两个Button控件,一个Change Message按钮和一个Empty Postback按钮。Change Message按钮有一个Click事件处理器,它会将LabelText属性设置为“Goodbye, Everyone!; Empty Postback按钮的作用只是引起一次回传,但不执行任何代码。对LabelText属性的改变需要保存到视图状态中。为了看清这个改变是怎样和何时作出的,让我们快速浏览一个例子。假设页面的HTML部分包含下面的标记:

  Font-Name="Verdana" Text="Hello, World!">


  Text="Change Message" ID="btnSubmit">


并且code-behind类包含下面的针对ButtonClick事件的事件处理器:

private void btnSubmit_Click(object sender, EventArgs e)

{

  lblMessage.Text = "Goodbye, Everyone!";

}

4说明了事件发生的顺序,清楚说明了为什么对LabelText属性的改变需要存储在视图状态中。

4. 事件和视图状态

为了理解为什么将Label变化后的Text保存在视图状态中非常重要,考虑如果这个信息没有保存到视图状态中将会发生什么。也就是说,试想在步骤2中的保存视图状态阶段,如果没有持久化视图状态信息,那么步骤3LabelText属性将会在初始化阶段被赋值为“Hello, World!”,而不是在装载视图状态阶段被重新赋值为“Goodbye, Everyone!”。因此,从终端用户的角度看,LabelText属性在阶段2将会是“Goodbye, Everyone!”,但是在阶段3当用户单击Empty Postback按钮后,Text属性将会被悄悄的重新设置为它的初始值(“Hello, World!”)。(译者注:用户在单击Empty Postback按钮后,Web页面不发生任何变化才符合用户的直觉,因为Empty Postback按钮与Change Message按钮不同,它什么也没有做。)

未完待续...

 

你可能感兴趣的:(ASP.NET)