1 自定义控件之状态管理

1 状态管理概述
Control.ViewState :状态信息的字典。ViewState为StateBag的实例。

image     

管理 ASP.NET 服务器控件的视图状态。

image    

为支持服务器控件的视图状态管理而必须实现的属性和方法。

名称 说明
LoadViewState 当由类实现时,加载服务器控件以前保存的控件视图状态。
SaveViewState 当由类实现时,将服务器控件的视图状态更改保存到 Object。
TrackViewState 当由类实现时,指示服务器控件跟踪其视图状态更改。
IsTrackingViewState 指示服务器控件是否正在跟踪其视图状态更改。

 

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后控件的状态仍然保留。效果如下图所示。

   image

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 时生成工作正常

   image

4.2.2 在post 时丢失状态

image

在页面启用 Trace='true' ,时能看到 viewstate 的值,ViewState值肯定不是完全的。

BarGraphControlWithViewStateProperty post时的__ViewState

image

BarGraphControl post 时的回传数据

image

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 事件执行顺序

image

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中导致排版比较恶心,在此向大家致以歉意。文档中有不正确的地方欢迎大家指正。)

原文地址:https://www.cnblogs.com/hbb0b0/p/1880531.html