浅谈ASP.NET内部机制(三)
前言:当我们把页面中的数据包含在表单中发送到服务器的时候,数据到底是以什么形式发送的?服务器接受到提交的数据后是如何读取这些数据,又是如何要决定来引发事件的?我们怎么样才能让我们自定义的控件能够在服务器端触发事件?为什么服务器控件有的要实IPostBackEventHandler,IPostBackDataHandler?.....
大家已经在知道,在我们开发ASP.NET网站的时候,每个服务器控件都有自己的ID。为了后面更好的展开,下面我们就来简单的看看这个场景:当我们在浏览器中点击了一个服务器控件,如 Button,此时页面回传给服务器,然后服务器就引发控件的Click等事件,这个场景是简单的不能在简单了。我们来进一步看,我们知道,不是所有的控件都会在服务器端触发事件的,比如 submit按钮,这个按钮也同样可以把表单数据传到服务器,但是这个控件不能在服务器端触发事件。其实在一个页面提交到了服务器之后,服务器就会检查是哪个控件引起了页面提交,然后就把这个控件的ID找到,然后再在我们请求的那个页面,如Default.aspx (假设我们点击按钮,请求的是Default.aspx),去找是否有服务器控件的ID和此时提交页面的ID是一样的,如果有,那么就在页面的生命周期的合适的时候,引发事件,在把处理的结果返回;如果没有,服务器就不做什么了特别的处理。
还有一点要注意的是,在查找那个控件的ID 的时候,页面(如Default.aspx),已经被编译成为了一个继承自Page的类
当然,上面说的只是一个很粗略额过程,希望大家有个总体的认识。下面就细致的讲解。
我通过一个流程来解释:
1.我们首先请求一个服务器端的页面,如:http://localhost/Demo/Default.aspx。为了方便解释,假设页面只有三个服务器控件:TextBox,DropDowmList,Button
定义如下:
class="cnblogs_code_copy"> <%@ Page Language=”C#” %>
在浏览器中,我们在”源“中,看到的如下:
确实,上面的那些HTML的代码确实没有什么,但是大家注意看看TextBox,DropDownList,它们的ID在服务器端的aspx页面和本地的源中的ID是一样的。
2.我们在浏览器中的TextBox中输入"xiaoyang",然后DropDowList 选中"Text2",然后我们点击按钮,提交。此时我们提交的数据被包含在表单中的,而且是以这样的格式保存的"TextBox1=xiaoyang&DropDownList1=Value2",不用多说,表单数据是用"&"来分隔的,而且每个分隔的串包含两个部分:ID和值,也就是"键值对"。
3.数据到了服务器后,ASP.NET就实例化一个 HttpRequest的类,这个类有两个属性 Forms和 QueryString,它们的类型都是NameValueCollection(键值对,大家可以把它看成一个HashTable),然后ASP.NET解析表单数据,表单数据就解析成为了一个个的键值对,然后就保存在Forms(POST提交)或者QueryString中(GET提交)。我们之前是以POST提交为例子的。
4.之前的事情是发生在页面的生命周期之前的。当页面调用自己的ProcessRequest方法后,就进入了页面生命周期,此时页面就会检查页面中的所有控件,看看它们有哪些实现了IPostBackDataHandler接口,然后把实现了这些接口的控件都放入到一个ArrayList中,然后也检查哪些控件实现了IPostBackEventHandler,也把它们假如到另外的集合中,之后就开发遍历实现了IPostBackDataHandler控件的集合,并且调用IPostBackDataHandler的方法:LoadPostData(string postdatakey,NameValueCollection value)。
之前说过了的,我们提交的表单的值都保存在了Forms中(它的类型是NameValueCollection 的),所以此时这些值就传入到了这个方法,然后就检查这些值和之前的是不是相同的,我们已经还记得,我们在浏览器中看到页面时,页面中的TextBox 初始时是没有值的,而且DropDownList选择的是Text2,现在我们的值改了,是“xiaoyang"和"Text2".所以这个方法检查的结果是:值变了,返回了true.只要返回了true,那么IPostBackDataHandler的下一个方法就会调用RaisePostDataChangeEvent();这个方法已经注册了事件,到页面生命周期的之后就后引发的。
5.所以,如果我们开发的自定义控件想要在数据改变的时候引发事件,那么就一定要实现IPostBackDataHandler接口。例子可以参看我的另外的控件开发的系列文章。
6.现在我们已经说到了数据的改变。下面就看看到底是怎么引发事件的。到了页面开发执行RaisePostbackEvent方法的时候,页面就会遍历实现了IPostBackEventHandler的控件的集合,并且检查是否在页面中存在一个控件:这个控件要实现了IPostBackEventHandler,并且ID和之前使得页面提交到服务器的的那个控件的ID一样。如果有IPostBackEventHandler的RaisePostBackEvent 方法来触发事件(不同控件实现这个方法的方式不同),如Click事件。
所以,如果想在自定义控件可以触发事件,那么就要实现IPostBackEventHandler接口。可以参看我的控件开发系列文章,有例子的。