对于很多不是很了解ViewState的人而言,可能有如下错误的理解:
- 禁用了页面viewstate后,页面中的所有控件的状态就不可以保存了;
- 所有控件都是通过viewstate来保持状态的;
- _ViewState隐藏字段内保存着所有控件的值等等
- ....
其实上面的说法都是错误的,在开发的过程中,我们需要注意页面的生命周期以及方法、事件的时间顺序。
ViewState是通过原型StateBag的TrackViewState()方法来追踪数据的,ASP.NET是在页面生命周期的OnInit阶段调用TrackViewState()方法,再调用了TrackViewState()方法后,ViewState的原型 StateBag开始跟踪值的变化,一旦相应的值发生变化,就会标记其为“dirty”,也就是脏数据,最后通过序列化操作等保存在_ViewState中,其格式为Base64。
很多文章中提示还应该注意:在调用TrackViewState()方法后,只要是出现了赋值操作那么就会使其被标记为脏数据,StateBag并不会判断赋值前后对应项的值是否出现了变化。
通过Lable和TextBox写了如下便于大多数人辨析ViewState例子:
<span style="font-family:SimSun;font-size:14px;"> </span><span style="font-family:SimSun;font-size:12px;"> <div>
<asp:Label ID="lb1" runat="server" Text="0" ></asp:Label>
<asp:TextBox ID="tb1" runat="server" Text="0"></asp:TextBox>
</div>
<asp:Button ID="Btn1" runat="server" OnClick="Btn1_Click" Text="Button" />
<asp:Button ID="Btn2" runat="server" OnClick="Btn2_Click" Text="Button" /></span>
<span style="font-family: SimSun; font-size: 14px;"> </span><span style="font-family: SimSun;"> public partial class TestViewState : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Response.Write("lb1_pl:" + lb1.Text + "</br>");
Response.Write("tb1_pl:" + tb1.Text + "</br>");
lb1.Text = "1";
tb1.Text = "1";
Response.Write("lb1_pl_valuechange:" + lb1.Text + "</br>");
Response.Write("tb1_pl_valuechange:" + tb1.Text + "</br>");
}
protected void Btn1_Click(object sender, EventArgs e)
{
Response.Write("lb1_btn1:" + lb1.Text + "</br>");
Response.Write("tb1_btn1:" + tb1.Text + "</br>");
lb1.Text = "2";
tb1.Text = "2";
Response.Write("lb1_btn1_valuechange:" + lb1.Text + "</br>");
Response.Write("tb1_btn1_valuechange:" + tb1.Text + "</br>");
}
protected void Btn2_Click(object sender, EventArgs e)
{
Response.Write("lb1_btn2:" + lb1.Text + "</br>");
Response.Write("tb1_btn2:" + tb1.Text + "</br>");
}
}</span>
结果如下:
注释: *每一行五个小单元格代表五次Response.Write的值;“无” 代表不存在 ;ViewState False代表将其控件的EnableViewState属性设置为False; 而Text(_ViewState)代表 页面viewstate中Text脏数据的值;
通过.net reflector 反编译查看Label Text如下:
[Bindable(true), Localizable(true), WebCategory("Appearance"), DefaultValue(""), WebSysDescription("Label_Text"), PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public virtual string Text
{
get
{
object obj2 = this.ViewState["Text"];
if (obj2 != null)
{
return (string) obj2;
}
return string.Empty;
}
set
{
if (this.HasControls())
{
this.Controls.Clear();
}
this.ViewState["Text"] = value;
}
}
结果分析:
页面的_ViewState的内容格式为Base64,我们如果要查看可以使用 ViewStateDecoder来查看,在查看的过程中我们看到了pair,具体详细介绍可以看上面的第一篇文章http://www.cnblogs.com/wwan/archive/2010/11/18/1880357.html (介绍ViewState原理)
先看Label 默认,也就是EnableViewState属性为True时
- 页面首次加载时,由于静态赋值text=0时,TraceViewState()并没有开始执行,赋值操作正常赋值,而在Page_Load事件执行时间在TraceViewState()之后,所以对Lable控件的赋值操作lb1.Text=“1” 会被ViewState的原型StateBag的捕获,并标记为dirty数据,通过序列化等一系列操作后,将Text值保存在页面的_ViewState隐藏字段中,所以值为1,此时页面的_ViewState保存着lb1现在的状态;
- 单击Btn1后,页面执行Page_Load和Btn1事件,Page_Load事件执行第一个Response.Write()时,由于页面的_ViewState保存着lb1现在的状态,通过将Base64编码的页面_ViewState内容进行反序列化等一系列操作,于是第一个Response值为1,后面对Lable控件的赋值操作lb1.Text=“1”,而btn1事件后,Lable控件的值最终会被赋值为2,如同前面一样,被ViewState的原型StateBag的捕获,并标记为dirty数据,所以最终页面的_ViewState保存着lb1的状态值为2;(注:无论原来的值与现在所赋的值是否相同,在TraceViewState()的追踪下,只要有赋值操作,就会将其标记为脏数据)
- 单击Btn2,再次单击Btn2分析方法如2一致;最终得如上图所示结论。
当Label 的EnableViewState属性为False时
- 页面首次加载时,由于静态赋值text=0时,TraceViewState()并没有开始执行,赋值操作正常赋值,由于禁用了ViewState,Lable控件的赋值操作lb1.Text=“1” 时不会出现脏数据,页面的_ViewState隐藏字段中不会保存label控件的状态,也就是Text值不存在(_ViewState隐藏字段不仅仅包含标记为“dirty”数据,其实还包含一些其他的数据)
- 单击Btn1后,页面执行Page_Load和Btn1事件,由于ViewState被禁用,没有保存相应的状态,所以导致lb1在没有赋值的情况下值依旧为0;当有了赋值操作后,则会选择获取赋值,后面的任何操作都不会保存状态了,因为禁用了ViewState,所以在不赋值的情况下,page_load()事件里面的response.Write的值都为0;控件的赋值操作正常;_ViewState隐藏字段永远不会有脏数据。
- 单击Btn2、再次单击Btn2分析方法如2一致;最终得如上图所示结论。
接着我们看下TextBox如何,通过.net reflector 反编译查看,并没有与Lable相似的Text的出现,通过运行上面的例子会发现,无论如何设置EnableViewState,最终的结果都是一样的,不会因为ViewState的禁用而影响TextBox控件状态的保存。我们可以得出结论,TextBox的状态保存不是通过ViewState来进行的,实际上,他是一个特例,特例不止TextBox一个。
在微软的说明文档中有这样的解释:
即使在该控件的视图状态 (应特性) 设置为False时,下面的服务器控件各请求之间保持它们的信息:
- 文本框控件。
- 复选框控件。
- 单选按钮控件。
TextBox部分需要注意的是:
禁用ViewState后,虽然并不会对TextBox的text赋值操作产生影响,但是会影响到TextBox控件的其它(比如设置它的width等等),这样就无法保存设置的相应的值的状态,需要注意。
通过上面的案例解析应该可以对ViewState有一个基本的了解。如有错的地方,帮忙纠正。
延伸其他相关内容文章 :
http://www.webjx.com/htmldata/2006-06-08/1149748147.html(ViewState和ControlState辨析)