asp.net控件开发基础(11) --------自定义视图状态管理

  上一篇讨论了集合属性的使用,这一篇我们主要来讨论视图状态的自定义管理.

         刚开篇的时后在最后把属性值用视图状态来保存时,得以把当前状态保存下来,关于视图状态的概述,这里不再累赘,没了解过的朋友可以在MSDN里输入 视图状态概述了解一下.以下我们还是以以前讲过的内容为例,一起继续来改善控件的使用(第五篇和第九篇的例子)


示例一

我们启用了跟踪,按下确定按钮后,控件属性发生变化,按下无事件按钮后,控件状态则恢复到之前的状态,而且在跟踪状态下发现 Custom无视图状态.

<% @ Page Language="C#" Trace="true"  %>
<% @ Register Assembly="CustomComponents" Namespace="CustomComponents" TagPrefix="custom"  %>
<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
< script  runat ="server" >
    protected 
void Button1_Click(object sender, EventArgs e)
    
{
        Custom1.Age 
= 21;
        Custom1.CustomMetier 
= Metier.教师;
        Custom1.CustomAddress.City 
= "杭州";
        Custom1.CustomAddress.State 
= "中国";
        Custom1.CustomAddress.Street 
= "街道";
        Custom1.CustomAddress.Zip 
= "310000";
    }

</ script >
< html  xmlns ="http://www.w3.org/1999/xhtml"   >
< head  runat ="server" >
    
< title > 无标题页 </ title >
</ head >
< body >
    
< form  id ="form1"  runat ="server" >
    
< div >
        
< custom:Custom  ID ="Custom1"  runat ="server" >
        
</ custom:Custom >
        
< br  />
        
< br  />
        
< asp:Button  ID ="Button1"  runat ="server"  OnClick ="Button1_Click"  Text ="确定"   />
        
&nbsp;   &nbsp;&nbsp;
        
< asp:Button  ID ="Button2"  runat ="server"  Text ="无事件"   /> &nbsp;
    
</ div >
    
</ form >
</ body >
</ html >

那么接下来将修改Custom的属性更改为视图状态保存,代码如下

重新编译一下代码,再次测试上面代码Custom的Age和CustomMetier属性可以保存其状态,而无法保存CustomAddress这个复杂属性的状态值.这个也可以理解,我们没有为CustomAddress的子属性值保存在视图状态里.启动跟踪后,还发现Custom控件在更改控件属性后保存了一部分的视图状态.


#region 属性

        [Description(
"年龄")]
        
public int Age
        
{
            
get return ViewState["Age"!= null ? (int)ViewState["Age"] : 0; }
            
set { ViewState["Age"= value; }
        }

        
        [Description(
"姓名")]
        
public String Name
        
{
            
get return ViewState["Name"!= null ? (string)ViewState["Name"] : string.Empty; }
            
set { ViewState["Name"= value; }
        }

        [TypeConverter(
typeof(GameConverter))]
        [Description(
"喜欢的游戏")]
        
public String Game
        
{
            
get return ViewState["Game"!= null ? (string)ViewState["Game"] : string.Empty; }
            
set { ViewState["Game"= value; }
        }


        [Description(
"职业")]
        
public Metier CustomMetier
        
{
            
get return ViewState["CustomMetier"!= null ? (Metier)ViewState["CustomMetier"] : Metier.程序员; }
            
set { ViewState["CustomMetier"= value; }
        }

        

        
#endregion


接下来我们更改Address的字属性,把其值保存在视图状态下.

代码如下:


#region 属性
        [
       Category(
"Behavior"),
       DefaultValue(
""),
       Description(
"街道"),
       NotifyParentProperty(
true),
       ]
        
public String Street
        
{
            
get return ViewState["Street"!= null ? (string)ViewState["Street"] : String.Empty; }
            
set { ViewState["Street"= value; }
        }


       [
       Category(
"Behavior"),
       DefaultValue(
""),
       Description(
"城市"),
       NotifyParentProperty(
true),
       ]
        
public String City
        
{
            
get return ViewState["City"!= null ? (string)ViewState["City"] : String.Empty; }
            
set { ViewState["City"= value; }
        }


       [
       Category(
"Behavior"),
       DefaultValue(
""),
       Description(
"国籍"),
       NotifyParentProperty(
true),
       ]

        
public String State
        
{
            
get return ViewState["State"!= null ? (string)ViewState["State"] : String.Empty; }
            
set { ViewState["State"= value; }
        }


       [
       Category(
"Behavior"),
       DefaultValue(
""),
       Description(
"邮编"),
       NotifyParentProperty(
true)
       ]
        
public String Zip
        
{
            
get return ViewState["Zip"!= null ? (string)ViewState["Zip"] : String.Empty; }
            
set { ViewState["Zip"= value; }
        }


        
#endregion


重新编译后,发现问题了,编译不通过,当前上下文不存在名称ViewState.如果这些属性直接定义在Custom控件下则一点问题也没有,但下面定义的是Address复杂属性的子属性.而Address属性又不能继承Control类,所以我们需要 自定义一个ViewState属性

如下代码:

private   bool  _isTrackingViewState;
private  StateBag _viewState;

protected  StateBag ViewState
        
{
            
get
            
{
                
if (_viewState == null)
                
{
                    _viewState 
= new StateBag(false);
                    
if (_isTrackingViewState) ((IStateManager)_viewState).TrackViewState();
                }

                
return _viewState;
            }

        }


先定义两个变量,然后定义一个ViewState属性,ViewState类型本身便是一个StateBag类型.大家一定注意到了 IStateManager接口,下面还有一个TrackViewState方法.先不管他.重新编译下,编译通过,重新测试下,发现还是没有变化.

MSDN上对ViewState能保存的值已经讲的很清楚了.你可以保存一些简单类型,但无法保存自定义类型,而我们定义的Address就是一个自定义类型. 为保存自定义类型数据,所以我们需要自定义类型状态管理

自定义类型状态管理,那么我们就必须接触到 IStateManager这个接口,此接口有一个属性和三个方法,如下




所以Address要继承 IStateManager接口,并显示实现接口属性和方法,注意是显示实现 .

下面看Address类具体的自定义状态管理代码

#region 自定义状态管理

        
bool IStateManager.IsTrackingViewState
        
{
            
get
            
{
                
return _isTrackingViewState;
            }

        }


        
void IStateManager.LoadViewState(object savedState)
        
{
            
if (savedState != null)
            
{
                ((IStateManager)ViewState).LoadViewState(savedState);
            }

        }


        
object IStateManager.SaveViewState()
        
{
            
object savedState = null;
            
if (_viewState != null)
            
{
                savedState 
=
                   ((IStateManager)_viewState).SaveViewState();
            }

            
return savedState;
        }


        
void IStateManager.TrackViewState()
        
{
            _isTrackingViewState 
= true;
            
            
if (_viewState != null)
            
{
                ((IStateManager)_viewState).TrackViewState();
            }

        }


        
#endregion

理解控件自定义的状态管理,你有必要了解 控件的生命周期,了解控件生命周期,那问题就迎刃而解了.
大家可以翻阅MSND的 控件执行生命周期

我个人认为最好的理解方法就是为上面代码 设置三个断点, 如下图


好了,下面把我们测试的那个aspx页面设置为起始页,然后按F5,开始测试.

本该启动后跳到TrackViewState方法里,但没跳进来,好怪,而且自定义类型状态管理后页面并未保存其值.
让我们回到Custom类里,我们还需要为属性(复杂属性)定义状态管理.
本身Control也有一套默认的状态管理机制,而没有实现 IStateManager接口  ,

其实现如下:
对下面代码我认为是错误的,因为书上全是这么写的,我认为因先把_viewState显示转换为 IStateManager类型,
因为StateBag本身是继承 IStateManager接口,但MSDN中,我并没看到其实现IStateManager的方法,而是显示的实现,当我用反射机制查看其方法时,却又发现是有其方法的,但当你不把StateBag显示转换为 IStateManager类型,而直接调用下面方法时,将会出错.如果书上是对的,还请看到此文的人指点一下,对此我已经疑惑很长时间了. 如果我是对的,那下面的_viewState因先显示转换为 IStateManager类型,事实上我们都是这么做的.


 1private StateBag _viewState;
 2protected virtual StateBag ViewState{
 3 get {
 4  if(_viewState != null
 5  {
 6   return _viewState;
 7  }

 8  _viewState = new StateBag(ViewStateIgnoresCase);
 9  if(IsTrackingViewState) 
10   _viewState.TrackViewState();
11   return _viewState;
12 }

13}

14
15protected virtual void TrackViewState()
16 if(_viewState != null
17  _viewState.TrackViewState();
18 }

19 return null;
20}

21
22protected virtual object SaveViewState(){
23 if(_viewState != null{
24  _viewState.SaveViewState(); 
25 }

26 return null;
27}

28protected virtual void LoadViewState(object savedState){
29 if(savedState != null
30  ViewState.LoadViewState(savedState); 
31 }

32}


下面再看如何在Custom类中自定义属性状态管理,当你定义了复杂类型时,你就需要重写上面的几个方法.
具体代码如下:
首先我们对属性进行视图状态的跟踪,然后重写了Control类的三个方法.其一方面调用了基类方法,一方面调用了Addres类的显示接口方法.

Pair类为一个辅助类,用作存储两个相关对象的基本结构,下面根据调试结果来理解.在Custom类中对其三个方法设置断点.


public Address CustomAddress
        
{
            
get
            
{
                
if (address == null)
                
{
                    address 
= new Address();
                    
if (IsTrackingViewState)
                    
{
                        ((IStateManager)address).TrackViewState();
                    }

                }

                
return address;
            }

        }


#region 自定义视图状态
        
protected override void LoadViewState(object savedState)
        
{
            Pair p 
= savedState as Pair;
            
if (p != null)
            
{
                
base.LoadViewState(p.First);
                ((IStateManager)CustomAddress).LoadViewState(p.Second);
                
return;
            }

            
base.LoadViewState(savedState);
        }


        
protected override object SaveViewState()
        
{
            
object baseState = base.SaveViewState();
            
object thisState = null;

            
if (address != null)
            
{
                thisState 
= ((IStateManager)address).SaveViewState();
            }


            
if (thisState != null)
            
{
                
return new Pair(baseState, thisState);
            }

            
else
            
{
                
return baseState;
            }


        }


        
protected override void TrackViewState()
        
{
            
if (address != null)
            
{
                ((IStateManager)address).TrackViewState();
            }

            
base.TrackViewState();
        }

        
#endregion


设置断点以后,启动起始页开始测试.

启动后第一步将会跳到Custom类的TrackViewState方法里面,执行完此方法后IsTrackingViewState将设置为true,
从而可以继续调用address的TrackViewState方法,另外可以看到address属性为空值,然后按F5,通过此方法继续

第二步将会跳到Custom类的SaveViewState方法里,发现baseState和thisState均为空,直接执行基类方法.按F5继续

第三步将会跳到Address类的TrackViewState方法里,_isTrackingViewState初始化时为false,执行此方法后将赋值为ture,然后调用_viewState的TrackViewState方法.

初始化的工作就完成了,然后我们点击确定按钮,重新执行.

重新跳到Custom类的TrackViewState方法里,步骤跟上面第一步一样,按F5,继续

跳到Address类的TrackViewState方法里,步骤跟上面第二步一样,按F5继续

跳到Custom类的SaveViewState方法里,此时address不再为null,此时会返回Pair构造函数.

然后会跳到Address类SaveViewState方法里,接着会跳回来,再执行Custom类的SaveViewState方法

以上调试方法不一定正确,但多调用会理解的更深刻.

我们还发现并未跳到LoadViewState方法里,以前的主要工作就是保存视图状态更改,接下来再次调试的话,就会跳到LoadViewState方法方法里面,这时你会发现savedState就是SaveViewState方法中保存下来的视图状态,可以看到其first和second值分别为Custom的页面属性和Address这个复杂属性的值.

视图状态以键/值的方式保存,有一个属性为Dirty,表示 StateItem是否被修改过,可以通过SetDirty方法和SetItemDirty方法给StateItem添加Dirty标记.
         internal   void  SetDirty()
        
{
            _viewState.SetDirty(
true);
        }

如果此StateItem标记为Dirty的话,则在SaveViewState方法中以键/值的方式保存到ArrayList中.

SaveViewState方法和LoadViewState方法执行的是相反的操作.我们在页面上看到的值,总是LoadViewState方法反序列化视图状态.大家可以具体去了解StateBag类默认情况下SaveViewState方法和LoadViewState方法的实现过程.

当控件禁用视图状态时将不再执行SaveViewState和LoadViewState,可以去调试一下就知道了.

还需要注意的是,我们了解视图状态可以保存的类型,其也是同过类型转换器来转换此类型,否则的话将以二进制串行化功能来串行化数值得,这样降低了效率,所以我们还需要为其定义一个类型转换器,第九篇的时候已经讲过怎么定义了,这里就不列代码了,只是需要注意就是.

此外asp.net2.0中加入了 控件状态,因为视图状态要么全开,要么全禁用,控件状态则是为弥补这一点,大家可以看MSDN,也可参考相关文章.

asp.net2.0中还可以 对视图状态进行分块处理,你需要在web.config里如下设置

< system .web >
    
< pages  maxPageStateFieldLength ="1000"   >
< system .web >

asp.net2.0还加入了视图状态持久性机制,大家可以在博客园参考相关文章,这里就当了解下有这种机制存在.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值