.net组件开发系列(二)之武林系列 太极拳 开发ajax控件.
A.开篇:
在如今的web开发中,ajax以一个旧瓶新装的技术,以xml,javascript,XmlHttp为基础变新而来的。正如如今的太极拳,用得相当的广。太极拳是中国武苑中的奇葩异卉,是中华之瑰宝,经历数百年沧桑源远流长,逐渐演变成陈、杨、武、吴、孙、和等诸多流派。太极拳集技击、强体、医身、益智和修性为一体,蕴藏着东方哲学之神韵,正在为越来越多的所认识,而ajax也如太极一样,越来越多的开发人员去追逐。
我们都知道在.net 2.0中新增了ajax控件,但我们也想开发自已的ajax控件,我们先先看看编写ajax控件的知识吧。
开发ajax 控件不得不了解
A. ICallbackEventHandler 接口
该接口用于指示控件可以作为服务器的回调事件的目标。ICallbackEventHandler 接口的控件为目标时,将把事件变量作为参数传递来调用 RaiseCallbackEvent 方法以处理该事件,并且 GetCallbackResult 方法返回回调的结果。继承这个接口需要实现两个方法: RaiseCallbackEvent,GetCallbackResult.
>> RaiseCallbackEvent
用于处理客户端提交的请求。它接收一个string类型的参数,
格式:
protected
void
RaiseCallbackEvent(
string
eventArgument)
{
//do something
}
>>GetCallbackResult负责把服务器的处理结果返回到客户端
格式
protected
void
GetCallbackResult()
{
return ### //返回服务端数据
}
B.GetCallbackEventReference方法
用天向服务器端发送回调节器请求的函数。语法:
public
string
GetCallbackEventReference(
Control control,
string
argument
string
clientCallback
string
context
string
clientErrorCallback
bool
useAsync
)
参数
control
处理客户端回调的服务器 Control。该控件必须实现 ICallbackEventHandler 接口并提供 RaiseCallbackEvent 方法。
argument 从客户端脚本传递给服务器端的一个参数
clientCallback
一个客户端事件处理程序的名称,该处理程序接收成功的服务器端事件的结果
context
启动回调之前在客户端计算的客户端脚本。脚本的结果传回客户端事件处理程序
clientErrorCallback
客户端事件处理程序的名称,该处理程序在服务器端事件处理程序出现错误时接收结果
useAsync
true 表示同步执行回调 false 表示异步执行回调
返回值
调用客户端回调的客户端函数的名称。
介绍完基础知识后。
开发一个ajax的Text控件,检查用户名是否可用 的控件。
B。正题:
我来开发一个ajax控件.命名为(AjaxText)
先看效果,再分析控件如何工作,再分段解析代码,再贴出完整代码,最后贴出示例代码
1。先看效果:
这个红框就是我们自定义的ajaxText控件.
请注意下列图红色箭头,就是当我们用户名这个文本框失去焦点时,
就异步去检查用户名是否存在。
我们来先看看用户名存在的情况:
点注册
我们来先看看用户名不存在的情况:可以注册
点注册。
我们做成 这样一个控件后,我们就不必去写xmlhttp了,
只要一拖这个控件,写上少量代码(说白了就是传参了)
2。该控件工作原理.
该控件从webcontrol继承,并实现了INameContainer,还实现了ICallbackEventHandler接口以便获得回调支持。
大家可以注意到,我们并没有去实现数据回发事件接口(IPostBackDataHandler),我们这里用到的是异步回调,可以不去实现此接口,
关于此接口在后述文章中会谈到。
按从客户端到服务端来解析工作原理
从注册到页面上的脚本讲起:
当然需要在客户端本中发出一个调用.
下面的控件代码中:
string
callbackScript
=
Page.ClientScript.GetCallbackEventReference(
this
,
"
this.value
"
, ClientCallBackScript,
null
);
前面的这条"Page.ClientScript.GetCallbackEventReference(this, "this.value", ClientCallBackScript, null);在运行时会被解析成什么样子呢?我们只要在页面运行时察看页面源码就可以看到,实际上服务器帮我们生成了下面这段 script代码:
WebForm_DoCallback(
'
__Page
'
,arg,ReceiveServerData,context,
null
,
false
);
这段代码是什么意思呢?很显然的他调用了一个系统与定义的script函数:WebForm_DoCallback。我们要把这个函数找出来看看它具体 为我们干了什么。在运行时的页面源码中,我们很容易可以找到这段脚本的出处。我们注意到有一个
<
script src
=
"
/ajaxTextDemo1/WebResource.axd?d=9QMEhP1JOTWr2B3RVTrtnA2&t=633255343980000000
"
type
=
"
text/javascript
"
></
script
>
就是调用下图的WebForm_DoCallback
好的,我们说完了客户端,我就来讲讲服务端给我们做了什么
实现ICallbackEventHandler,则会在客户端页面发现如下一代码
<script src="/ajaxTextDemo1/WebResource.axd?d=9QMEhP1JOTWr2B3RVTrtnA2&t=633255343980000000" type="text/javascript"></script>
这个代码,眼熟吧,我们开发ajax时,将ScriptManage拖到页面后,在浏览器源代码中发现也有这个脚本吧。
如果有兴趣的,可以去研究研究它。
从前面的基础知识是我们知道了它有GetCallbackResult()和void RaiseCallbackEvent(eventArgument)这两个方法。
根据MSDN的文档,我们知道,在一个callback被post到服务端时, Page将会首先将post回来的form data绑定到当前页面的服务端web控件,接着判断本次post是callback还是postback,如果是postpost,那么自然是原来的那 个机制;
如果是callback,则将回调用触发本次callback的控件的RaiseCallbackEvent (eventArgument),当然,eventArgument也将会正确的传过来,
在这个函数的实现代码里我们可以对这个参数进行解析处理,并在某 个地方,存储我们准备返回的数据,或者待处理的已经被解析出来的参数。
接着,系统将调用string GetCallbackResult(),在这个函数的实现代码中,我们可以直接返回我们在RaiseCallback函数中存储的准备返回的数据,或者 根据待处理的已经被解析出来的参数处理这些参数,并返回结果。这个返回的字符串,自然将以脚本的形式被render回客户端。
3。分析示例代码:
3.1我们先申明一个包含事件处理的派生类,因为我们要把AjaxText控件的Text属性写入其中,为什么要这样呢。其实,我们用的是异步回调中的自定义事件,如下下例中的TextChange事件,我想把this.控件ID.Text属性取出来,去对这个text的值去操作,发现text的值取不出来,后面就想到了把text的值写入到事件处理数据类中,也就是 这段代码。
public
class
TextChangedEventArgs : EventArgs
{
public TextChangedEventArgs()
{
Text = "";
}
public TextChangedEventArgs(string _Text)
{
Text = _Text;
}
private string Text = "";
public string TextValue
{
get { return Text; }
}
}
3.2我们定义的一些属性。如Text(string),ReturnString(string),IsValid(bool),ClientCallBackScript(string)
注意其中的两个属性:一个是IsValid,一个是ClientCallBackScript,那个IsValid是返回给客户端的值,又将它转成字符串给了ReturnString,因为下面这个方法中GetCallbackResult()
要求返回字符串。
IsValid的另一个重要作用在于,在客户端脚本中对于进行判定:例:
<
script
>
function
GetCallbackData(res)
{
if
(res
==
"
True
"
)
{
document.getElementById(
"
Label1
"
).innerHTML
=
"
<font color=blue>成功</font>
"
;
document.getElementById(
"
hi
"
).value
=
"
1
"
;
}
else
{
document.getElementById(
"
Label1
"
).innerHTML
=
"
<font color=blue>该用户已存在</font>
"
;
document.getElementById(
"
hi
"
).value
=
"
0
"
;
}
}
</
script
>
那个ClientCallBackScript属性,非常重要,它是指向哪个js将被调用,并由GetCallbackEventReference去调用。
如:
string callbackScript = Page.ClientScript.GetCallbackEventReference(this, "this.value", ClientCallBackScript, null);
页面中
<txt:ajaxText runat=server ID=aj ClientCallBackScript=GetCallbackData ...
指向正是上例中的function GetCallbackData(res)
3.3 Render方法
Render 方法将Web 控件发送到指定的HtmlTextWriter 实例。重写此方法以将自定义服务器控件发送到客户端
这个方法在后述中会讲到。
3。4 事件与委托。
定义了TextChanged事件,TextChangedEventHandler委托
1
public
event
TextChangedEventHandler TextChanged
2
{
3
add
4
{
5
6
Events.AddHandler(eventTextChanged, value);
7
}
8
remove
9
{
10
11
Events.RemoveHandler(eventTextChanged, value);
12
}
13
}
14
15
定义了一个TextChanged事件,而事件发生的时候只能用TextChangedHandler这个委托来做的。
把委托都存放在了一个EventHandlerList中,因此此处你可以看到add与remove,
这是访问器的声明,用于添加或移除客户代码中的事件处理程序,这样做的好处是公开大量的事件但不为每个事件分配字段,而是使用EventHandlerList存储这些事件例
关于事件与委托,事件与委托详见上篇
http://www.cnblogs.com/suiqirui19872005/archive/2007/10/12/922313.html
3.5 定义一个方法:
protected
virtual
void
OnTextChanged(
object
sender, TextChangedEventArgs e)
{
TextChangedEventHandler handler
=
Events[eventTextChanged]
as
TextChangedEventHandler;
if
(handler
!=
null
)
{
Text
=
e.TextValue;
handler(
this
, e);
}
}
当们重写这个方法时,将会激发TextChanged事件。并将事件处理类中的TextValue属性。附加上去。
3。6 实现ICallbackEventHandler接口
接口的两个方法。
public
void
RaiseCallbackEvent(
string
eventArgument)
{
TextChangedEventArgs args
=
new
TextChangedEventArgs(eventArgument);
OnTextChanged(
this
, args);
}
public
string
GetCallbackResult()
{
return
ReturnString;
}
3。7静态构造器
static
ajaxText()
{
eventTextChanged = new object();
}
一个给定的类(或结构)只能定义一个static构造器
无论生成多少个实例,一个类的static构造器只执行一次
不能为static构造器指定访问修饰符,不能带有任何参数
static构造器在程序员创建第一个实例的时候,在头一次访问static成员之前被调用
static构造器在所有实例级别的构造器之前执行
4。控件代码:
完整的控件代码
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;
11using System.Collections.Specialized;
12/**//// <summary>
13/// ajaxText 的摘要说明
14/// </summary>
15///
16namespace cnblogs.suiqirui
17{
18 public class TextChangedEventArgs : EventArgs
19 {
20
21
22 public TextChangedEventArgs()
23 {
24 Text = "";
25
26 }
27
28 public TextChangedEventArgs(string _Text)
29 {
30 Text = _Text;
31
32 }
33 private string Text = "";
34 public string TextValue
35 {
36 get { return Text; }
37 }
38 }
39 [DefaultEvent("TextChanged")]
40 [ToolboxData("<{0}:ajaxtext runat=server></{0:ajaxtext>")]
41 public class ajaxText : WebControl, ICallbackEventHandler
42 {
43
44 private string returnstring;
45 public delegate void TextChangedEventHandler(object sender, TextChangedEventArgs e);//申明一个委托。
46 private static readonly object eventTextChanged;
47
48 public event TextChangedEventHandler TextChanged
49 {
50 add
51 {
52
53 Events.AddHandler(eventTextChanged, value);
54 }
55 remove
56 {
57
58 Events.RemoveHandler(eventTextChanged, value);
59 }
60 }
61
62 static ajaxText()
63 {
64 eventTextChanged = new object();
65 }
66 属性列表#region 属性列表
67 [Description("得到或者设置一个text值")]
68
69 public string Text
70 {
71 get
72 {
73
74 object o = ViewState["Text"];
75 return o == null ? "" : (string)o;
76 }
77 set
78 {
79
80 ViewState["Text"] = value;
81 }
82
83
84 }
85
86 private string ReturnString
87 {
88 get
89 {
90 return (string)ViewState["ReturnString"];
91
92 }
93 set
94 {
95
96 ViewState["ReturnString"] = value;
97 }
98
99
100 }
101 public bool IsValid
102 {
103
104 get
105 {
106
107 object o = ViewState["IsValid"];
108 return o == null ? false : (bool)o;
109 }
110 set
111 {
112
113 ViewState["IsValid"] = value;
114
115 }
116 }
117
118 [Description("设置或获取客户端脚本名字")]
119 public string ClientCallBackScript
120 {
121
122 get
123 {
124
125 object o = ViewState["ClientCallBackScript"];
126 return o == null ? "null" : o.ToString();
127 }
128 set
129 {
130
131 ViewState["ClientCallBackScript"] = value;
132 }
133 }
134
135
136 #endregion
137
138
139
140 public string GetCallbackResult()
141 {
142
143 return ReturnString;
144 }
145 protected override void Render(HtmlTextWriter writer)
146 {
147 if (base.Page == null)
148 {
149 base.Page.VerifyRenderingInServerForm(this);
150 }
151 string callbackScript = Page.ClientScript.GetCallbackEventReference(this, "this.value", ClientCallBackScript, null);
152
153 // writer.WriteBeginTag("input");
154
155 writer.AddAttribute("onblur", callbackScript);
156 writer.Write("<INPUT type=\"text\" name=\"" + this.UniqueID + "\" onblur=\"" + callbackScript);
157 writer.Write("\" value=\"" + this.Text + "\" />");
158 base.Render(writer);
159
160
161 }
162
163 public void RaiseCallbackEvent(string eventArgument)
164 {
165
166
167 TextChangedEventArgs args = new TextChangedEventArgs(eventArgument);
168
169
170 OnTextChanged(this, args);
171 ReturnString = Convert.ToString(IsValid);
172
173
174
175
176 }
177
178 protected virtual void OnTextChanged(object sender, TextChangedEventArgs e)
179 {
180 TextChangedEventHandler handler = Events[eventTextChanged] as TextChangedEventHandler;
181 if (handler != null)
182 {
183 Text = e.TextValue;
184 handler(this, e);
185
186 }
187 }
188 public ajaxText()
189 {
190 //
191 // TODO: 在此处添加构造函数逻辑
192 //
193 }
194 }
195}
196
197
5。示例代码:
aspx页面:
<%
@ Page Language
=
"
C#
"
AutoEventWireup
=
"
true
"
CodeFile
=
"
Default.aspx.cs
"
Inherits
=
"
_Default
"
%>
<%
@ Register Namespace
=
"
cnblogs.suiqirui
"
TagPrefix
=
"
txt
"
%>
<!
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>
<
html
xmlns
="http://www.w3.org/1999/xhtml"
>
<
head
runat
="server"
>
<
title
>
无标题页
</
title
>
</
head
>
<
body
>
<
form
id
="form1"
runat
="server"
>
<
div
>
<
input
type
=hidden
id
=hi
runat
="server"
/>
用户名:
<
txt:ajaxText
runat
=server
ID
=aj
ClientCallBackScript
=GetCallbackData
EnableViewState
=true
OnTextChanged
="aj_TextChanged"
/><
asp:label
runat
=server
ID
="Label1"
></
asp:label
><
br
/>
密码:
<
asp:TextBox
ID
="TextBox1"
runat
="server"
></
asp:TextBox
>
<
br
/>
<
asp:Button
ID
="Button1"
runat
="server"
Text
=" 注册"
OnClick
="Button1_Click"
Width
="216px"
/></
div
>
<
script
>
function
GetCallbackData(res)
{
if
(res
==
"
True
"
)
{
document.getElementById(
"
Label1
"
).innerHTML
=
"
<font color=blue>成功</font>
"
;
document.getElementById(
"
hi
"
).value
=
"
1
"
;
}
else
{
document.getElementById(
"
Label1
"
).innerHTML
=
"
<font color=blue>该用户已存在</font>
"
;
document.getElementById(
"
hi
"
).value
=
"
0
"
;
}
}
</
script
>
</
form
>
</
body
>
</
html
>
cs页面:
using
System;
using
System.Data;
using
System.Configuration;
using
System.Web;
using
System.Web.Security;
using
System.Web.UI;
using
System.Web.UI.WebControls;
using
System.Web.UI.WebControls.WebParts;
using
System.Web.UI.HtmlControls;
public
partial
class
_Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ViewState["valid"] = Request["hi"];
}
protected void aj_TextChanged(object sender, cnblogs.suiqirui.TextChangedEventArgs e)
{
//模似用户aa
if (e.TextValue == "aa")
{
this.aj.IsValid = false;
}
else
{
this.aj.IsValid = true;
}
}
protected void Button1_Click(object sender, EventArgs e)
{
string s = (string)ViewState["valid"];
if (s == "0")
{
Response.Write(" 不能进行提交,因为未通过验证");
}
else {
Response.Write("可以正常注册了,我们就可以进行数据库操作了");
}
}
}
控件代码:见上
上述那个(4.0)
6.结语
终于完成了,休息一下了,呵呵,过两天,就出第三系列,如有不妥的地方,请大家更正。
7.前一系列:
马步功:http://www.cnblogs.com/suiqirui19872005/archive/2007/10/12/922313.html
示例代码:/Files/suiqirui19872005/ajaxTextDemo1.rar