前段时间看了一篇金旭亮老师的文章(http://blog.csdn.net/bitfan/archive/2008/06/02/2504743.aspx),收获颇丰,但也有一个疑问:
viewstate保存到页面时与代码顺序有关。可以在页面上丢一个服务端button,试验以下代码:
protected void Page_Load(object sender, EventArgs e)
{
Panel p = new Panel();
// form1.Controls.Add(p);//(1)
if (!IsPostBack)
{
p.Style.Add("width", "100px");
p.Style.Add("height", "100px");
p.Style.Add("border", "solid 1px red");
}
form1.Controls.Add(p);//(2)
}
点击button后,add放在(1)处可使 p 不变,放在(2)处 p 不见了。原因是放在(1)处 p 的style被加到页面的viewstate隐藏域,在提交后loadviewstate生命周期还原给p。
而放在(2)处没有被加到隐藏域。(可以用看viewstate的工具察看)。
那为什么与代码顺序有关呢?这两天用reflector总算看出点道道,写下来免得忘了。
(1)style其实就是:CssStyleCollection
public sealed class CssStyleCollection
{ ... private StateBag _state; // 关键b ...
public void Add(string key, string value); // 关键a ...
}
(2)关键a代码:
public void Add(string key, string value)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException("key");
}
if (this._table == null)
{
this.ParseString();
}
this._table[key] = value;
if (this._intTable != null)
{
HtmlTextWriterStyle styleKey = CssTextWriter.GetStyleKey(key);
if (styleKey != ~HtmlTextWriterStyle.BackgroundColor)
{
this._intTable.Remove(styleKey);
}
}
if (this._state != null)
{ this._state["style"] = this.BuildString(); // 关键c
} this._style = null; }
关键c告诉我们对style调用add就会往this._state["style"]里增加,原因是:
(3)statebag代码:
public sealed class StateBag : IStateManager, IDictionary, ICollection, IEnumerable { ...
public StateItem Add(string key, object value); // 关键e ...
public object this[string key] { get; set; } // 关键d ...
}
关键d代码:
public object this[string key]
{
get
{
if (string.IsNullOrEmpty(key))
{
throw ExceptionUtil.ParameterNullOrEmpty("key");
}
StateItem item = this.bag[key] as StateItem;
if (item != null) {
return item.Value; }
return null; }
set {
this.Add(key, value); // 关键e
} } 关键e代码:
public StateItem Add(string key, object value) { if (string.IsNullOrEmpty(key)) { throw ExceptionUtil.ParameterNullOrEmpty("key"); }
StateItem item = this.bag[key] as StateItem;
if (item == null) {
if ((value != null) || this.marked) {
item = new StateItem(value); // 关键f
this.bag.Add(key, item); } }
else if ((value == null) && !this.marked) {
this.bag.Remove(key); } else {
item.Value = value; }
if ((item != null) && this.marked) //关键g {
item.IsDirty = true;
}
return item; }
(4)在关键f处 new 一个stateitem时默认isdirty是false,证据:
internal StateItem(object initialValue) {
this.value = initialValue;
this.isDirty = false; }
关键g处:this.marked 在没有调用过trackviewstate时是一定是false,证据:statebag的构造函数:
public StateBag(bool ignoreCase) {
this.marked = false;
this.ignoreCase = ignoreCase;
this.bag = this.CreateBag();
}
所以如果在style.add之前没有使关键b处style实例的_state的marked变成true, 那么加进_state的stateitem的isdirty一定是false,干净的东西就不会加到页面隐藏域了。 而controls.add会默认调用trackviewstate,使在它调用之后style.add增加的东西变脏。 这就是全部的原因。但这里遗留一个问题:_state是如何在saveviewstate中被操作的。
有时间我会继续reflector。