首先,属性是各种.net语言的基本语法。而我们常说的控件属性是指控件类中用public修饰的属性。
见Lable的Text属性:
monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white">[Bindable(true), DefaultValue(""), Localizable(true), PersistenceMode(PersistenceMode.InnerDefaultProperty), WebCategory("Appearance"), WebSysDescription("Label_Text")]
public virtual string Text
{
get
{
object obj = this.ViewState["Text"];
if (obj != null)
{
return (string)obj;
}
return string.Empty;
}
set
{
if (this.HasControls())
{
this.Controls.Clear();
}
this.ViewState["Text"] = value;
}
}
Lable的Text属性都干了些什么呢?为什么不直接封装一个string字段呢?
在回答这些问题前,我们先看看这样一个实验。
话说:Lable有一个儿子,他是这样的:
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls;
namespace CustomServerControl
{
public class MemorylessLable : Label
{
public override string Text
{
get;
set;
}
}
}
有一天Lable和儿子排好队,站出相同的姿势:
<div>
<csc:MemorylessLable ID="MemorylessLable1" Text="MemorylessLable" runat="server">
</csc:MemorylessLable>
<br />
<asp:Button ID="btnMemorylessLable" runat="server" Text="MemorylessLableAdd(?)" OnClick="btnMemorylessLable_Click" />
</div>
<div>
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
<br />
<asp:Button ID="btnLabel" runat="server" Text="LabelAdd(?)" OnClick="btnLabel_Click" />
</div>
他们做着同样的事情:
protected void btnMemorylessLable_Click(object sender, EventArgs e)
{
MemorylessLable1.Text += "?";
}
protected void btnLabel_Click(object sender, EventArgs e)
{
Label1.Text += "?";
}
但是......
但是,是你看到的结果。但是,这又是为什么呢?
我们回顾一下HTTP协议的工作模式:HTTP是一种无状态的断开式连接模式,也就是说,客户端向服务端发送请求,服务端做出响应后就不再维持此次请求客户端的信息。在默认情况下,多次请求来自同一个客户端还是多个不同的客户端,对于服务端来说处理方式没有什么不同。
现在,我们已经弄清了MemorylessLable 中Text属性没有记忆的原因。回到问题的开始,Lable的Text属性又干了些什么呢?是什么让他看上去有了记忆?
我们看生成的HTML中有一个name为“__VIEWSTATE”的隐藏表单域:
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTEzNjg4MTc2OQ9kFgICAw9kFgICBQ8PFgIeBFRleHQFCkxhYmVsPz8/Pz9kZGTR/AR1D6vaE/KV3PxzwkBnTnXssA==" />
为了解决保存控件状态的问题,ASP.NET引入了一种叫视图状态(ViewState)的特性。需要在页面回传过程中保存值的控件属性,可以把值保存在视图状态中,ASP.NET框架会在呈现页面前把视图信息序列化成一个字符串,并保存到页面中的一个叫_VIEWSTATE”的隐藏表单域(<input type='hidden'/>)中。这样,控件的状态就保存到了客户端,隐藏表单域中的值在下次页面回传时,由表单(Form)提交回服务器,服务器再对提交回的_VIEWSTATE隐藏域的值进行反序列化,还原各个控件的状态。
但,有时用户是要禁用视图状态的。对,视图状态可以禁用。禁用方法是在页面前台文件顶部的<%@Page%>指令中添加EnableViewState="false"。什么时候禁用呢?试想一下,比如,一个页面中有一个产生数KB大小视图状态的GridView(GridView会产生大量的视图状态内容),而且N多次表单的提交。那么这数KB的视图状态就要在在客户端和服务端见往返的传递,它会拖慢网页的响应速度。好,我们把EnableViewState="false"添加到我们的Demo中,看一看。
但,有些信息对于控件的正确运行是至关重要的。比如,我们刚才所说的那个GridView中的PageIndex属性。如果该属性序列化到视图状态中,在页面禁用视图状态时,GridView的分页功能将彻底崩溃。但实际却不是如此,GirdView是怎么做到的呢?如,标题:控件状态(ControlState)。
ASP.NET2.0后为控件开发人员提供了一种比视图状态更安全的保存控件状态值的机制。控件状态类似于视图状态,但它在用户设置EnableViewState="false"时任然有效。
Lable的孙子:
using System;
using System.Collections.Generic;
using System.Text;
namespace CustomServerControl
{
public class SuperMemoryLable:MemorylessLable
{
protected override void OnInit(EventArgs e)
{
//通知控件运行时所在页面,将该控件注册为具有持久性控件状态的控件
Page.RegisterRequiresControlState(this);
base.OnInit(e);
}
//返回要保存到控件状态中的值
protected override object SaveControlState()
{
return this.Text;
}
//将从控件状态中反序列化出来的对象解析成控件的各个状态值
protected override void LoadControlState(object savedState)
{
this.Text = savedState as string;
}
}
}Lable和孙子玩起了他和儿子当年的游戏:
<div>
<csc:SuperMemoryLable ID="SuperMemoryLable1" runat="server" Text="SuperMemoryLable">
</csc:SuperMemoryLable>
<br />
<asp:Button ID="btnSuperMemoryLable" runat="server" Text="SuperMemoryLableAdd(?)"
OnClick="btnSuperMemoryLable_Click" />
</div>
<div>
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
<br />
<asp:Button ID="btnLabel" runat="server" Text="LabelAdd(?)" OnClick="btnLabel_Click" />
</div>
游戏升级:添加EnableViewState="false"后的一瞥。
1.控件属性是指控件类中用public修饰的属性。
2.实现属性持久化的方式有两种:
①将控件属性序列化到视图状态;
②将控件属性序列化到控件状态。
页面开发时,可以通过EnableViewState="false"禁用视图状态。
3.应用控件状态的三个步骤:
①重写OnInit()方法,在调用base.OnInit()前,调用Page.RegisterRequiresControlState()方法通知控件运行时所在的页面,需要为它提供控件状态功能;
②重写SaveControlState()方法,提供需要保存到控件状态中的值;
③重写LoadControlState()方法,将从控件状态中反序列化出来的对象解析成控件的各个状态值。
步骤一:
protected internal override void OnInit(EventArgs e)
{
base.OnInit(e);
if (this.Page != null)
{
if (this.DataKeyNames.Length > 0 && !this.AutoGenerateColumns)
{
this.Page.RegisterRequiresViewStateEncryption();
}
//通知控件运行时所在的页面,需要为它提供控件状态功能
this.Page.RegisterRequiresControlState(this);
}
}
步骤二:
protected internal override object SaveControlState()
{
object obj = base.SaveControlState();
if (obj != null || this._pageIndex != 0 || this._editIndex != -1 || this._selectedIndex != -1 ||
(this._sortExpression != null && this._sortExpression.Length != 0) || (this._sortDirection != SortDirection.Ascending ||
(this._dataKeyNames != null && this._dataKeyNames.Length != 0)) ||
(this._dataKeysArrayList != null && this._dataKeysArrayList.Count > 0) || this._pageCount != -1)
{
return new object[]
{
obj,
(this._editIndex == -1) ? null : this._editIndex,
(this._pageIndex == 0) ? null : this._pageIndex, //将PageIndex属性的值和其他需要保存到控件状态中的值组成一个复杂对象(object[])
(this._selectedIndex == -1) ? null : this._selectedIndex,
(this._sortExpression == null || this._sortExpression.Length == 0) ? null : this._sortExpression,
(this._sortDirection == SortDirection.Ascending) ? null : ((int)this._sortDirection),
(this._dataKeyNames == null || this._dataKeyNames.Length == 0) ? null : this._dataKeyNames,
this.SaveDataKeysState(),
this._pageCount,
(this._persistedDataKey == null) ? null : ((IStateManager)this._persistedDataKey).SaveViewState(),
(this._clientIDRowSuffix == null || this._clientIDRowSuffix.Length == 0) ? null : this._clientIDRowSuffix,
this.SaveClientIDRowSuffixDataKeysState()
};
}
return true;
}步骤三:
protected internal override void LoadControlState(object savedState)
{
this._editIndex = -1;
this._pageIndex = 0;
this._selectedIndex = -1;
this._sortExpression = string.Empty;
this._sortDirection = SortDirection.Ascending;
this._dataKeyNames = new string[0];
this._pageCount = -1;
object[] array = savedState as object[];
if (array != null)
{
base.LoadControlState(array[0]);
if (array[1] != null)
{
this._editIndex = (int)array[1];
}
if (array[2] != null)
{
this._pageIndex = (int)array[2]; //将从控件状态中反序列化出来的对象中解析PageIndex属性的状态值
}
if (array[3] != null)
{
this._selectedIndex = (int)array[3];
}
if (array[4] != null)
{
this._sortExpression = (string)array[4];
}
if (array[5] != null)
{
this._sortDirection = (SortDirection)array[5];
}
if (array[6] != null)
{
this._dataKeyNames = (string[])array[6];
}
if (array[7] != null)
{
this.LoadDataKeysState(array[7]);
}
if (array[8] != null)
{
this._pageCount = (int)array[8];
}
if (array[9] != null && this._dataKeyNames != null && this._dataKeyNames.Length > 0)
{
this._persistedDataKey = new DataKey(new OrderedDictionary(this._dataKeyNames.Length), this._dataKeyNames);
((IStateManager)this._persistedDataKey).LoadViewState(array[9]);
}
if (array[10] != null)
{
this._clientIDRowSuffix = (string[])array[10];
}
if (array[11] != null)
{
this.LoadClientIDRowSuffixDataKeysState(array[11]);
return;
}
}
else
{
base.LoadControlState(null);
}
}