我们来看每个阶段Control控件里面都做了一些什么事情
1. Instantiate :
控件被页面或者另一控件调用, 实例化。
2. Initialize :
3. Begin Tracking View State :
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
4 Load View State :
在这一阶段控件通过 LoadViewStateRecursive 方法从ViewState 中还原状态。
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
这一阶段比较简单, 也是我们最常用的。
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 控件的实现就明白了。
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
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 刚好相反。
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 阶段
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 继承, 并实现自定义逻辑。