1 自定义控件之状态管理
1 状态管理概述 | |||||||||||||||
Control.ViewState :状态信息的字典。ViewState为StateBag的实例。 | |||||||||||||||
管理 ASP.NET 服务器控件的视图状态。 |
为支持服务器控件的视图状态管理而必须实现的属性和方法。
| ||||||||||
2 如何在自定义控件中实现自定义状态管理? | |||
重载:LoadViewState与SaveViewState |
3.1 代码
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.UI;
namespace AspnetEssential.CustomerControl
{
/// <summary>
/// BarGraphControl
/// </summary>
/// <remarks>
/// 简单的图表控件。根据描述,值绘制图表
/// </remarks>
public class BarGraphControl:Control
{
/// <summary>
/// 描述列表
/// </summary>
private List<string> _description;
/// <summary>
/// 值列表
/// </summary>
private List<int> _values;
/// <summary>
/// 最大值(用于计算相BarItem的相对长度,本程序为实现此功能)
/// </summary>
private int _max;
/// <summary>
/// 构造函数中初始化控件的默认值
/// </summary>
public BarGraphControl()
{
_description = new List<string>();
_values = new List<int>();
}
/// <summary>
/// 添加键值对
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
public void AddItem(string name,int value)
{
_description.Add(name);
_values.Add(value);
if(value>_max)
{
_max = value;
}
}
#region State Manager
protected override object SaveViewState()
{
object[] vState = new object[4];
vState[0] = base.SaveViewState();
vState[1] = _description;
vState[2] = _values;
vState[3] = _max;
return vState;
}
protected override void LoadViewState(object savedState)
{
//base.LoadViewState(savedState);
if(savedState !=null)
{
object[] vState =(object[])savedState;
if(vState[0]!=null)
{
base.LoadViewState(vState[0]);
}
if (vState[1] != null)
{
_description=(List<string>)vState[1];
}
if (vState[2] != null)
{
_values=(List<int>)vState[2];
}
if (vState[3] != null)
{
_max=(int)vState[3];
}
}
}
#endregion
#region Render
/// <summary>
/// 呈现
/// </summary>
/// <param name="writer"></param>
protected override void Render(HtmlTextWriter writer)
{
//base.Render(writer);
writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);
writer.AddAttribute(HtmlTextWriterAttribute.Bordercolor, "red");
//单元格内容与单元格边框的距离
writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "0");
//单元格之间的距离
writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0");
writer.RenderBeginTag(HtmlTextWriterTag.Table);
//bar的颜色
string color = string.Empty;
//绘制tr,td
for(int i=0;i<_values.Count;i++ )
{
//tr
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
//td
writer.RenderBeginTag(HtmlTextWriterTag.Td);
writer.WriteEncodedText(_description[i]);
writer.RenderEndTag();
writer.RenderBeginTag(HtmlTextWriterTag.Td);
//writer.AddAttribute(HtmlTextWriterAttribute.Width, _values[i].ToString());
writer.AddStyleAttribute(HtmlTextWriterStyle.BorderColor, "blue");
writer.AddStyleAttribute(HtmlTextWriterStyle.BorderStyle, "solid");
writer.AddStyleAttribute(HtmlTextWriterStyle.BorderWidth, "1px");
color=((i % 2) == 0 ? "red" : "blue");
writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor, color);
writer.AddStyleAttribute(HtmlTextWriterStyle.Width, _values[i].ToString()+"px");
writer.RenderBeginTag(HtmlTextWriterTag.Div);
writer.RenderEndTag();
writer.RenderEndTag();
writer.RenderEndTag();
}
writer.RenderEndTag();
}
#endregion
}
}
3.2 控件使用实例
private const int MAXCOUNT = 10;
protected void Page_Load(object sender, EventArgs e)
{
if(!this.IsPostBack)
{
AddBarGraphValue(this.BarGraphControl1);
}
}
/// <summary>
/// 生成控件的随机数据
/// </summary>
/// <param name="barControl"></param>
private void AddBarGraphValue(BarGraphControl barControl)
{
Random rd = null;
for (int i = 0; i < MAXCOUNT;i++ )
{
rd = new Random(i);
int currentValue= rd.Next(100);
string title = string.Format("Item:{0}",i.ToString());
barControl.AddItem(title, currentValue);
}
}
3.3 运行说明
页面执行post后控件的状态仍然保留。效果如下图所示。
4 问题:不使用重载LoadViewState与SaveViewState方法,直接把_description等字段发布为存放在viewstate 的属性也应该能达到以上效果,那么什么时候使用重载方法控件的状态管理?
4.1 实验代码
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.UI;
namespace AspnetEssential.CustomerControl
{
/// <summary>
/// BarGraphControl
/// </summary>
/// <remarks>
/// 简单的图表控件。根据描述,值绘制图表
/// </remarks>
public class BarGraphControlWithViewStateProperty:Control
{
/// <summary>
/// 描述列表
/// </summary>
private List<string> Description
{
get
{
return ViewState["_description"] as List<string>;
}
set
{
ViewState["_description"] = value;
}
}
/// <summary>
/// 值列表
/// </summary>
//private List<int> _values;
private List<int> Values
{
get
{
return ViewState["_values"] as List<int>;
}
set
{
ViewState["_values"] = value;
}
}
/// <summary>
/// 最大值(用于计算相BarItem的相对长度,本程序为实现此功能)
/// </summary>
//private int _max;
private int Max
{
get
{
return (int)ViewState["_max"];
}
set
{
ViewState["_max"] = value;
}
}
/// <summary>
/// 构造函数中初始化控件的默认值
/// </summary>
public BarGraphControlWithViewStateProperty()
{
ViewState["_description"] = new List<string>();
ViewState["_values"] = new List<int>();
ViewState["_max"] = 0;
}
/// <summary>
/// 添加键值对
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
public void AddItem(string name,int value)
{
Description.Add(name);
Values.Add(value);
if(value>Max)
{
Max = value;
}
}
#region Render
/// <summary>
/// 呈现
/// </summary>
/// <param name="writer"></param>
protected override void Render(HtmlTextWriter writer)
{
writer.Write(
string.Format("{0} Instance hashCode:{1}",
this.GetType().FullName,
this.GetHashCode()
)
);
//base.Render(writer);
writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);
writer.AddAttribute(HtmlTextWriterAttribute.Bordercolor, "red");
//单元格内容与单元格边框的距离
writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "0");
//单元格之间的距离
writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0");
writer.RenderBeginTag(HtmlTextWriterTag.Table);
//bar的颜色
string color = string.Empty;
//绘制tr,td
for(int i=0;i<Values.Count;i++ )
{
//tr
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
//td
writer.RenderBeginTag(HtmlTextWriterTag.Td);
writer.WriteEncodedText(Description[i]);
writer.RenderEndTag();
writer.RenderBeginTag(HtmlTextWriterTag.Td);
//writer.AddAttribute(HtmlTextWriterAttribute.Width, _values[i].ToString());
writer.AddStyleAttribute(HtmlTextWriterStyle.BorderColor, "blue");
writer.AddStyleAttribute(HtmlTextWriterStyle.BorderStyle, "solid");
writer.AddStyleAttribute(HtmlTextWriterStyle.BorderWidth, "1px");
color=((i % 2) == 0 ? "red" : "blue");
writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor, color);
writer.AddStyleAttribute(HtmlTextWriterStyle.Width, Values[i].ToString() + "px");
writer.RenderBeginTag(HtmlTextWriterTag.Div);
writer.RenderEndTag();
writer.RenderEndTag();
writer.RenderEndTag();
}
writer.RenderEndTag();
}
#endregion
}
}
4.2 BarGraphControlWithViewStateProperty 说明:
4.2.1 此控件在form get 时生成工作正常
4.2.2 在post 时丢失状态
在页面启用 Trace='true' ,时能看到 viewstate 的值,ViewState值肯定不是完全的。
BarGraphControlWithViewStateProperty post时的__ViewState
BarGraphControl post 时的回传数据
5 为什么BarGraphControlWithViewStateProperty 在post中,控件丢失状态???
5.1 比较 BarGraphControl与 BarGraphWithViewStateProperty 在get时生成的__ViewState
5.1.1BarGraphControl get时页面生成的__ViewState
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTQ5NzYxMjc0Nw9kFgICAw9kFgICAQ8UKwAEZDLGAgABAAAA/////
wEAAAAAAAAABAEAAAB/U3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWMuTGlzdGAxW1tTeXN0ZW0uU3RyaW5nLC
Btc2NvcmxpYiwgVmVyc2lvbj0yLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1Yz
U2MTkzNGUwODldXQMAAAAGX2l0ZW1zBV9zaXplCF92ZXJzaW9uBgAACAgJAgAAAAoAAAAKAAAAEQIAAAAQAAAABgMAA
AAGSXRlbTowBgQAAAAGSXRlbToxBgUAAAAGSXRlbToyBgYAAAAGSXRlbTozBgcAAAAGSXRlbTo0BggAAAAGSXRlbTo1BgkA
AAAGSXRlbTo2BgoAAAAGSXRlbTo3BgsAAAAGSXRlbTo4BgwAAAAGSXRlbTo5DQYLMo0CAAEAAAD/////AQAAAAAAAAAEA
QAAAH5TeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5MaXN0YDFbW1N5c3RlbS5JbnQzMiwgbXNjb3JsaWIsIFZlcnNpb249Mi4
wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0DAAAABl9pdGVtcwVf
c2l6ZQhfdmVyc2lvbgcAAAgICAkCAAAACgAAAAoAAAAPAgAAABAAAAAISAAAABgAAABNAAAAHQAAAFEAAAAhAAAAVgAAAC
YAAABaAAAAKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsCWmRkSxJZ9hCeFbsC8EmHusO606dT97U=" />
5.1.2 BarGraphWithViewStateProperty get时在页面生成的__ViewState
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWAgKTiu/aDAKM54rGBq/KxaTPszsZuaIb9spV87cnfmhD" />
5.2 BarGraphControl 事件执行顺序
5.3 经跟踪发现BarGraphControlWithViewStateProperty的值只有max有值,其他两个list都没有值。会不会是ViewState只能保存简单类型,不能保存复杂类型如list<int>?
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack )
{
SaveViewStateInfo();
}
ShowViewStateInfo();
}
private void SaveViewStateInfo()
{
this.ViewState["max"] = 100;
this.ViewState["list"] = new List<int>() { 1,2,3};
Dictionary<string, int> myDic =
new Dictionary<string, int>()
{
{"hb1",100},
};
this.ViewState["dic"] = myDic;
}
private void ShowViewStateInfo()
{
StringBuilder sb = new StringBuilder();
string info =string.Empty ;
info = string.Format("Max:{0}", (int)this.ViewState["max"]);
sb.Append(info);
List<int> mylist = this.ViewState["list"] as List<int>;
if(mylist!=null)
{
info = string.Format("List.Count:{0}", mylist.Count);
sb.Append(System.Environment.NewLine);
sb.Append(info);
}
Dictionary<string, int> myDic = this.ViewState["dic"] as Dictionary<string,int>;
if(myDic !=null)
{
info = string.Format("Dictionary<string, int> :{0}",myDic.Count);
sb.Append(System.Environment.NewLine);
sb.Append(info);
}
Response.Write(sb.ToString());
}
经以上测试。单击button后ViewState存储List没有问题。
5.4 StateBag TrackViewState 机制。(ViewState 是StateBag的实例)
Post 时页面保存ViewState ,最终是否把ViewState中的值持久化到__ViewState中,这由 StateBag.IsItemDirty("key")决定。如果 StateBag.IsItemDirty("key")是true,标志数据被修改过,这时Control类会调用持久化方法,会把key对应的数据经过base64编码后写入__viewstate字段中。如果 StateBag.IsItemDirty("key")为false,Control就不会吧key对应的数据添加到__viewstate中。
5.5 StateBag 简介
注意:
1) StateBag 只有在调用TrackViewState方法之后,修改它的键值,键对应的值才会被标记为Dirty。
2)StateBag 只跟踪直接替换数据标记为Dirty,不跟踪引用修改数据。
5.5. 1测试代码
StateBag myBag=new StateBag();
myBag["key"]=new List<int>();
((IStateManager)myBag).TrackViewState();
List<int> mylist=myBag["key"] as List<int>;
mylist.Add(1);
Console.WriteLine(myBag.IsItemDirty("key"));
输出:False
myBag["key"]=new List<int>(){1,2};
Console.WriteLine(myBag.IsItemDirty("key"));
输出:True
(由于上述文档以前写在excel中,这次复制到writer中导致排版比较恶心,在此向大家致以歉意。文档中有不正确的地方欢迎大家指正。)