当前位置:首页 > .net组件开发系列(四)之事件处理机制 三个接口两个方法

.net组件开发系列(四)之事件处理机制 三个接口两个方法

点击次数:1194  更新日期:2010-12-29
\n

这两天我想了想,还是改成.net控件开发系列好一点吧。最起码是顺口一点。好了,废话不多说,进入正题吧,我们如果要作为一个控件开发人员,必须考虑事件处理机制,然而在事件处理机制需要解决两大难题,一是服务器如何接收到回传的事件,二个通过post提交的表单回传到服务器后,服务器做了什么。带着这两个问题,进入我们的正题吧。


\n

为了解决以上问题,.net 2.0提供了两个重要接口:IPostBackEventHandler,IPostBackDataHandler,而且在.net增强了回调处理方面功能,如ICallbackEventHandler接口(详细第二节 开发ajax控件),GetCallbackEventReference方法,GetPostBackClientHyperlink方法等等,


\n

我们来详述这二种接口二种方法种方法(那个ICallbackEventHandler在第二章已讲述了,所以现在讲述其余四个).跟我走吧。


\n

第一个:IPostBackEventHandler,该接口用于处理由客户端引发的页面回传事件,实现此接口,服务器控件可将客户端的提交表单事件对应到服务器的事件上。允许页面开发者通过事件处理程序去调用服务器事个把来处理客户端事件。例如。页面上有一个button,当我们按下button时,它就要服务器端触发了一个Click事件。 为了开发能触发事件响应回传的控件,我们需要实现此接口System.Web.UI.IPostBackEventHandler 接口。


\n

该接口表述如下:


\n


\n

Public interface IPostBackEventHandler{


\n

void RaisePostBackEvent(string eventArgument);


\n

}


\n


\n

此参数eventArgument表示要传递到事件处理程序的可选事件参数,eventArgument 参数的值传递给实现IPostBackEventHandler接口的控件的 RaisePostBackEvent 方法。此控件还会呈现导致发生回发的 HTML 元素。如果控件呈现了用于回发的客户端脚本,则会在 eventArgument 参数中传递脚本中的参数。如果回发是由简单提交所引起的,则 eventArgument 参数为空引用.


\n

示例一:


\n

控件代码:


\n

控件代码
1using System;
2using System.Data;
3using System.Configuration;
4using System.Web;
5using System.Web.Security;
6using System.Web.UI;
7using System.Web.UI.WebControls;
8using System.Web.UI.WebControls.WebParts;
9using System.Web.UI.HtmlControls;
10using System.ComponentModel;
11/**//// <summary>
12/// IPostBackEventHandler示例
13/// </summary>
14///
15namespace cnblogs.suiqirui
16{
17 [DefaultEvent("Click")]
18 [DefaultProperty("Text")]
19 public class buttonevent : WebControl,IPostBackEventHandler
20 {
21 public event EventHandler Click;
22 [Bindable(true)]
23
24
25 public virtual string Text {
26
27 get {
28 string s = (string)ViewState["Text"];
29 return ((s==null)?”":s);
30
31 }
32 set {
33
34 ViewState["Text"] = value;
35 }
36
37 }
38 protected override HtmlTextWriterTag TagKey
39 {
40 get
41 {
42 return HtmlTextWriterTag.Input;
43 }
44 }
45 protected override void AddAttributesToRender(HtmlTextWriter writer)
46 {
47 base.AddAttributesToRender(writer);
48 writer.AddAttribute(“Name”, this.UniqueID);
49 writer.AddAttribute(“Type”, “submit”);
50 writer.AddAttribute(HtmlTextWriterAttribute.Value, this.Text);
51 }
52 public void RaisePostBackEvent(string eventArgument)
53 {
54
55 onClick(new EventArgs());
56 }
57
58 protected virtual void onClick(EventArgs e)
59 {
60
61 if (Click != null)
62 {Click(this, e); }
63 }
64 protected override void Render(HtmlTextWriter writer)
65 {
66 if (Page != null)
67 {
68 Page.VerifyRenderingInServerForm(this); //确保此控件在form表单内
69 }
70 base.Render(writer);
71 }
72 public buttonevent()
73 {
74 }
75 }
76}


\n

(必须要有writer.AddAttribute(“Name”, this.UniqueID);。不然不会触发事件,我们下文将有一个没有用到UniqueID的,将与之比比)


\n

Aspx代码:


\n

1<%@ Page Language=”C#” AutoEventWireup=”true” CodeFile=”Default.aspx.cs” Inherits=”_Default” %>
2<%@ Register Namespace=”cnblogs.suiqirui” TagPrefix=”but” %>
3<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
4
5<html xmlns=”http://www.w3.org/1999/xhtml” >
6<head runat=”server”>
7 <title>无标题页</title>
8 <script runat=server>
9
10 protected void but_Click(object sender, EventArgs e)
11 {
12 Response.Write(“你触发了按钮”);
13
14 }
15 </script>
16</head>
17<body>
18 <form id=”form1″ runat=”server”>
19 <div>
20 <but:buttonevent runat=server ID=”but” Text=”click me” onClick=”but_Click”/>
21
22 </div>
23 </form>
24</body>
25</html>
26


\n


我只是上述控件例了,为了说明IPostBackEventHandler应用场合,但上述例子中的事件处理没有得到优化,因为它为每个委托实例生成一个字段,当控件上定义了很多事件时,就会增加存储成本,基类 System.Web.UI.Control 为存储和检索事件委托提供了更有效的数据结构(通过其 Events 属性)。Events 属性的类型为 EventHandlerList,该类型是为有效存储和检索事件委托而设计的数据结构;我把优化了的事件处理贴上来,看看:


\n

代化事件后
1using System;
2using System.Data;
3using System.Configuration;
4using System.Web;
5using System.Web.Security;
6using System.Web.UI;
7using System.Web.UI.WebControls;
8using System.Web.UI.WebControls.WebParts;
9using System.Web.UI.HtmlControls;
10using System.ComponentModel;
11/// <summary>
12/// IPostBackEventHandler示例
13/// </summary>
14///
15namespace cnblogs.suiqirui
16{
17 [DefaultEvent("Click")]
18 [DefaultProperty("Text")]
19 public class buttonevent1 : WebControl, IPostBackEventHandler
20 {
21 private static readonly object ClickEvent = new object();
22 public event EventHandler Click
23 {
24
25 add
26 {
27
28 Events.AddHandler(ClickEvent, value);
29 }
30 remove
31 {
32 Events.AddHandler(ClickEvent, value);
33 }
34 }
35 [Bindable(true)]
36
37
38 public virtual string Text
39 {
40
41 get
42 {
43 string s = (string)ViewState["Text"];
44 return ((s == null) ? “” : s);
45
46 }
47 set
48 {
49
50 ViewState["Text"] = value;
51 }
52
53 }
54 protected override HtmlTextWriterTag TagKey
55 {
56 get
57 {
58 return HtmlTextWriterTag.Input;
59 }
60 }
61 protected override void AddAttributesToRender(HtmlTextWriter writer)
62 {
63 base.AddAttributesToRender(writer);
64 writer.AddAttribute(“Name”, this.UniqueID);
65 writer.AddAttribute(“Type”, “submit”);
66 writer.AddAttribute(HtmlTextWriterAttribute.Value, this.Text);
67 }
68 public void RaisePostBackEvent(string eventArgument)
69 {
70
71 onClick(new EventArgs());
72 }
73
74 protected virtual void onClick(EventArgs e)
75 {
76 EventHandler clickEventDel = Events[ClickEvent] as EventHandler;
77 if (clickEventDel != null)
78 { clickEventDel(this, e); }
79 }
80 protected override void Render(HtmlTextWriter writer)
81 {
82 if (Page != null)
83 {
84 Page.VerifyRenderingInServerForm(this); //确保此控件在form表单内
85 }
86 base.Render(writer);
87 }
88 public buttonevent1()
89 {
90 }
91 }
92}


\n

到此为此,讲完了第一个事件处理IPostBackEventHandler


\n

第二个:IPostBackDataHandler 该接口用于处理客户端引发的页面回传的事件,实现此接口,服务器可将客户端的提交表单事件对应到服务器的事件上,并且通过事件上,并且通过事件处理程序完成对该客户端事个把的处理. 如果希望设计的服务器控件检查客户端回发到服务器的窗体数据,则必须实现 IPostBackDataHandler 接口。该接口定义的合同允许服务器控件确定其状态是否在回发发生后更改,并引发适当的事件 该接口有两个方法.


\n

该接口表述如下:

\n

1Public interface IPostBackDataHandler{
2
3bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection);// 为 ASP.NET 服务器控件处理回发数据。
4
5void RaisePostDataChangedEvent();//要求服务器控件对象通知 ASP.NET 应用程序该控件的状态已更改
6
7}
8


\n

LoadPostData方法。如果服务器控件的状态在回发发生后更改,则为 true;否则为 false


\n

示例:

\n

LoadPostData示例
public virtual bool LoadPostData(string postDataKey,


\n

NameValueCollection postCollection) {

\n

String presentValue = Text;


\n

String postedValue = postCollection[postDataKey];

\n

if (presentValue == null || !presentValue.Equals(postedValue)){


\n

Text = postedValue;


\n

return true;


\n

}


\n

return false;


\n

}

\n

RaisePostDataChangedEvent实现该接口的服务器控件的更改事件(如果有)从此方法引发。


\n

示例:

\n

public virtual void RaisePostDataChangedEvent() {


\n

OnTextChanged(EventArgs.Empty);


\n

}

\n

控件代码:


\n

控件示例
1using System;
2using System.Web;
3using System.Web.UI;
4using System.Collections;
5using System.Collections.Specialized;
6
7
8namespace CustomWebFormsControls {
9
10 [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]
11 public class MyTextBox: Control, IPostBackDataHandler {
12
13
14 public String Text {
15 get {
16 return (String) ViewState["Text"];
17 }
18
19 set {
20 ViewState["Text"] = value;
21 }
22 }
23
24
25 public event EventHandler TextChanged;
26
27
28 public virtual bool LoadPostData(string postDataKey,
29 NameValueCollection postCollection) {
30
31 String presentValue = Text;
32 String postedValue = postCollection[postDataKey];
33
34 if (presentValue == null || !presentValue.Equals(postedValue)) {
35 Text = postedValue;
36 return true;
37 }
38
39 return false;
40 }
41
42
43 public virtual void RaisePostDataChangedEvent() {
44 OnTextChanged(EventArgs.Empty);
45 }
46
47
48 protected virtual void OnTextChanged(EventArgs e) {
49 if (TextChanged != null)
50 TextChanged(this,e);
51 }
52
53
54 protected override void Render(HtmlTextWriter output) {
55 output.Write(“<INPUT type= text name = “+this.UniqueID
56 + ” value = ” + this.Text + ” >”);
57 }
58 }
59}
60

\n

我们详细说一下下面的两个方法:

\n

第三个: GetPostBackClientHyperlink,该方法将由页面Page调用,用于产生客户端脚本,从超链接中引起回传,为了说明其执行回传功能,我们来创建军一个自定义控件(LinkButton_Demo),这个控件生成一个超链接,在点击链接时,将表单上传到服务器端.:


\n

示例:


\n

控件代码:


\n

GetPostBackClientHyperlink 方法
1using System;
2using System.Data;
3using System.Configuration;
4using System.Web;
5using System.Web.Security;
6using System.Web.UI;
7using System.Web.UI.WebControls;
8using System.Web.UI.WebControls.WebParts;
9using System.Web.UI.HtmlControls;
10using System.ComponentModel;
11/**//// <summary>
12/// GetPostBackClientHyperlink 用方法
13/// </summary>
14///
15namespace cnblogs.sui
16{
17
18 [DefaultEvent("Click"),DefaultProperty("Text")]
19 public class LinkButton_demo : WebControl,IPostBackEventHandler
20 {
21 private static readonly object EventClick = new object();
22 [Bindable(true), Category("Behavior")]
23 public virtual string Text {
24
25 get {
26
27 string s = (string)ViewState["Text"];
28 return ((s == null) ? String.Empty : s);
29 }
30 set {
31
32 ViewState["Text"] = value;
33 }
34 }
35 protected override HtmlTextWriterTag TagKey
36 {
37 get
38 {
39 return HtmlTextWriterTag.A;
40 }
41 }
42 public event EventHandler Click {
43
44 add{
45 Events.AddHandler(EventClick, value);
46
47
48 }
49 remove {
50 Events.AddHandler(EventClick, value);
51 }
52 }
53 public void RaisePostBackEvent(string eventArgument) {
54 onClick(EventArgs.Empty);
55 }
56 protected override void AddAttributesToRender(HtmlTextWriter writer)
57 {
58
59 base.AddAttributesToRender(writer);
60 writer.AddAttribute(HtmlTextWriterAttribute.Href,Page.GetPostBackClientHyperlink(this,string.Empty));//注意这里
61
62 }
63 protected virtual void onClick(EventArgs e)
64 {
65
66 EventHandler clickHandler = (EventHandler)Events[EventClick];
67 if (clickHandler != null)
68 {
69 clickHandler(this, e);
70 }
71 }
72 protected override void Render(HtmlTextWriter writer)
73 {
74 if (Page != null)
75 {
76 Page.VerifyRenderingInServerForm(this);
77 }
78 base.Render(writer);
79 }
80 protected override void RenderContents(HtmlTextWriter writer)
81 {
82 writer.Write(Text);
83 //base.RenderContents(writer);
84 }
85 public LinkButton_demo()
86 {
87 //
88 // TOD 在此处添加构造函数逻辑
89 //
90 }
91 }
92}


\n

Aspx页面:


\n

1<%@ Page Language=”C#” AutoEventWireup=”true” CodeFile=”Default2.aspx.cs” Inherits=”Default2″ %>
2<%@ Register Namespace=”cnblogs.sui” TagPrefix=”ss” %>
3<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
4
5<html xmlns=”http://www.w3.org/1999/xhtml” >
6<head runat=”server”>
7 <title>无标题页</title>
8 <script runat=server>
9 protected void tt(object sender, EventArgs e)
10 {
11
12 Response.Write(“sender ..”);
13 }
14
15 </script>
16</head>
17<body>
18 <form id=”form1″ runat=”server”>
19 <div>
20 <ss:LinkButton_demo runat=server ID=”link” onClick=”tt” Text=”click me” />
21 </div>
22 </form>
23</body>
24</html>
25


\n

结下来我们来分析其原理:


\n

先看运行结果:


\n

\n

点击click me后,打出”send….”


\n

当们查看其源代码,发现.

\n

我将看到由LinkButton_demo生成如下HTML语言。


\n

<a id=”link” href=”javascript:__doPostBack(‘link’,”)”>click me</a>


\n

在页面底部,将看到两个隐藏域和一段javascript脚本.


\n


\n

<input type=”hidden” name=”__EVENTTARGET” id=”__EVENTTARGET” value=”" />


\n

<input type=”hidden” name=”__EVENTARGUMENT” id=”__EVENTARGUMENT” value=”" />


\n

</div>


\n

<script type=”text/javascript”>


\n

<!–


\n

var theForm = document.forms[\'form1\'];


\n

if (!theForm) {


\n

theForm = document.form1;


\n

}


\n

function __doPostBack(eventTarget, eventArgument) {


\n

if (!theForm.onsubmit || (theForm.onsubmit() != false)) {


\n

theForm.__EVENTTARGET.value = eventTarget;


\n

theForm.__EVENTARGUMENT.value = eventArgument;


\n

theForm.submit();


\n

}


\n

}


\n

// –>


\n

</script>


\n


现在分析一下是如何提供之些元素,我们重新来看看控件代码。我们在AddAttributes中有这么一段代码。将看到下面的调用:

\n

Writer.AddAttribute(HtmlTextWriterAttribute.Href,Page.GetPostBackClientHyperlink(this,string.Empty));


\n

GetPostBackClientHyperlink方法主要实现两个功能,


\n

GetPostBackClientHyperlink返回一个字符串” javascript:__doPostBack(‘link’,”)”,这个由页面生成到__doPostBack javascript函数的调用。参数值:以一个UniqueID和一个当控件生成多重回传元素时采用的可选字符串参数(我们将在下一个方法[GetCallbackEventReference]中来讲述)
GetPostBackClientHyperlink将告诉页面去生成两个隐藏域(__EVENTTARGET, __EVENTARGUMENT),以及引起回传(__doPostBack)的javascript函数,
当我们点了超链接后,发生了什么呢?


\n

其实,当点击后,Javascript 函数__doPostBack被调用。这个函数把控件的UniqueID值给了__EVENTTARGET隐藏域中,把事件参数赋给了__EVENTARGUMENT隐藏域并回传表单,在服务器端,将读取过来的表单数据中的__EVENTTARGET变量,并调用控件的RaisePostBackEvent方法,此方法的UniqueID的值与__EVENTTARGET变量的值相匹配,此外,页面把表单发送数据中的__EVENTARGUMENT变量的值作为参数传给RaisePostBackEvent方法。不知道大家注意到一个细节没有,我们在这个LinkButton_demo控件中,没有给它附上UniqueID,而在我们那IPostBackEventHandler这个接口中举的那个例子,一定要用到name(id)给上一个UniqueID值,那是因为LinkButton_demo它会在HTML标签上自动生成作为name属性的UniqueID.


\n

当我们生成的是一个表单元素不是超链接时,我们就要用到GetPostBackEventReference


\n

第四个:GetPostBackEventReference


\n

因为实现原理与GetPostBackClientHyperlink差不多,所以直接写个例子大家看看吧。


\n

控件代码:
1using System;
2using System.Data;
3using System.Configuration;
4using System.Web;
5using System.Web.Security;
6using System.Web.UI;
7using System.Web.UI.WebControls;
8using System.Web.UI.WebControls.WebParts;
9using System.Web.UI.HtmlControls;
10using System.ComponentModel;
11/**//// <summary>
12/// GetPostBackEventReference 的使用
13/// </summary>
14///
15namespace cnblogs.suiqi
16{
17 [DefaultEvent("ClickN")]
18 [DefaultProperty("NextT")]
19
20 public class EventRef : WebControl,IPostBackEventHandler
21 {
22 private static readonly object EventClickN = new object();
23 private static readonly object EventClickP = new object();
24 [Bindable(true)]
25 public virtual string NextT {
26
27 get {
28
29 string s = (string)ViewState["NextT"];
30 return ((s == null) ? String.Empty : s);
31 }
32 set {
33 ViewState["NextT"] = value;
34 }
35 }
36 public virtual string PreviousT
37 {
38 get {
39
40 string s = (string)ViewState["PreviousT"];
41 return ((s == null) ? String.Empty : s);
42 }
43 set
44 {
45 ViewState["PreviousT"] = value;
46 }
47 }
48 public event EventHandler ClickN
49 {
50 add {
51 Events.AddHandler(EventClickN, value);
52 }
53
54 remove{
55 Events.AddHandler(EventClickN, value);
56 }
57 }
58 public event EventHandler ClickP
59 {
60 add
61 {
62 Events.AddHandler(EventClickP, value);
63 }
64
65 remove
66 {
67 Events.AddHandler(EventClickP, value);
68 }
69 }
70 protected virtual void onClickN(EventArgs e) {
71
72 EventHandler clickNextHandler = (EventHandler)Events[EventClickN];
73 if (clickNextHandler !=null)
74 {
75 clickNextHandler(this, e);
76 }
77 }
78 protected virtual void onClickP(EventArgs e)
79 {
80
81 EventHandler clickPreviousHandler = (EventHandler)Events[EventClickP];
82 if (clickPreviousHandler != null)
83 {
84 clickPreviousHandler(this, e);
85 }
86 }
87 public void RaisePostBackEvent(string eventArgument) {
88
89 if (eventArgument == “Previous”)
90 {
91 onClickP(new EventArgs());
92 }
93 else if (eventArgument == “Next”)
94 {
95 onClickN(EventArgs.Empty);
96 }
97
98 }
99 protected override void Render(HtmlTextWriter writer)
100 {
101 if (Page != null)
102 {
103 base.Page.VerifyRenderingInServerForm(this);
104 }
105 base.Render(writer);
106 }
107 protected override void AddAttributesToRender(HtmlTextWriter writer)
108 {
109 writer.AddAttribute(HtmlTextWriterAttribute.onclick, Page.GetPostBackEventReference(this, “Previous”));//注意这里,
110 writer.AddAttribute(“language”, “javascript”);
111 writer.RenderBeginTag(HtmlTextWriterTag.Button);
112 writer.Write(this.PreviousT);
113 writer.RenderEndTag();
114 writer.AddAttribute(HtmlTextWriterAttribute.onclick, Page.GetPostBackEventReference(this, “Next”));//注意这里
115 writer.AddAttribute(“language”, “javascript”);
116 writer.RenderBeginTag(HtmlTextWriterTag.Button);
117 writer.Write(this.NextT);
118 writer.RenderEndTag();
119
120
121 base.AddAttributesToRender(writer);
122 }
123 public EventRef()
124 {
125 //
126 // TOD 在此处添加构造函数逻辑
127 //
128 }
129 }
130}


\n

示例代码:


\n

1<%@ Page Language=”C#” AutoEventWireup=”true” CodeFile=”Default3.aspx.cs” Inherits=”Default3″ %>
2<%@ Register Namespace=”cnblogs.suiqi” TagPrefix=”cn” %>
3<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
4
5<html xmlns=”http://www.w3.org/1999/xhtml” >
6<head runat=”server”>
7 <title>无标题页</title>
8 <script runat=server>
9
10 protected void NextClick(object sender, EventArgs e)
11 {
12 Response.Write(“you press Next button: “+this.eve.NextT);
13 }
14 protected void PreClick(object sender, EventArgs e)
15 {
16 Response.Write(“you press Previous button: “+this.eve.PreviousT);
17 }
18 </script>
19</head>
20<body>
21 <form id=”form1″ runat=”server”>
22 <div>
23 <cn:EventRef runat=server ID=”eve” NextT=”后一下” PreviousT=”前一个” onClickN=”NextClick” onClickP=”PreClick”/>
24
25 </div>
26 </form>
27</body>
28</html>
29


\n

最后我们看看生成后的html:


\n

发现有如下代码:


\n

<button onclick=”__doPostBack(‘eve’,\'Previous’)” language=”javascript”>前一个</button><button onclick=”__doPostBack(‘eve’,\'Next’)” language=”javascript”>后一下</button>


\n

其中的__doPostBack(‘eve’,\'Previous’),这个就说明了第二个参数的作用,当控件生成多重回传元素时,采用的可选字符串参数

\n