这两天在看《ASP.NET服务器控件与组件开发》,解决了不少困惑。
从ASP转到ASP.NET的一个很大的原因就是服务器控件的易用性,自动的状态保持,以及方便的事件注册,但当时并不明白是怎么实现的。因为HTTP请求是无状态的,所以在ASP中我们需要将要保存的值在隐藏域里提交回去或者放入SESSION或者COOKIE中。而在.NET中,页面框架实际上是模拟了一个有状态的类似桌面程序的过程。每一个后续的请求都是在前一个请求中断的地方恢复并继续执行。而这种模拟是靠以下几个逻辑阶段实现的。
拿一个TextBox做为例子(别的控件甚至页面和它也差不多),当请求到达页面时,如果它被列入了要生成的控件树,那它就会被页面或者别的控件调用它的构造器来实例化。控件树的生成也是很有意思的,所有派生自Control类的控件(页面也是间接派生自Control),都有可重载的Render和RenderChildren方法和RenderControl(写程序时可以重载这些方法,加入自己的处理逻辑),页面控件首先会建立一个HtmlTextWriter的实例,然后将这个对象传给RenderControl方法,RenderControl方法会检查控件的Visible属性,如果为真,就调用Render方法,Render方法默认的实现会调用RenderChildren方法,而这个方法默认会调用每个子控件的RenderControl方法,这样通过递归,就完成了控件树的生成。下面是从书中看到的代码。
而HtmlTextWriter是什么东东呢?查一下MSDN,可以看到它们于System.Web.UI命名空间中,其解释是“在 Web 窗体页上写出一系列连续的 HTML 特定字符和文本。此类提供 ASP.NET 服务器控件在将 HTML 内容呈现给客户端时所使用的格式化功能”。
回到页面模型,每个控件实例化时都会默认调用OnInit方法(引发Init事件),在此过程中可以重载OnInit方法来添加自己的初始化逻辑。然后就到了实现有状态的WEB程序的最重要的几步,最先是页面自动调用TrackViewState方法,跟踪视图状态的更改,并保存到控件的StateTag对象中。如果是回传,则会调用LoadViewState方法,用来恢复ViewState字典,像TextBox这样实现了IPostBackDataHandler接口,则会通过LoadPostData方法更新控件数据状态。再接送就是加载(load),这时所有控件的所有最后状态已经保留。下一步如果是回传,则会触发RaisePostDataChangedEvent方法,如果不是,则进入PreRender,然后保存视图状态,生成控件(Render),卸载(Unload)(清除工作),释放(Dispose)(释放所有资源)。
至此,页面处理完成,处理程序会将生成的HTML流输出到客户端。
从ASP转到ASP.NET的一个很大的原因就是服务器控件的易用性,自动的状态保持,以及方便的事件注册,但当时并不明白是怎么实现的。因为HTTP请求是无状态的,所以在ASP中我们需要将要保存的值在隐藏域里提交回去或者放入SESSION或者COOKIE中。而在.NET中,页面框架实际上是模拟了一个有状态的类似桌面程序的过程。每一个后续的请求都是在前一个请求中断的地方恢复并继续执行。而这种模拟是靠以下几个逻辑阶段实现的。
拿一个TextBox做为例子(别的控件甚至页面和它也差不多),当请求到达页面时,如果它被列入了要生成的控件树,那它就会被页面或者别的控件调用它的构造器来实例化。控件树的生成也是很有意思的,所有派生自Control类的控件(页面也是间接派生自Control),都有可重载的Render和RenderChildren方法和RenderControl(写程序时可以重载这些方法,加入自己的处理逻辑),页面控件首先会建立一个HtmlTextWriter的实例,然后将这个对象传给RenderControl方法,RenderControl方法会检查控件的Visible属性,如果为真,就调用Render方法,Render方法默认的实现会调用RenderChildren方法,而这个方法默认会调用每个子控件的RenderControl方法,这样通过递归,就完成了控件树的生成。下面是从书中看到的代码。
public void RenderContrl(HtmlTextWriter writer){
if(Visible)
{Render(Writer);}
}
protected virtual void Render(HtmlTextWriter writer)
{
RenderChildren(writer);
}
protected virtual void RenderChildren(HtmlTextWriter writer)
{
foreach (Control c in Controls)
{c.RenderControl(writer);}
}
if(Visible)
{Render(Writer);}
}
protected virtual void Render(HtmlTextWriter writer)
{
RenderChildren(writer);
}
protected virtual void RenderChildren(HtmlTextWriter writer)
{
foreach (Control c in Controls)
{c.RenderControl(writer);}
}
而HtmlTextWriter是什么东东呢?查一下MSDN,可以看到它们于System.Web.UI命名空间中,其解释是“在 Web 窗体页上写出一系列连续的 HTML 特定字符和文本。此类提供 ASP.NET 服务器控件在将 HTML 内容呈现给客户端时所使用的格式化功能”。
回到页面模型,每个控件实例化时都会默认调用OnInit方法(引发Init事件),在此过程中可以重载OnInit方法来添加自己的初始化逻辑。然后就到了实现有状态的WEB程序的最重要的几步,最先是页面自动调用TrackViewState方法,跟踪视图状态的更改,并保存到控件的StateTag对象中。如果是回传,则会调用LoadViewState方法,用来恢复ViewState字典,像TextBox这样实现了IPostBackDataHandler接口,则会通过LoadPostData方法更新控件数据状态。再接送就是加载(load),这时所有控件的所有最后状态已经保留。下一步如果是回传,则会触发RaisePostDataChangedEvent方法,如果不是,则进入PreRender,然后保存视图状态,生成控件(Render),卸载(Unload)(清除工作),释放(Dispose)(释放所有资源)。
至此,页面处理完成,处理程序会将生成的HTML流输出到客户端。