一、Control的呈现过程
在上个章节““生死有序”的控件生命周期”中,我们提到Render是控件开发的主角,但在控件树的“合成模式(Composite)”部分这位主角却缺席了(戏份太多的缘由)。哦,好吧。主角现在登场。
1)控件树呈现的“合成模式(Composite)”
控件树的呈现过程是一个华丽的大圈,它从RenderControl(HtmlTextWriter writer)开始、从RenderChildrenInternal(HtmlTextWriter writer, ICollection children)结束。其过程涉及Control类的6个方法。期间种种,我们慢慢道来。
public virtual void RenderControl(HtmlTextWriter writer);
protected void RenderControl(HtmlTextWriter writer, ControlAdapter adapter);
private void RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter);
protected internal virtual void Render(HtmlTextWriter writer);
protected internal virtual void RenderChildren(HtmlTextWriter writer);
internal void RenderChildrenInternal(HtmlTextWriter writer, ICollection children);
①Page中RenderControl()的调用:故事开始的地方
我们知道,Page类实现了IHttpHandler接口,所以ASP.NET框架得以最终把对请求进行响应的任务通过调用Page的ProcessRequest()方法交给aspx页面。在Page执行ProcessRequest()方法处理请求时,它完成了大量的工作:维持状态、处理回传数据、处理事件等等,而最后一个环节是基于HttpContext中Response.Output流创建HtmlTextWrite,并调用Page从Control类那里继承来的RenderControl()方法把页面内容发送给请求者。
//......呈现的入口
this.RenderControl(this.CreateHtmlTextWriter(this.Response.Output));
②RenderControl(HtmlTextWriter writer):
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
public virtual void RenderControl(HtmlTextWriter writer)
{
this.RenderControl(writer, this.Adapter);
}
③RenderControl(HtmlTextWriter writer, ControlAdapter adapter):
protected void RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
{
if (this.flags[16] || this.flags[512]) //if(this.Visible == false):详见下面附的代码
{
this.TraceNonRenderingControlInternal(writer);
return;
}
HttpContext httpContext = (this.Page == null) ? null : this.Page._context;
if (httpContext != null && httpContext.TraceIsEnabled)
{
int bufferedLength = httpContext.Response.GetBufferedLength();
this.RenderControlInternal(writer, adapter);
int bufferedLength2 = httpContext.Response.GetBufferedLength();
httpContext.Trace.AddControlSize(this.UniqueID, bufferedLength2 - bufferedLength);
return;
}
this.RenderControlInternal(writer, adapter);
}
[Bindable(true), DefaultValue(true), WebCategory("Behavior"), WebSysDescription("Control_Visible")]
public virtual bool Visible
{
get
{
return !this.flags[16] && (this._parent == null || this.DesignMode || this._parent.Visible);
}
set
{
if (this.flags[2])
{
bool flag = !this.flags[16];
if (flag != value)
{
this.flags.Set(32);
}
}
if (!value)
{
this.flags.Set(16);
return;
}
this.flags.Clear(16);
}
}
private const int invisible = 16;
private const int notVisibleOnPage = 512;
④RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter):
private void RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
{
try
{
this.BeginRenderTracing(writer, this);
if (adapter != null) //控件是否有相关的呈现适配器
{
//如果有,呈现适配器调用相关的呈现方法呈现控件
adapter.BeginRender(writer);
adapter.Render(writer);
adapter.EndRender(writer);
}
else
{
//如果没有,使用控件类本身的呈现方法呈现控件
this.Render(writer);
}
}
finally
{
this.EndRenderTracing(writer, this);
}
}
⑤Render(HtmlTextWriter writer):
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
protected internal virtual void Render(HtmlTextWriter writer)
{
this.RenderChildren(writer);
}
⑥RenderChildren(HtmlTextWriter writer):
protected internal virtual void RenderChildren(HtmlTextWriter writer)
{
ICollection controls = this._controls;
this.RenderChildrenInternal(writer, controls);
}
⑦RenderChildrenInternal(HtmlTextWriter writer, ICollection children):
internal void RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
{
if (this.RareFields != null && this.RareFields.RenderMethod != null)
{
writer.BeginRender();
this.RareFields.RenderMethod(writer, this);
writer.EndRender();
return;
}
if (children != null) //如果子控件的集合不为空,继续递归;若为空,结束递归。
{
foreach (Control control in children)
{
control.RenderControl(writer);
}
}
}
2)控件树呈现的简单模型
上面我们分析了Control利用“合成模式”递归生成控件树的全过程,也注意到Control类用于呈现的6个方法中有三个是虚方法,它们是开发控件我们可重写改变呈现逻辑的部分。首先,我们用伪代码概述下Control类中这三个方法呈现控件的模型。
public virtual void RenderControl(HtmlTextWriter writer)
{
if (this.Visible)
{
this.Render(writer);
}
}
protected internal virtual void Render(HtmlTextWriter writer)
{
-->this.RenderChildren(writer);
-->
}