现在的位置: 首页 > 综合 > 正文

★★★【庖丁解牛:纵向切入Asp.net 3.5控件和组件开发技术系列—(5)事件和数据回发机制】★★★

2012年08月31日 ⁄ 综合 ⁄ 共 6460字 ⁄ 字号 评论关闭
 

 

 

5  庖丁解牛系列—事件和数据回发机制

 

 

本章内容

5.1  控件事件和数据回发概述

5.2  事件和数据回发机制的实现

5.3  复合控件的事件处理机制

[点击下载本书word格式完整目录介绍]

5.1  控件事件和数据回发概述

在讲解实现控件事件和数据回发功能之前,有必要先了解一下相关的基本概念。

5.1.1  事件及其意义

要为控件定制事件,先得使用控件的事件。首先以经典的Button控件的OnClick事件为例子,说一下事件的使用模型。

1.注册事件

<asp:Button ID="Button1" runat="server"OnClick="Button1_Click"Text="Button"/>

u  或在Page_Load中注册:

protected void Page_Load(object sender, EventArgs e)

{

    this.Button1.Click +=new EventHandler(Button1_Click);

}

2.事件方法体

protected void Button1_Click(object sender, EventArgs e)

{

}

首先从事件要实现的功能角度理解事件。我们把按钮(Button)看做一个对象,把页面(Page)也看做一个对象。

正向理解:假如我们在Page对象中要修改Button的行为,可以直接通过this.Button1的形式直接访问Button对象的属性或方法对Button进行修改,可以理解为Page类能访问到Button代码功能。原因是Button1Page类内部的一个对象,类当然可以直接访问其内部的对象。

逆向理解:假如我们需要在Button中要访问Page中的代码呢?直接像上面那样通过this.Page的形式是不行的,因为ButtonPage类的内部对象,但Page不是Button类的内部对象,从面向对象角度讲,类(Button)不能访问其外部的对象(Page),也就是说在Button中不可能通过this.Page的形式访问到Page对象。而使用事件机制就可以解决此问题,即事件机制解决了面向对象编程中不允许类访问类外部代码问题。关于事件的应用本章后面会专门讲解,在这里仅介绍一下它的功能。

更深层了解一下Button的事件:假如我们没有对Button注册Click事件,则Button会执行一遍它内部的Click相关逻辑,并没有对Page对象产生任何影响;如果我们为Button定义了Click事件(如上面代码片段),则Button还是执行一遍它内部的Click相关逻辑,不同的是在执行自己内部逻辑的过程中它还执行了Page对象中的一些代码功能(即Button的事件体Button1_Click方法),就达到了我们要实现的功能。通过触动一个对象Button影响到另一个对象Page的行为,并且在Button的事件体中即可以修改Button本身(通过senderthis.Button1),也可以修改Page页面对象的其他控件或执行任意想要的代码功能。

其次从ASP.NET工作机制说一下它的工作原理,与桌面应用程序中的事件不同,ASP.NET服务器控件事件是在服务器上引发和处理的。当Web请求将客户端操作发送到服务器时,控件可以在服务器上引发事件来响应客户端操作。该页或其子控件对事件进行处理,然后ASP.NET将响应发送回客户端。这样,用户感觉就像在使用桌面应用程序一样。但是,控件开发人员必须了解只有一个客户端事件发送到服务器,即回发事件。

有些在客户端执行的事件(如JavaScript定义的客户端事件)没有被发送到服务器,不能被服务端处理,这样的事件不是我们这一章要讲的事件,本章讲的是服务器控件事件,是由服务器来处理的事件。

5.1.2  数据回发机制

ASP.NET技术的服务器编程中,服务器处理完客户端的每个请求就认为任务结束,当客户端再次请求时,服务器会作为新的一次请求处理,即使是相同的客户端也是如此。也就是说服务器不会保存我们两次请求之间的一些前后相接的数据,这就比较麻烦了,比如当我们输入一些信息到一个文本中,然后提交一个按钮,很多时候我们要在按钮提交的服务端事件中处理提交之前的数据和提交按钮时用户输入的最新数据,即想同时得到文本框的旧值和新值,而服务端不会保存前一个请求的任何信息,那怎样才能做到这一点呢?

两次页面请求之间的数据关联性问题,ASP.NET是通过视图机制实现的。简单地讲,视图区域信息(ViewState)存储在页面上的一个隐藏字段,里面存储每次需要视图机制保存的一些信息,每次提交时,它都会以“客户端到ó服务端”的形式来回传递一次,当处理完成后,最后会以处理后的新结果作为新的视图信息存储到页面中的隐藏字段,并与页面内容一起返回到客户端。后面会有针对视图状态机制的专门讲解,这里仅了解其功能即可。

有了视图机制,在其基础之上的数据回发机制就是完成处理视图信息的功能。具体过程是,服务端控件只要实现IPostBackDataHandler接口,则当客户端提交请求后,就会有机会利用IPostBackDataHandler接口的LoadPostData方法,在该方法内部处理子控件的新旧值逻辑,而视图信息数据这时以一个集合对象形式作为LoadPostData参数,并可以决定是否引发控件值变化后的事件。这就是要引入数据回发机制功能的原因。

通过上面两小节的讲解,您应该对事件和数据回发机制有了比较系统的认识。这样会较容易理解接下来要讲的事件和数据回发机制的具体使用和实践部分内容。

5.2  事件和数据回发机制的实现

5.2.1  客户端回传事件接口IPostBackEventHandler

要使控件捕获回发事件,控件必须实现System.Web.UI.IPostBackEventHandler 接口。此接口约定允许控件在服务器上引发事件来响应来自客户端的回发。IPostBackEventHandler接口包含一个方法。

  1. /// <summary>
  2. /// 获得本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary>
  5. public interface IPostBackEventHandler
  6. {
  7.     void RaisePostBackEvent(string eventArgument);
  8. }

参数

eventArgument表示要传递到事件处理程序的可选事件参数,一般通过此参数可以确定不同的引发事件源,进而作不同的逻辑处理。在本章最后有个例子说明eventArgument参数用法。回发后,页框架就会搜索发送的内容,并确定发送的名称是否与实现IPostBackEventHandler的服务器控件的UniqueID对应。如果对应,页框架就会在该控件上调用RaisePostBackEvent方法(在引发更改事件后)。

以下代码片段显示了在服务器上引发Click事件的RaisePostBackEvent实现:

  1. /// <summary>
  2. /// 获得本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary>
  5. public void RaisePostBackEvent(String eventArgument)
  6. {
  7.     ButtonEventArgs e = new ButtonEventArgs(eventArgument);
  8.     OnClick(this, e);
  9. }

在该方法中主要完成调用

OnClick(e)事件功能,其中包含两个参数:第一个参数为当前控件本身(即一般事件体中sender,类型一般为object);第二个参数eButtonEventArgs参数类型对象,是继承于System.EventArgs类实现的参数类,在该类中可以定义与代码逻辑相关的任意属性,作为事件体的参数。

最后,RaisePostBackEvent需要被客户端引发才能够执行,下面是一段能够引发服务端事件的代码:

  1. /// <summary>
  2. /// 获得本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary>
  5. protected override void Render(HtmlTextWriter output) 
  6. {
  7.     output.Write("<INPUT TYPE=submit name=" + this.UniqueID + 
  8.         " Value='Click Me' />");   
  9. }

这段代码输出一个HTMLbutton标签,并设置为提交类型。非常重要一点是,不要忘记设置其name属性,因为当回发后,页框架就会搜索发送的内容,并确定发送的名称是否与实现IPostBackEventHandler的服务器控件的UniqueID对应。如果对应,页框架就会在该控件上调用RaisePostBackEvent方法;反之,如果没有设置按钮的name值为UniqueID属性值,当单击按钮时页框架就不会引发该控件的RaisePostBackEvent方法,因为只有名称为UniqueID(服务器控件服务端ID)的按钮才会被注册为具有IPostBackEventHandler接口功能的控件。

下面通过一个简单的完整例子,了解事件回发处理机制,代码如下:

  1. /// <summary>
  2. /// 获得本书更多内容,请看:
  3. /// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
  4. /// </summary>
  5. [DefaultEvent("Click")]
  6. [ToolboxData("<{0}:PostBackEventControl runat=server></{0}:PostBackEventControl>")]
  7. public class PostBackEventControl : Control, IPostBackEventHandler
  8. {
  9.     public event EventHandler Click;
  10.     protected virtual void OnClick(EventArgs e)
  11.     {
  12.         if (Click != null)
  13.         {
  14.             Click(this, e);
  15.         }
  16.     }
  17.     public void RaisePostBackEvent(string eventArgument)
  18.     {
  19.         OnClick(EventArgs.Empty);
  20.     }
  21.     protected override void Render(HtmlTextWriter output)
  22.     {
  23.         output.Write("<INPUT TYPE=submit name="+this.UniqueID+"Value='单击我'
  24.         />");
  25.     }
  26. }

控件内容比较简单,仅输出一个提交类型的按钮,由于这个按钮类型为submit,所以当单击按钮时,其本身已经可以提交事件到服务器,但仅仅这样主控件还不能够捕捉到该按钮事件。控件能够捕捉处理该事件需要具备两个条件:第一,主控件继承了IPostBackEventHandler接口以及实现了RaisePostBackEvent方法;第二,必须有name值为UniqueID的客户端标签,页框架只认识控件的name属性。只有这两个条件同时具备才能够使控件具备捕捉并处理事件的机会。

语句[DefaultEvent("Click")]的功能是定义Click事件为默认事件。通常把最常用的一个事件定义为默认事件,如果定义了默认事件,在设计器中双击控件时系统会自动从源代码视图(*.aspx)切换到后面代码(*.cs)页面,并可以自动注册默认事件;否则,仅切换到后台代码,但不注册任何事件。

类代码中最上面的一句

public event EventHandler Click;

这条语句定义了一个委托事件。EventHandler是一个预定义的委托,专用于表示不生成数据的事件的事件处理程序方法。如果事件生成数据,则必须提供自定义事件数据类型,并且必须要么创建一个委托(其中第二个参数的类型为自定义类型),要么使用泛型EventHandler委托类并用自定义类型替代泛型类型参数。

5.1.1节讲过事件的意义及其完成的功能,即在一个类中执行另一个功能类的功能,要想执行另一个类中的某个功能方法,必定要在当前类中保留另一个方法的引用,委托和事件就是为实现此功能而出现的。委托类似于C++中的函数指针,它一般指向一个方法。这样,当调用本类中的委托就相当于调用了另一个类中的方法。

若要将事件与处理事件的方法关联,请向事件添加委托的实例和委托事件实例,如:

this.Button1.Click +=new EventHandler(Button1_Click);

除非移除了该委托,否则每当发生该事件时就调用事件处理程序。

另外,委托的应用比较广泛,还有更加复杂的应用,比如定义一个委托可以同时指向多个事件,即一个委托指定一个事件列表,当调用委托时可以执行一系列的事件。这里限于篇幅就不多讲了,更多关于事件委托概念请查看微软官方文档,那里面已经讲得很详细了。

5.2.2  客户端回发/回调揭密

对于服务端控件元素,比如ASP.NETButton标准服务端控件在提交时可以自动把请求发送到服务端处理,这样的控件我们不用自己处理它们的事件回发;但对于呈现不引起回发的HTML元素,如“文本框”(TextBox)或“链接按钮”(LinkButton),而希望由控件启动回发,则可以在ASP.NET中通过依靠客户端脚本的事件结构进行编程来实现这一功能。

完整地处理一个事件则还需要回发和捕捉。捕捉是IPostBackEventHandler接口的事情,上一节讲得比较清楚了,这一节主要讲回发(客户端回发请求到服务端)。

下面就以一些常用的HTML标记展开分析客户端回发机制,以及各种HTML标记的回发形式。

5.2.2.1  设置HTML Button标记的类型为submit

如上节的PostBackEventControl控件例子就是采用设置HTML Button标记的类型为submit方式从客户端提交回发的。回发代码如下:

output.Write("<INPUT TYPE=submit name=" + this.UniqueID + " Value='单击我' />");

INPUT是标准的HTML标记控件,它默认情况下没有runat="server",并且这些控件默认情况下只能处理一些客户端方法。INPUTTYPE属性表示该标记的控件类型,如:type=button表示按钮;type=submit表示可提交表单功能的按钮;type=text表示文本框控件;type=file表示上传文件控件等。这里主要说明的是当type=submit类型的控件表示提交按钮时,它显示的样式与type=button的效果一样,不同的是单击它后可以提交表单到服务器,且不需要另外的代码,ASP.NET默认情况下是提交表单到当前页面。type=submit提交功能是从IE 3.0版开始支持的,只要浏览器版本不小于此版本都可使用该功能。

5.2.2.2  使用方法GetPostBackEve

抱歉!评论已关闭.