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

了解控件的生命周期:
其中Init是由内而外,即先子控件后父控件,Load等相反。
在Init之前控件树根据声明语法已经生成
控件状态和视图状态:
控件状态是专门为维护控件的核心行为功能而设计的,而 ViewState 只包含维护控件内容 (UI) 的状态。应该按照这个规则设计控件状态。
先看一个例子:改自asp服务器控件和组件开发一书
PageTracker,用来跟踪页面会话状态和程序状态收到的点击数目,并计算页面往返时间
跟踪方式:
控件代码:
调用:
< cc1:PageTracker  ID ="PageTracker3"  runat ="server"  FormatString ="{0}"  TrackingMode ="ByTripTime"   />
       

分析视图状态
视图状态只会把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值(部分):
< Pair >
                  
< ArrayList >
                    
< IndexedString > TimeStamp </ IndexedString >
                    
< DateTime > 2006-8-7 16:05:19 </ DateTime >
                    
< IndexedString > Visible </ IndexedString >
                    
< Boolean > False </ Boolean >
                  
</ ArrayList >
                
</ 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中的视图状态会是:
< Pair >
                  
< ArrayList >
                    
< IndexedString > TimeStamp </ IndexedString >
                    
< DateTime > 2006-8-7 17:05:19 </ DateTime >
                    
< IndexedString > Visible </ IndexedString >
                    
< Boolean > True </ Boolean >
                  
</ ArrayList >
                
</ Pair >
仍然只有两个,因为另外两个仍没有被修改过,不会被序列化。
简单属性:
简单属性是可以映射到文本的属性,他们一般有内置的TypeConvert,我们为他们启用声明性语法,不必做任何工作。因为.net内置支持.本例的TrackingMode等都是简单属性。枚举类型会在页面解析时自动映射到EnumConvert。
控件状态:
现在修改上面的例子,加上ControlState,只是示例作用:
         protected   override   void  OnInit(EventArgs e)
        
{

            
base.OnInit(e);
            Page.RegisterRequiresControlState(
this);
        }

        
protected   override   object  SaveControlState()
        
{

            
return (object)this._trackingMode;
        }

        
protected   override   void  LoadControlState( object  savedState)
        
{
            
if (savedState != null)
            
{
                
this._trackingMode = (PageTrackingMode)savedState;
            }

        }

        
private  PageTrackingMode _trackingMode;
        [
       Category(
" Behavior " ),
       DefaultValue(PageTrackingMode.ByApplication),
       Description(
" The type of tracking to perform.in control state " )
       ]
        
public   virtual  PageTrackingMode TrackingMode
        
{
            
get
            
{
                
return (PageTrackingMode)_trackingMode;
            }

            
set
            
{
                
if (value < PageTrackingMode.ByApplication || value > PageTrackingMode.ByTripTime)
                
{
                    
throw new ArgumentOutOfRangeException("value");
                }

                _trackingMode 
= value;


            }

        }


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

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

protected   override   object  SaveControlState() 

   
object[] state = new object[3]; 
   state[
0= base.SaveControlState(); 
   state[
1= this._myValue1; 
   state[
2= this._myValue2; 
   
return state; 
}
 

protected   override   void  LoadControlState( object  state) 

    
object[] stateTmp = (object[])state; 
    
base.LoadControlState(stateTmp[0]); 
    
this._myValue1 = (string)stateTmp[1]; 
    
this._myValue2 = (string)stateTmp[2]; 
}


可以用Triplet,和Pair存取值,也可以自己定义数组,象上面一样。

一段代码中的两个问题:
       // Perform cleanup of the old storage.
                 
// We have to check that the Page is not null
                 
// because the control could be initialized
                 
// before it is added to the control tree.
                 
// Note that the Application and Session
                 
// objects are not available in the designer.
                 switch  (TrackingMode)
                
{
                    
case PageTrackingMode.ByApplication:
                        
if (Page != null && Page.Application != null)
                        
{
                            Page.Application.Remove(HitsKey);
                        }

                        
break;
                    
case PageTrackingMode.BySession:
                        
if (Page != null && Page.Session != null)
                        
{
                            Page.Session.Remove(HitsKey);
                        }

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

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

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

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

           
           
protected   override   void  FrameworkInitialize()  {
               SetStringResourcePointer(ASP.WebForm1_aspx.__stringResource, 
377);
               
this.__BuildControlTree(this);
               
this.FileDependencies = ASP.WebForm1_aspx.__fileDependencies;
               
this.EnableViewStateMac = true;
               
this.Request.ValidateInput();
           }

 

protected   virtual   void  AddParsedSubObject( object  obj)
{
      Control control1 
= obj as Control;
      
if (control1 != null)
      
{
            
this.Controls.Add(control1);
      }

}


 

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


 

你可能感兴趣的:(asp.net)