博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一起谈.NET技术,asp.net控件开发基础(11)
阅读量:7071 次
发布时间:2019-06-28

本文共 8709 字,大约阅读时间需要 29 分钟。

  刚开篇的时后在最后把属性值用视图状态来保存时,得以把当前状态保存下来,关于视图状态的概述,这里不再累赘,没了解过的朋友可以在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类型,事实上我们都是这么做的。

 
 
private
StateBag _viewState;
protected
virtual
StateBag ViewState{
 
get
{
  
if
(_viewState
!=
null
)
  {
   
return
_viewState;
  }
  _viewState
=
new
StateBag(ViewStateIgnoresCase);
  
if
(IsTrackingViewState)
   _viewState.TrackViewState();
   
return
_viewState;
 }
}
protected
virtual
void
TrackViewState(){
 
if
(_viewState
!=
null
) {
  _viewState.TrackViewState();
 }
 
return
null
;
}
protected
virtual
object
SaveViewState(){
 
if
(_viewState
!=
null
) {
  _viewState.SaveViewState();
 }
 
return
null
;
}
protected
virtual
void
LoadViewState(
object
savedState){
 
if
(savedState
!=
null
) {
 ViewState.LoadViewState(savedState);
 }
}

 

  下面再看如何在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还加入了视图状态持久性机制,大家可以在博客园参考相关文章,这里就当了解下有这种机制存在。好了,就写到这里,个人认为视图状态是很重要的,下面很多东西都要涉及到它,所以要好好理解这个东西。写的比较乱,对视图状态我真的比较敏感,很难理解,也难表达,可能很多地方写错,还请指出,这样才能提高

你可能感兴趣的文章
MultiRow发现之旅(三)- 模板管理器和Table
查看>>
Exchange 2010与Exchange Online混合部署PART 4:混合部署
查看>>
panama项目中字符编码问题解决
查看>>
向C#的String类添加按字节截取字符串的扩展方法
查看>>
Python中元组、列表、字典的遍历和相互转化
查看>>
SmoothWall的小企业应用
查看>>
FOSCommentBundle功能包:设置Doctrine ORM映射(投票)
查看>>
Enterprise Library Step By Step系列(十五):配置应用程序块——设计篇
查看>>
Visio中插入公式
查看>>
对于软件生产能解决到痛点的容器技术就是好!Wise2C睿云智合如何运行
查看>>
SharePoint 2013 状态机工作流之扩展自定义状态
查看>>
物联网下的工控机产业发展迅速 潜力正不断被挖掘
查看>>
云上数据安全,初识数据库审计
查看>>
微信中的video属性设置
查看>>
第一篇:linux系统应用管理之用户的切换
查看>>
haproxy负载均衡
查看>>
MySQL GEO 字段类型说明及案例
查看>>
Java中内存分配
查看>>
使用JDBC处理LOB大型对象
查看>>
我的友情链接
查看>>