如果是一般的aspx页面,习惯上要动态加载控件是放在CreateChildControl()里面的,在里面创建Control然后用this.Controls.Add()来添加。但是现在我是在一个HttpHandler里面使用Page就出问题了。HttpHandler部分代码如下:
public void ProcessRequest(HttpContext context)
{
HttpApplicationState Application=HttpContext.Current.Application;
Cache Cache=HttpContext.Current.Cache;
HttpRequest Request=HttpContext.Current.Request;
HttpResponse Response=HttpContext.Current.Response;
HttpServerUtility Server=HttpContext.Current.Server;
HttpSessionState Session=HttpContext.Current.Session; CustomPage page=new CustomControl();
StringBuilder stringBuilder=new StringBuilder();
StringWriter stringWriter=new StringWriter(stringBuilder);
HtmlTextWriter htmlTextWriter=new HtmlTextWriter(stringWriter);
page.RenderControl(htmlTextWriter);
Response.Write(stringBuilder.ToString());
}然后中CustomPage(CustomPage继承自Page类)如同往常一样使用CreateChildControls():
protected override void CreateChildControls()
{
this.Controls.Add(new Button())
}理论上会出现一个完全空的Button,客户端代码就象这样:
<input type="submit" name value="" />
但实际上什么都没有出现。然而如果把HttpHandler的ProcessRequest()改动一下关键部分成如下:
public void ProcessRequest(HttpContext context)
{
HttpApplicationState Application=HttpContext.Current.Application;
Cache Cache=HttpContext.Current.Cache;
HttpRequest Request=HttpContext.Current.Request;
HttpResponse Response=HttpContext.Current.Response;
HttpServerUtility Server=HttpContext.Current.Server;
HttpSessionState Session=HttpContext.Current.Session; CustomPage page=new CustomControl();
StringBuilder stringBuilder=new StringBuilder();
StringWriter stringWriter=new StringWriter(stringBuilder);
HtmlTextWriter htmlTextWriter=new HtmlTextWriter(stringWriter);
page.Controls.Add(new Button());  //关键在于在Render前添加Control。
page.RenderControl(htmlTextWriter);
Response.Write(stringBuilder.ToString());
}
于是页面就正常了。现在我的问题是,怎么CreateChildControls()会跑到Render()后面啦?我不是很清楚Page的执行机制,更不清楚System.Web.UI.PageHandlerFactory是怎么运作的(我就是想模仿他的基本功能——无html部分的纯class Page)。

解决方案 »

  1.   

    你把ProcessRequest都改了,怎么还能按书上写的那个思路,用Reflector查看一下Page是怎么运行ProcessRequest的
      

  2.   

    我也想知道PageHandlerFactory是怎样运作的,请问有没有已有的研究结果可用?
      

  3.   

    it is too long to explain here, use Reflectorhttp://www.aisto.com/roeder/dotnet/to read the source code to see how System.Web.UI.Page's ProcessRequest works
      

  4.   

    谢谢!用了Reflector能够看到,PageHandlerFactory其实只是调用了非保护类PageParser的受保护方法GetCompiledPageInstanceInternal,而PageParser也有一个非保护方法作相同的功能:GetCompiledPageInstance,总算是找到了一个调用入口。不过一直追寻下去,就是在TemplateControlParser内了,这对我来说有点麻烦:我把Page当作非TemplateControl用(无HTML代码部分),然而却没有ControlParser(当然是没有的,都不用Parse),所以还没找到关键部分。其实我本来的问题很简单,就是CreateChildControls假如不是Page内部自动顺序执行而会被触发的话,那就肯定存在外部触发的方法,不过我又看不出Page的哪个方法能够触发CreateChildControls。
      

  5.   

    用Reflector的Callee Graph功能,顺着CtreateChildControls()->EnsureChildControls()->...一直追寻,最后发现应该是ProcessRequest(context)为调用的根源。但是是谁调用了ProcessRequest(context)呢?Callee Graph在此找不到上一个Callee了。难道源头在PageParser(或者其基类TemplateControlParser)中?我暂时还没有找到。另外原来ProcessRequest(context)是一个在编辑环境不可见的入口,当然MSDN中也没提到,不过很好用(至少它调用了CreateChildControls(),呵呵)。我现在已经不是提供writer给Render了,而是直接用ProcessRequest(context)。
      

  6.   

    不妨参考下我写的追踪例子http://www.cnblogs.com/rchen/archive/2005/03/22/123592.html
      

  7.   

    看了一下 inelm(木野狐) 的例子,发现很有意思。我以前还没注意过这个问题(因为多数时候都是些HttpHandler)。不过我还是不知道如何追踪ProcessRequest(context)的Callee。不知道那个Callee Graph的搜索范围有多大,是不是所有当前察看的组件都会搜索。但是既然Callee Graph都搜索不出来,那么还说什么好呢,呵呵……
      

  8.   

    这个问题经过那么长时间,我终于弄明白了。原来我根本不应该直接调用Page.Render()输出,而应该直接把Page整个return回去,之后他就会被当作一个HttpHandler运行Page.ProcessRequest(),里面包括对Page.EnsureChildControls()、Page.CreateChildControls()等一系列Page的初始化操作。