18.4 事件处理
尽管视图状态与控件状态可以帮助我们跟踪控件中的内容,但是对于输入控件,这些还是远远不够的。这是因为输入控件允许用户更改它们的数据。
18.4.1 回传数据与change事件
在创建需要检查由客户端回发到服务器的窗体数据的自定义服务器控件时,必须要实现IPostBackDataHandler接口。此接口定义的协定允许服务器控件确定其状态是否应作为回发的结果而发生更改,并引发相应的事件。
也就是说,通过实现这个接口,向ASP.NET表明,当一个回传发生的时候,控件将需要一个时机来检查回传数据。无论实际上是哪个控件触发了这次回传,你的控件将会得到这个机会。
除此之外,IPostBackDataHandler接口还定义了如下两个方法:
1)LoadPostData:当由某个类实现时,它为ASP.NET服务器控件处理回发数据。
2)RaisePostDataChangedEvent:当由类实现时,它用信号要求服务器控件对象通知ASP.NET应用程序该控件的状态已更改。如果有必要,ASP.NET会通过调用RaisePostDataChangedEvent方法来给你机会触发change事件。
下面的代码示例演示了一个实现IPostBackDataHandler接口的自定义文本框服务器控件。其中,Text属性作为回发的结果而发生更改,并且服务器控件在回发数据加载后引发TextChanged事件。如代码清单18-5所示。
代码清单18-5 MyTextBox.cs
using System;
using System.Web;
using System.Web.UI;
using System.Collections;
using System.Collections.Specialized;
namespace_18_1
{
[System.Security.Permissions.PermissionSet(
System.Security.Permissions.SecurityAction.Demand,
Name="FullTrust")]
public class MyTextBox:Control, IPostBackDataHandler
{
public String Text
{
get
{
return(String)ViewState["Text"];
}
set
{
ViewState["Text"]=value;
}
}
public event EventHandler TextChanged;
public virtual bool LoadPostData(string postDataKey,
NameValueCollection postCollection)
{
String presentValue=Text;
String postedValue=postCollection[postDataKey];
if(presentValue==null
||!presentValue.Equals(postedValue))
{
Text=postedValue;
return true;
}
return false;
}
public virtual void RaisePostDataChangedEvent()
{
OnTextChanged(EventArgs.Empty);
}
protected virtual void OnTextChanged(EventArgs e)
{
if(TextChanged!=null)
TextChanged(this, e);
}
protected override void Render(HtmlTextWriter output)
{
output.Write("<INPUT type=text name="+this.UniqueID
+"value="+this.Text+">");
}
}
}
在代码清单18-5中,LoadPostData方法有两个参数,第一个参数postDataKey用于标识当前控件数据的键值,而第二个参数postCollection是传送回到页面的值的集合。因此,可以使用下面的语法来访问控件数据:
String postedValue=postCollection[postDataKey];
在这里,LoadPostData方法还需要告诉ASP.NET是否需要一个change事件。可以通过返回true来告诉ASP.NET一个改变已经发生。如果返回true, ASP.NET就会在所有控件都被初始化之后调用RaisePostDataChangedEvent方法;如果返回false,则不会调用RaisePostDataChangedEvent方法。
而RaisePostDataChangedEvent方法就比较简单了,就是触发一个change事件。如下面的代码所示:
public virtual void RaisePostDataChangedEvent()
{
OnTextChanged(EventArgs.Empty);
}
18.4.2 触发回传
在上面,通过实现IPostBackDataHandler接口,使你能够参与每一个回传活动,并且读取控件里的回传数据。但是,如果想触发回传,该怎么做呢?
其实,类似的最简单的例子就是Button控件了。这里,支持是自动的,因为按照HTML表单标准,“提交”按钮总是回传页面。但是许多其他的富Web控件(包括Calendar与GridView控件)都允许通过另一种ASP.NET机制提供,即JavaScript函数_doPostBack。_doPostBack函数接受两个参数:触发回传的控件的名称和一个代表额外回传数据的字符串。
ASP. NET提供了Page.ClientScript.GetPostBackEventReference方法来简化了对_doPostBack函数的访问。该方法创建一个对客户端的_doPostBack函数的引用,这样就可以把这个引用呈现到控件里。
通常,会将_doPostBack函数引用放置在控件中HTML元素的onClick特性中。这样,当某个HTML元素被单击时,_doPostBack函数就会被触发。下面的示例演示了一个可以单击的图像,当单击该图像时,该页面就会被回传了,而无须任何额外的代码。如代码清单18-6所示。
代码清单18-6 MyImageButton.cs
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;
using System.Collections.Specialized;
namespace_18_1
{
public class MyImageButton:WebControl, IPostBackDataHandler
{
public MyImageButton():base(HtmlTextWriterTag.Img)
{
ImageUrl="";
}
public String ImageUrl
{
get
{
return(String)ViewState["ImageUrl"];
}
set
{
ViewState["ImageUrl"]=value;
}
}
protected override void AddAttributesToRender(
HtmlTextWriter writer)
{
writer.AddAttribute("name",UniqueID);
writer.AddAttribute("src",ImageUrl);
writer.AddAttribute("onClick",
Page.ClientScript.GetPostBackEventReference(
this, String.Empty));
}
public event EventHandler ImageClicked;
public virtual bool LoadPostData(string postDataKey,
NameValueCollection postCollection)
{
String presentValue=ImageUrl;
String postedValue=postCollection[postDataKey];
if(presentValue==null
||!presentValue.Equals(postedValue))
{
ImageUrl=postedValue;
return true;
}
return false;
}
public virtual void RaisePostDataChangedEvent()
{
OnImageClicked(new EventArgs());
}
protected virtual void OnImageClicked(EventArgs e)
{
if(ImageClicked!=null)
{
ImageClicked(this, e);
}
}
}
}