拿一个TextBox做为例子(别的控件甚至页面和它也差不多),当请求到达页面 时,如果它被列入了要生成的控件树,那它就会被页面或者别的控件调用它的构造器来实例化。控件树的生成也是很有意思的,所有派生自Control类的控件 (页面也是间接派生自Control),都有可重载的Render和RenderChildren方法和RenderControl(写程序时可以重载这 些方法,加入自己的处理逻辑),页面控件首先会建立一个HtmlTextWriter的实例,然后将这个对象传给RenderControl方法, RenderControl方法会检查控件的Visible属性,如果为真,就调用Render方法,Render方法默认的实现会调用 RenderChildren方法,而这个方法默认会调用每个子控件的RenderControl方法,这样通过递归,就完成了控件树的生成。
查了一下RenderControl方法:将服务器控件的内容输出到所提供的 HtmlTextWriter 对象中;如果已启用跟踪功能,则存储有关控件的跟踪信息。(跟踪功能是为了调试用的,详细地解释在这里。)
RenderChildren方法呢?将服务器控件子级的内容输出到提供的 HtmlTextWriter 对象,此对象编写将在客户端呈现的内容。是不是这里的问题??如果是,在那里调用RenderChildren呢?
从控件生命周期(见附录)来看,这里应该是Render阶段。在这里我先假定在Render阶段,ViewState已经包含了子控件的状态信息了。 在控件树的遍历过程中,没理由不render子控件阿。先放在一边,还是先研究viewstate吧。。
附录:控件生命周期
阶段 | 控件需要执行的操作 | 要重写的方法或事件 |
初始化 | 初始化在传入 Web 请求生命周期内所需的设置。请参见处理继承的事件。 | Init 事件(OnInit 方法) |
加载视图状态 | 在此阶段结束时,就会自动填充控件的 ViewState 属性,详见维护控件中的状态中的介绍。控件可以重写 LoadViewState 方法的默认实现,以自定义状态还原。 | LoadViewState 方法 |
处理回发数据 | 处理传入窗体数据,并相应地更新属性。请参见处理回发数据。 注意 只有处理回发数据的控件参与此阶段。 | LoadPostData (如果已实现 IPostBackDataHandler) |
加载 | 执行所有请求共有的操作,如设置数据库查询。此时,树中的服务器控件已创建并初始化、状态已还原并且窗体控件反映了客户端的数据。请参见处理继承的事件。 | Load 事件 (OnLoad 方法) |
发送回发更改通知 | 引发更改事件以响应当前和以前回发之间的状态更改。请参见处理回发数据。 注意 只有引发回发更改事件的控件参与此阶段。 | RaisePostDataChangedEvent 方法 (如果已实现 IPostBackDataHandler) |
处理回发事件 | 处理引起回发的客户端事件,并在服务器上引发相应的事件。请参见捕获回发事件。 注意 只有处理回发事件的控件参与此阶段。 | RaisePostBackEvent 方法 (如果已实现 IPostBackEventHandler) |
预呈现 | 在呈现输出之前执行任何更新。可以保存在预呈现阶段对控件状态所做的更改,而在呈现阶段所对的更改则会丢失。请参见处理继承的事件。 | PreRender 事件 (OnPreRender 方法) |
保存状态 | 在此阶段后,自动将控件的 ViewState 属性保持到字符串对象中。此字符串对象被发送到客户端并作为隐藏变量发送回来。为了提高效率,控件可以重写 SaveViewState 方法以修改 ViewState 属性。请参见维护控件中的状态。 | SaveViewState 方法 |
呈现 | 生成呈现给客户端的输出。请参见呈现 ASP.NET 服务器控件。 | Render 方法 |
处置 | 执行销毁控件前的所有最终清理操作。在此阶段必须释放对昂贵资源的引用,如数据库链接。请参见 ASP.NET 服务器控件中的方法。 | Dispose 方法 |
卸载 | 执行销毁控件前的所有最终清理操作。控件作者通常在 Dispose 中执行清除,而不处理此事件。 | UnLoad 事件(On UnLoad 方法) |