asp.net控件开发(一)简单属性、视图状态、控件状态

了解控件的生命周期:
其中Init是由内而外,即先子控件后父控件,Load等相反。
在Init之前控件树根据声明语法已经生成
控件状态和视图状态:
控件状态是专门为维护控件的核心行为功能而设计的,而 ViewState 只包含维护控件内容 (UI) 的状态。应该按照这个规则设计控件状态。
先看一个例子:改自asp服务器控件和组件开发一书
PageTracker,用来跟踪页面会话状态和程序状态收到的点击数目,并计算页面往返时间
跟踪方式:
ContractedBlock.gif ExpandedBlockStart.gif
None.gif public enum PageTrackingMode
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif{
InBlock.gif        ByApplication,
InBlock.gif        BySession,
InBlock.gif        ByTripTime
ExpandedBlockEnd.gif    }
控件代码:
ContractedBlock.gif ExpandedBlockStart.gif
None.gif [
None.gif    DefaultProperty(
"TrackingMode")
None.gif    ]
ExpandedBlockStart.gifContractedBlock.gif    
public class PageTracker : WebControl dot.gif{
InBlock.gif   
InBlock.gif        
private TimeSpan _tripTime;
InBlock.gif
InBlock.gif        [
InBlock.gif        Category(
"Appearance"),
InBlock.gif        DefaultValue(
"{0}"),
InBlock.gif        Description(
"The formatting string used to display the value being tracked.")
InBlock.gif        ]
ExpandedSubBlockStart.gifContractedSubBlock.gif        
public virtual string FormatString dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
get dot.gif
InBlock.gif                
string s = (string)ViewState["FormatString"];
InBlock.gif                
return ((s == null? "{0}" : s);
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockStart.gifContractedSubBlock.gif            
set dot.gif{
InBlock.gif                ViewState[
"FormatString"= value;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        [
InBlock.gif        Browsable(
false),
InBlock.gif        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
InBlock.gif        ]
ExpandedSubBlockStart.gifContractedSubBlock.gif        
public int Hits dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
get dot.gif{
InBlock.gif                PageTrackingMode mode 
= TrackingMode;
InBlock.gif                
object o = null;
ExpandedSubBlockStart.gifContractedSubBlock.gif                
if (mode == PageTrackingMode.ByApplication) dot.gif{
InBlock.gif                    o 
= Page.Application[HitsKey];
ExpandedSubBlockEnd.gif                }

ExpandedSubBlockStart.gifContractedSubBlock.gif                
else if (mode == PageTrackingMode.BySession) dot.gif{
InBlock.gif                    o 
= Page.Session[HitsKey];
ExpandedSubBlockEnd.gif                }

ExpandedSubBlockStart.gifContractedSubBlock.gif                
else dot.gif{
InBlock.gif                    
throw new NotSupportedException("Hits is only supported when TrackingMode is PageTrackingMode.ByApplication or PageTrackingMode.BySession");
ExpandedSubBlockEnd.gif                }

InBlock.gif                
return ((o == null? 0 : (int)o);
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        [
InBlock.gif        Category(
"Behavior"),
InBlock.gif        DefaultValue(PageTrackingMode.ByApplication),
InBlock.gif        Description(
"The type of tracking to perform.")
InBlock.gif        ]
ExpandedSubBlockStart.gifContractedSubBlock.gif        
public virtual PageTrackingMode TrackingMode dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
get dot.gif{
InBlock.gif                
object mode = ViewState["TrackingMode"];
InBlock.gif                
return ((mode == null? PageTrackingMode.ByApplication : (PageTrackingMode)mode);
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockStart.gifContractedSubBlock.gif            
set dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif                
if (value < PageTrackingMode.ByApplication || value > PageTrackingMode.ByTripTime) dot.gif{
InBlock.gif                    
throw new ArgumentOutOfRangeException("value");
ExpandedSubBlockEnd.gif                }

InBlock.gif                ViewState[
"TrackingMode"= value;
InBlock.gif                
InBlock.gif                
// Perform cleanup of the old storage.
InBlock.gif                
// We have to check that the Page is not null
InBlock.gif                
// because the control could be initialized
InBlock.gif                
// before it is added to the control tree.
InBlock.gif                
// 因为 Application and Session
InBlock.gif                
// 在设计时不可用,所以会出现异常
ExpandedSubBlockStart.gifContractedSubBlock.gif                switch (TrackingMode) dot.gif{
InBlock.gif                    
case PageTrackingMode.ByApplication:
ExpandedSubBlockStart.gifContractedSubBlock.gif                        
if (Page != null && Page.Application != nulldot.gif{
InBlock.gif                            Page.Application.Remove(HitsKey);
ExpandedSubBlockEnd.gif                        }

InBlock.gif                        
break;
InBlock.gif                    
case PageTrackingMode.BySession:
ExpandedSubBlockStart.gifContractedSubBlock.gif                        
if (Page != null && Page.Session != nulldot.gif{
InBlock.gif                            Page.Session.Remove(HitsKey);
ExpandedSubBlockEnd.gif                        }

InBlock.gif                        
break;
InBlock.gif                    
case PageTrackingMode.ByTripTime:
InBlock.gif                        ViewState.Remove(
"TimeStamp");
InBlock.gif                        
break;
ExpandedSubBlockEnd.gif                }
                           
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        [
InBlock.gif        Browsable(
false),
InBlock.gif        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
InBlock.gif        ]
ExpandedSubBlockStart.gifContractedSubBlock.gif        
public TimeSpan TripTime dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
get dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif                
if (TrackingMode != PageTrackingMode.ByTripTime) dot.gif{
InBlock.gif                    
throw new NotSupportedException("TripTime is only supported when TrackingMode is PageTrackingMode.ByTripTime");
ExpandedSubBlockEnd.gif                }

InBlock.gif                
return _tripTime;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
protected override HtmlTextWriterTag TagKey dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
get dot.gif{
InBlock.gif                
return HtmlTextWriterTag.Div;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
private string HitsKey dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
get dot.gif{
InBlock.gif                
// Create a unique HitsKey for the page for keying 
InBlock.gif                
// in hits per page in the Application and Session objects.
InBlock.gif
                return Page.GetType().FullName;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
protected override void OnLoad(EventArgs e) dot.gif{
InBlock.gif            
base.OnLoad(e);
ExpandedSubBlockStart.gifContractedSubBlock.gif            
switch (TrackingMode)dot.gif{
InBlock.gif                
case PageTrackingMode.ByApplication:
InBlock.gif                    
// Update the page hits in the Application 
InBlock.gif                    
// object. This operation needs a lock because
InBlock.gif                    
// multiple users could access a page at the same 
InBlock.gif                    
// time.
ExpandedSubBlockStart.gifContractedSubBlock.gif
                    lock(Page.GetType())dot.gif{
InBlock.gif                        Page.Application[HitsKey] 
= Hits + 1;
ExpandedSubBlockEnd.gif                    }

InBlock.gif                    
break;
InBlock.gif                
case PageTrackingMode.BySession:
InBlock.gif                    Page.Session[HitsKey] 
= Hits + 1;
InBlock.gif                    
break;
InBlock.gif                
case PageTrackingMode.ByTripTime:
InBlock.gif                    
// Get the timestamp for the previous request 
InBlock.gif                    
// from ViewState and compute the difference
InBlock.gif                    
// between the previous 
InBlock.gif                    
// and current timestamp.
InBlock.gif
                    object timeStamp = ViewState["TimeStamp"];
InBlock.gif                    DateTime requestTime 
= Context.Timestamp;
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
if (timeStamp == nulldot.gif{
InBlock.gif                        _tripTime 
= TimeSpan.Zero;
InBlock.gif                        
this.Visible = false;
ExpandedSubBlockEnd.gif                    }

ExpandedSubBlockStart.gifContractedSubBlock.gif                    
else dot.gif{
InBlock.gif                        
this.Visible = true;
InBlock.gif                        _tripTime 
= (requestTime - (DateTime)timeStamp); 
ExpandedSubBlockEnd.gif                    }

InBlock.gif                    ViewState[
"TimeStamp"= requestTime;
InBlock.gif                    
break;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
protected override void RenderContents(HtmlTextWriter writer) dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
switch (TrackingMode)dot.gif{
InBlock.gif                
case PageTrackingMode.ByApplication:
InBlock.gif                
case PageTrackingMode.BySession:
InBlock.gif                    writer.Write(FormatString, Hits);
InBlock.gif                    
break;
InBlock.gif                
case PageTrackingMode.ByTripTime:
InBlock.gif                    writer.Write(FormatString, TripTime.TotalSeconds);
InBlock.gif                    
break;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }
调用:
None.gif < cc1:PageTracker  ID ="PageTracker3"  runat ="server"  FormatString ="{0}"  TrackingMode ="ByTripTime"   />
None.gif       

分析视图状态
视图状态只会把Diry的数据序列化到客户端,并不会序列化所有ViewState中的值,这样就减少了往返的数据量。如果禁用视图状态,就不会执行SaveViewState和LoadViewState。
第一次请求时(非Postback),视图状态从生成到序列化到客户端的过程:
1、在Init事件后,会调用TrackViewState方法,在此方法中,页面会自动调用控件的TrackViewState,并且把 变化的属性存储到ViewState,保证了尽量少的值.此例子中ViewState此时只有两个值:FormtString和TrackingMode,其余的值还保持默认状态,并不会被加入ViewState。
2、Onload方法执行时,通过执行:“ ViewState[ " TimeStamp " =  requestTime;",ViewState中会加入TimeStamp,值变为三个,虽然通过 this .Visible  =   false把Visible设置为了False,可是在此时并不在ViewState中加入值,除非显示调用ViewState方法,否则到SaveViewState时才会加入到视图状态。
3、执行SaveViewState,因为Onload修改了Visible的属性,这时Visible被加入,此时ViewState的值变为四个。这个例子中值是页面框架自动保存到ViewState中的。如果自定义视图状态管理,必须自己实现Save,load。在此后控件状态的改变都不会被加入到视图状态中了,这是可以修改视图状态的最后机会。
4、解码序列化的视图状态:
通过查看生成的Html,解析出它的Viewstate值(部分):
None.gif < Pair >
None.gif                  
< ArrayList >
None.gif                    
< IndexedString > TimeStamp </ IndexedString >
None.gif                    
< DateTime > 2006-8-7 16:05:19 </ DateTime >
None.gif                    
< IndexedString > Visible </ IndexedString >
None.gif                    
< Boolean > False </ Boolean >
None.gif                  
</ ArrayList >
None.gif                
</ Pair >
其中Pair是存储ViewState的结构,第一个是状态名,第二个是值。从中可以看到视图状态只有两对值,而最后Save到ViewState的却是4对,为什么会如此呢?
原来,FormtString和TrackingMode没有被改写过,他们是没有必要被序列化到客户端的,从他们的IsItemDiry=fals可以看处来。
回发时(Postback)
在TraceViewState后会调用LoadViewState,LoadViewState会自动反序列化被编码的值(不自定义状态管理的情况下),并且合并TraceViewState生成的ViewState集合,本例中会是(Visible和 TimeStamp+ FormtString和TrackingMode)。
然后经过Onload,修改了timeStamp,Visible,再到SaveViewState被序列化到客户端。
生成的Html中的视图状态会是:
None.gif < Pair >
None.gif                  
< ArrayList >
None.gif                    
< IndexedString > TimeStamp </ IndexedString >
None.gif                    
< DateTime > 2006-8-7 17:05:19 </ DateTime >
None.gif                    
< IndexedString > Visible </ IndexedString >
None.gif                    
< Boolean > True </ Boolean >
None.gif                  
</ ArrayList >
None.gif                
</ Pair >
仍然只有两个,因为另外两个仍没有被修改过,不会被序列化。
简单属性:
简单属性是可以映射到文本的属性,他们一般有内置的TypeConvert,我们为他们启用声明性语法,不必做任何工作。因为.net内置支持.本例的TrackingMode等都是简单属性。枚举类型会在页面解析时自动映射到EnumConvert。
控件状态:
现在修改上面的例子,加上ControlState,只是示例作用:
None.gif          protected   override   void  OnInit(EventArgs e)
ExpandedBlockStart.gifContractedBlock.gif        
dot.gif {
InBlock.gif
InBlock.gif            
base.OnInit(e);
InBlock.gif            Page.RegisterRequiresControlState(
this);
ExpandedBlockEnd.gif        }

None.gif        
protected   override   object  SaveControlState()
ExpandedBlockStart.gifContractedBlock.gif        
dot.gif {
InBlock.gif
InBlock.gif            
return (object)this._trackingMode;
ExpandedBlockEnd.gif        }

None.gif        
protected   override   void  LoadControlState( object  savedState)
ExpandedBlockStart.gifContractedBlock.gif        
dot.gif {
InBlock.gif            
if (savedState != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
this._trackingMode = (PageTrackingMode)savedState;
ExpandedSubBlockEnd.gif            }

ExpandedBlockEnd.gif        }

None.gif        
private  PageTrackingMode _trackingMode;
None.gif        [
None.gif       Category(
" Behavior " ),
None.gif       DefaultValue(PageTrackingMode.ByApplication),
None.gif       Description(
" The type of tracking to perform.in control state " )
None.gif       ]
None.gif        
public   virtual  PageTrackingMode TrackingMode
ExpandedBlockStart.gifContractedBlock.gif        
dot.gif {
InBlock.gif            
get
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
return (PageTrackingMode)_trackingMode;
ExpandedSubBlockEnd.gif            }

InBlock.gif            
set
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
if (value < PageTrackingMode.ByApplication || value > PageTrackingMode.ByTripTime)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
throw new ArgumentOutOfRangeException("value");
ExpandedSubBlockEnd.gif                }

InBlock.gif                _trackingMode 
= value;
InBlock.gif
InBlock.gif
ExpandedSubBlockEnd.gif            }

ExpandedBlockEnd.gif        }


控件状态实际上就是自定义的视图状态,现在简单的分析一下:
通过调用RegisterRequiresControlState 告诉容器Page,自定义控件将要使用控件状态。
LoadControlState和SaveContolState和ViewState的方法是一样的,他们会在ViewState的同名方法前被调用。

如果父控件有自己的控件状态,你又想把自己的控件状态加入到父控件。可以调用基类的SaveControlState 和LoadControlState 

None.gif protected   override   object  SaveControlState() 
ExpandedBlockStart.gifContractedBlock.gif
dot.gif
InBlock.gif   
object[] state = new object[3]; 
InBlock.gif   state[
0= base.SaveControlState(); 
InBlock.gif   state[
1= this._myValue1; 
InBlock.gif   state[
2= this._myValue2; 
InBlock.gif   
return state; 
ExpandedBlockEnd.gif}
 
None.gif
None.gif
protected   override   void  LoadControlState( object  state) 
ExpandedBlockStart.gifContractedBlock.gif
dot.gif
InBlock.gif    
object[] stateTmp = (object[])state; 
InBlock.gif    
base.LoadControlState(stateTmp[0]); 
InBlock.gif    
this._myValue1 = (string)stateTmp[1]; 
InBlock.gif    
this._myValue2 = (string)stateTmp[2]; 
ExpandedBlockEnd.gif}

None.gif

可以用Triplet,和Pair存取值,也可以自己定义数组,象上面一样。
一段代码中的两个问题:
None.gif        // Perform cleanup of the old storage.
None.gif                 
// We have to check that the Page is not null
None.gif                 
// because the control could be initialized
None.gif                 
// before it is added to the control tree.
None.gif                 
// Note that the Application and Session
None.gif                 
// objects are not available in the designer.
None.gif
                 switch  (TrackingMode)
ExpandedBlockStart.gifContractedBlock.gif                
dot.gif {
InBlock.gif                    
case PageTrackingMode.ByApplication:
InBlock.gif                        
if (Page != null && Page.Application != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif                        
dot.gif{
InBlock.gif                            Page.Application.Remove(HitsKey);
ExpandedSubBlockEnd.gif                        }

InBlock.gif                        
break;
InBlock.gif                    
case PageTrackingMode.BySession:
InBlock.gif                        
if (Page != null && Page.Session != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif                        
dot.gif{
InBlock.gif                            Page.Session.Remove(HitsKey);
ExpandedSubBlockEnd.gif                        }

InBlock.gif                        
break;
InBlock.gif                    
case PageTrackingMode.ByTripTime:
InBlock.gif                        ViewState.Remove(
"TimeStamp");
InBlock.gif                        
break;
ExpandedBlockEnd.gif                }

1、在Init之前究竟执行了什么?
我认为:会实例化子控件并且把他们加入控件树
上面代码的注释说,控件会在加入控件树之前被初始化,因此我们要保证Page不为Null。
我认为:在根据声明语法生成控件树的过程中,执行到此步骤时,控件树已经生成了一部分,即Page在此时不可能为Null,因此不用判断Null。可是跟踪发现Page确实是Null,原因可能是,控件树已经部分生成,Page已经生成。但在此时子控件是不能访问Page的,所以才会是Null。
下面是编译源:

ExpandedBlockStart.gif ContractedBlock.gif   private  System.Web.UI.Control __BuildControlForm1()  dot.gif {
InBlock.gif               System.Web.UI.HtmlControls.HtmlForm __ctrl;         
InBlock.gif               __ctrl 
= new System.Web.UI.HtmlControls.HtmlForm();              
InBlock.gif               
this.Form1 = __ctrl;             
InBlock.gif               __ctrl.ID 
= "Form1";           
InBlock.gif               __ctrl.Method 
= "post";              
InBlock.gif               System.Web.UI.IParserAccessor __parser 
= ((System.Web.UI.IParserAccessor)(__ctrl));              
InBlock.gif               __parser.AddParsedSubObject(
new System.Web.UI.LiteralControl("\r\n\t\t\t"));              
InBlock.gif               
this.__BuildControlPageTracker3();              
InBlock.gif               __parser.AddParsedSubObject(
this.PageTracker3);                             
InBlock.gif               __parser.AddParsedSubObject(
new System.Web.UI.LiteralControl("\r\n\t\t\t\t"));            
InBlock.gif               
this.__BuildControlButton1();             
InBlock.gif               __parser.AddParsedSubObject(
this.Button1);             
InBlock.gif               __parser.AddParsedSubObject(
new System.Web.UI.LiteralControl("\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t\t"));              
InBlock.gif               
return __ctrl;
ExpandedBlockEnd.gif           }

None.gif           
ExpandedBlockStart.gifContractedBlock.gif           
private   void  __BuildControlTree(System.Web.UI.Control __ctrl)  dot.gif {
InBlock.gif               System.Web.UI.IParserAccessor __parser 
= ((System.Web.UI.IParserAccessor)(__ctrl));            
InBlock.gif               __parser.AddParsedSubObject(
this.CreateResourceBasedLiteralControl(0377true));            
InBlock.gif               __parser.AddParsedSubObject(
new System.Web.UI.LiteralControl("\r\n\t</HEAD>\r\n\t<body>\r\n\t\t"));           
InBlock.gif               
this.__BuildControlForm1();             
InBlock.gif               __parser.AddParsedSubObject(
this.Form1);              
InBlock.gif               __parser.AddParsedSubObject(
new System.Web.UI.LiteralControl("\r\n\t</body>\r\n</HTML>\r\n"));              
ExpandedBlockEnd.gif           }

None.gif           
ExpandedBlockStart.gifContractedBlock.gif           
protected   override   void  FrameworkInitialize()  dot.gif {
InBlock.gif               SetStringResourcePointer(ASP.WebForm1_aspx.__stringResource, 
377);
InBlock.gif               
this.__BuildControlTree(this);
InBlock.gif               
this.FileDependencies = ASP.WebForm1_aspx.__fileDependencies;
InBlock.gif               
this.EnableViewStateMac = true;
InBlock.gif               
this.Request.ValidateInput();
ExpandedBlockEnd.gif           }

 

None.gif protected   virtual   void  AddParsedSubObject( object  obj)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif      Control control1 
= obj as Control;
InBlock.gif      
if (control1 != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif            
this.Controls.Add(control1);
ExpandedSubBlockEnd.gif      }

ExpandedBlockEnd.gif}

None.gif
None.gif 
None.gif

可以看到在执行Init前,会编译运行上面自动生成的编译源,这样以来第一个问题就彻底清楚了,也证明我上面的判断是正确的。


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值