服务器控件开发 —— Control 基类(1)

       要想熟练的开发服务器控件 首先需要了解asp.net 中服务器控件的生命周期。其实服务器控件的生命周期与asp.net 页面的生命周期差不多, 因为asp.net 页面其实就是间接从Control继承。
        我们来看每个阶段Control控件里面都做了一些什么事情

1.   Instantiate :
   控件被页面或者另一控件调用, 实例化。
2.  Initialize  :
3.  Begin Tracking View State :
#region
 
internal void InitRecursive(Control namingContainer)
            
{
                
if (_controls != null)
                
{
                    
if (_isNamingContainer)                         // _isNamingContainer 指示控件是否从实现INamingContainer 接口 
                        namingContainer = this;                     

                    
if (namingContainer != null &&
                        namingContainer._userId 
== null &&
                        namingContainer.autoID)
                        namingContainer._userId 
= namingContainer.GetDefaultName() + "b";
                    
                    
// 以下循环递归调用InitRecursive 初始化控件树中的所有控件。所做的工作主要有:
                    foreach (Control c in _controls)
                    
{
                        c._page 
= Page;                           // 将父控件的 page 对象 赋给所有子控件
                        c._namingContainer = namingContainer;     // 往子控件传递 NamingContainer.
                        if (namingContainer != null && c._userId == null && c.autoID)
                            c._userId 
= namingContainer.GetDefaultName() + "c";   // 根据NamingContainer 给控件的userId 赋值, 我们经常使用的
                                                                   
// UniqueID, ClientID 都与这个userId   相关联

                        c.InitRecursive(namingContainer);
                    }

                }


                initing 
= true;
                OnInit(EventArgs.Empty);                           
// 激发OnInit 事件。
                TrackViewState();                                  // 该方法的内部实现其实就是设置一个标识 指示开始 Track ViewState 状态。
                inited = true;
                initing 
= false;
            }

#Endregion
            **  从代码中我们可以看出OnInit 事件中给控件赋值并不会被保存到ViewState 中去。
4 Load View State :
            在这一阶段控件通过 LoadViewStateRecursive  方法从ViewState 中还原状态。
#region
 
internal void LoadViewStateRecursive(object savedState)
            
{
                
if (!EnableViewState || savedState == null)
                    
return;

                
// savedInfo.First 保存控件本身属性的视图状态
                
// savedInfo.Third[i] 保存第i 个控件的视图状态
                
// savedInfo.Second[i] 保存当前需要保存视图状态的控件列表, 如果控件已经被移出(比如说调用了Controls.Clear())
                
//其视图状态被保存到pendingVS 这个Hashtable 中 中
                Triplet savedInfo = (Triplet)savedState;
                LoadViewState(savedInfo.First);

                ArrayList controlList 
= savedInfo.Second as ArrayList;
                
if (controlList == null)
                    
return;
                ArrayList controlStates 
= savedInfo.Third as ArrayList;
                
int nControls = controlList.Count;
                
// 递归调用 为每一个子控件加载视图状态
                for (int i = 0; i < nControls; i++)
                
{
                    
int k = (int)controlList[i];
                    
if (k < Controls.Count && controlStates != null)
                    
{
                        Control c 
= Controls[k];
                        c.LoadViewStateRecursive(controlStates[i]);
                    }

                    
else
                    
{
                        
if (pendingVS == null)
                            pendingVS 
= new Hashtable();

                        pendingVS[k] 
= controlStates[i];
                    }

                }


                viewStateLoaded 
= true;
            }

#endregion

5 Load Postback Data
         对于需要加载回发数据的控件必须实现IPostBackDataHandler  接口
         public interface IPostBackDataHandler
        {
            bool LoadPostData(string postDataKey, NameValueCollection postCollection);
            void RaisePostDataChangedEvent();
        }
        在这一阶段 控件调用LoadPostData 接口方法来获取回发数据, 其中的postDataKey 表明回发数据的关键字 ,
        NameValueCollection 是所有PostBack 的数据的集合。
        返回值指示是否 激发 RaisePostDataChangedEvent 事件。

6 Load 
         这一阶段比较简单, 也是我们最常用的。

#region
   
internal void LoadRecursive()
            
{
                OnLoad(EventArgs.Empty);                    
// 激发OnLoad事件
                if (_controls != null)
                
{
                    
foreach (Control c in _controls)
                        c.LoadRecursive();                  
// Load 子控件
                }

                loaded 
= true;
            }

#endregion

7  Raise Change Event
         判断Load Postback Data 阶段的返回值, 如果返回True,就执行RaisePostDataChangedEvent()方法.
 
8 Raise PostBack Event 
         这一阶段只有单控件实现 IPostBackEventHandler 接口时执行。
           void IPostBackEventHandler.RaisePostBackEvent(string eventArgument)。 该接口方法主要用来允许
           开发者捕获PostBack  事件并执行自定义逻辑。 不好理解, 看看Button  控件的实现就明白了。

#region
   
void IPostBackEventHandler.RaisePostBackEvent (string eventArgument)
        
{
            RaisePostBackEvent (eventArgument);
        }

   
protected virtual void RaisePostBackEvent (string eventArgument)
        
{
         
// 验证控件
            if (CausesValidation)
                Page.Validate (ValidationGroup);
        
//  激发OnClick 事件, 执行用户Onclick 事件代码。
            OnClick (EventArgs.Empty);
        
//  激发OnCommand 事件,  执行用户OnCommand 自定义代码。
            OnCommand (new CommandEventArgs (CommandName, CommandArgument));
        }

#endregion

9 PreRender

#region
internal void PreRenderRecursiveInternal()
            
{
                
if (_visible)
                
{
                    EnsureChildControls();               
// 检查子控件是否创建, 如果未创建,侧创建。
                    OnPreRender(EventArgs.Empty);        // 激发OnPreRender 事件
                    if (_controls == null)
                        
return;
                    
// 递归调用子控件上的PreRenderRecursiveInternal()
                    foreach (Control c in _controls)
                        c.PreRenderRecursiveInternal();
                }

                prerendered 
= true;
            }

            
            
// 保障子控件不会被重复创建
            protected virtual void EnsureChildControls() 
            
{
                
if (ChildControlsCreated == false && !creatingControls)
                
{
                    creatingControls 
= true;
                    CreateChildControls();
                    ChildControlsCreated 
= true;
                    creatingControls 
= false;
                }

            }

#endregion

            看到这段代码之前也写过一些组合控件, 依样画葫芦 重写 ChildControlsCreated(), 一直以为
            ChildControlsCreated()在控件生命周期的特定阶段被调用, 现在才知道以前的想法是错的。
            基本上ChildControlsCreated()  是在第一次调用EnsureChildControls 的时候被创建,最晚是在Page Load
            之后,   PreRender 之前被调用, Control 类中另一个调用EnsureChildControls 的地方是在FindControl()
            方法里面。 也就 是说一旦你调用了FindControl() 方法, 子控件就已经被自动创建。
10  SaveViewState
           保存视图状态, 与Load ViewState 刚好相反。

#region
internal object SaveViewStateRecursive()
            
{
                
if (!EnableViewState)
                    
return null;

                ArrayList controlList 
= null;
                ArrayList controlStates 
= null;

                
int idx = -1;
                
// 保存子控件视图状态
                foreach (Control ctrl in Controls)
                
{
                    
object ctrlState = ctrl.SaveViewStateRecursive();
                    idx
++;
                    
if (ctrlState == null)
                        
continue;

                    
if (controlList == null)
                    
{
                        controlList 
= new ArrayList();
                        controlStates 
= new ArrayList();
                    }


                    controlList.Add(idx);
                    controlStates.Add(ctrlState);
                }

               
// 保存控件本身视图状态
                object thisState = SaveViewState();
                
if (thisState == null && controlList == null && controlStates == null)
                    
return null;

                
return new Triplet(thisState, controlList, controlStates);
            }

#endregion

11  Render
             Render 阶段

#region
public void RenderControl(HtmlTextWriter writer)
            
{
                   
// 控件是否可见
                   if (_visible)
                          Render(writer);
            }

                
            
protected virtual void Render(HtmlTextWriter writer) //DIT
            {
                RenderChildren(writer);
            }


            
protected virtual void RenderChildren(HtmlTextWriter writer) //DIT
            {
               
//  SetRenderMethodDelegate 可以设置一个Render代理。
                if (_renderMethodDelegate != null)
                    _renderMethodDelegate(writer, 
this);
                
else if (_controls != null)
                
// 递归调用, Render 子控件
                    foreach (Control c in _controls)
                        c.RenderControl(writer);
            }

#endregion

        看到这三个方法, 终于搞清楚了RenderControl, Render, RenderChildren 三个方法的区别。 这三个方法的设计 充分体现了通过组合, 而不是通过过程来实现的面向对象原则。 
12 UnLoad
13 Dispose
             这2 个阶段大同小异, 都是通过递归调用来卸载页面 释放资源。
我的下一篇文章将分析WebControl 是如何从
Control 继承, 并实现自定义逻辑。

转载于:https://www.cnblogs.com/wwwyfjp/archive/2008/06/16/1223256.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值