当前位置:首页 > .net组件开发系列(二)之武术系列-太极拳 开发ajax控件

.net组件开发系列(二)之武术系列-太极拳 开发ajax控件

点击次数:1142  更新日期:2010-12-28
\n

.net组件开发系列(二)之武林系列 太极拳 开发ajax控件.


\n

A.开篇:
在如今的web开发中,ajax以一个旧瓶新装的技术,以xml,javascript,XmlHttp为基础变新而来的。正如如今的太极拳,用得相当的广。太极拳是中国武苑中的奇葩异卉,是中华之瑰宝,经历数百年沧桑源远流长,逐渐演变成陈、杨、武、吴、孙、和等诸多流派。太极拳集技击、强体、医身、益智和修性为一体,蕴藏着东方哲学之神韵,正在为越来越多的所认识,而ajax也如太极一样,越来越多的开发人员去追逐。
我们都知道在.net 2.0中新增了ajax控件,但我们也想开发自已的ajax控件,我们先先看看编写ajax控件的知识吧。
开发ajax 控件不得不了解
A. ICallbackEventHandler 接口
该接口用于指示控件可以作为服务器的回调事件的目标。ICallbackEventHandler 接口的控件为目标时,将把事件变量作为参数传递来调用 RaiseCallbackEvent 方法以处理该事件,并且 GetCallbackResult 方法返回回调的结果。继承这个接口需要实现两个方法: RaiseCallbackEvent,GetCallbackResult.
>> RaiseCallbackEvent
用于处理客户端提交的请求。它接收一个string类型的参数,
格式:


\n


protected void RaiseCallbackEvent(string eventArgument)
{
//do something
}


\n

>>GetCallbackResult负责把服务器的处理结果返回到客户端
格式


\n


protected void GetCallbackResult()
{
return ### //返回服务端数据
}


\n

B.GetCallbackEventReference方法
用天向服务器端发送回调节器请求的函数。语法:


\n


public string GetCallbackEventReference(
Control control,
string argument
string clientCallback
string context
string clientErrorCallback
bool useAsync
)

\n


参数
control
  处理客户端回调的服务器 Control。该控件必须实现 ICallbackEventHandler 接口并提供 RaiseCallbackEvent 方法。


\n

argument   从客户端脚本传递给服务器端的一个参数


\n

clientCallback
  一个客户端事件处理程序的名称,该处理程序接收成功的服务器端事件的结果


\n

context
  启动回调之前在客户端计算的客户端脚本。脚本的结果传回客户端事件处理程序


\n

clientErrorCallback
  客户端事件处理程序的名称,该处理程序在服务器端事件处理程序出现错误时接收结果


\n

useAsync
  true 表示同步执行回调 false 表示异步执行回调


\n

返回值
  调用客户端回调的客户端函数的名称。


\n

介绍完基础知识后。
开发一个ajax的Text控件,检查用户名是否可用 的控件。


\n

B。正题:


\n

我来开发一个ajax控件.命名为(AjaxText)
先看效果,再分析控件如何工作,再分段解析代码,再贴出完整代码,最后贴出示例代码


\n

1。先看效果:



这个红框就是我们自定义的ajaxText控件.
请注意下列图红色箭头,就是当我们用户名这个文本框失去焦点时,
就异步去检查用户名是否存在。
我们来先看看用户名存在的情况:


\n



点注册


\n


\n


我们来先看看用户名不存在的情况:可以注册


\n


点注册。


\n


\n


我们做成 这样一个控件后,我们就不必去写xmlhttp了,
只要一拖这个控件,写上少量代码(说白了就是传参了)


\n


2。该控件工作原理.
该控件从webcontrol继承,并实现了INameContainer,还实现了ICallbackEventHandler接口以便获得回调支持。
大家可以注意到,我们并没有去实现数据回发事件接口(IPostBackDataHandler),我们这里用到的是异步回调,可以不去实现此接口,
关于此接口在后述文章中会谈到。


\n

按从客户端到服务端来解析工作原理


\n


从注册到页面上的脚本讲起:


\n


当然需要在客户端本中发出一个调用.
下面的控件代码中:

\n

string callbackScript = Page.ClientScript.GetCallbackEventReference(this, “this.value”, ClientCallBackScript, null);


\n

前面的这条”Page.ClientScript.GetCallbackEventReference(this, “this.value”, ClientCallBackScript, null);在运行时会被解析成什么样子呢?我们只要在页面运行时察看页面源码就可以看到,实际上服务器帮我们生成了下面这段 script代码:


\n

 WebForm_DoCallback(‘__Page’,arg,ReceiveServerData,context,null,false);


\n

   这段代码是什么意思呢?很显然的他调用了一个系统与定义的script函数:WebForm_DoCallback。我们要把这个函数找出来看看它具体 为我们干了什么。在运行时的页面源码中,我们很容易可以找到这段脚本的出处。我们注意到有一个


\n

<script src=”/ajaxTextDemo1/WebResource.axd?d=9QMEhP1JOTWr2B3RVTrtnA2&amp;t=633255343980000000″ type=”text/javascript”></script>


\n

就是调用下图的WebForm_DoCallback


\n



好的,我们说完了客户端,我就来讲讲服务端给我们做了什么
实现ICallbackEventHandler,则会在客户端页面发现如下一代码
<script src=”/ajaxTextDemo1/WebResource.axd?d=9QMEhP1JOTWr2B3RVTrtnA2&amp;t=633255343980000000″ type=”text/javascript”></script>
这个代码,眼熟吧,我们开发ajax时,将ScriptManage拖到页面后,在浏览器源代码中发现也有这个脚本吧。
如果有兴趣的,可以去研究研究它。
从前面的基础知识是我们知道了它有GetCallbackResult()和void RaiseCallbackEvent(eventArgument)这两个方法。


\n

根据MSDN的文档,我们知道,在一个callback被post到服务端时, Page将会首先将post回来的form data绑定到当前页面的服务端web控件,接着判断本次post是callback还是postback,如果是postpost,那么自然是原来的那个机制;


\n

  如果是callback,则将回调用触发本次callback的控件的RaiseCallbackEvent (eventArgument),当然,eventArgument也将会正确的传过来,
在这个函数的实现代码里我们可以对这个参数进行解析处理,并在某 个地方,存储我们准备返回的数据,或者待处理的已经被解析出来的参数。


\n

  接着,系统将调用string GetCallbackResult(),在这个函数的实现代码中,我们可以直接返回我们在RaiseCallback函数中存储的准备返回的数据,或者 根据待处理的已经被解析出来的参数处理这些参数,并返回结果。这个返回的字符串,自然将以脚本的形式被render回客户端。


\n

3。分析示例代码:
3.1我们先申明一个包含事件处理的派生类,因为我们要把AjaxText控件的Text属性写入其中,为什么要这样呢。其实,我们用的是异步回调中的自定义事件,如下下例中的TextChange事件,我想把this.控件ID.Text属性取出来,去对这个text的值去操作,发现text的值取不出来,后面就想到了把text的值写入到事件处理数据类中,也就是 这段代码。


\n


public class TextChangedEventArgs : EventArgs
{


\n


public TextChangedEventArgs()
{
Text = “”;


\n

}


\n

public TextChangedEventArgs(string _Text)
{
Text = _Text;


\n

}
private string Text = “”;
public string TextValue
{
get { return Text; }
}
}


\n


3.2我们定义的一些属性。如Text(string),ReturnString(string),IsValid(bool),ClientCallBackScript(string)
注意其中的两个属性:一个是IsValid,一个是ClientCallBackScript,那个IsValid是返回给客户端的值,又将它转成字符串给了ReturnString,因为下面这个方法中GetCallbackResult()
要求返回字符串。
IsValid的另一个重要作用在于,在客户端脚本中对于进行判定:例:


\n


<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>

\n

那个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委托


\n


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.dwww.cn/news/2007-10/200710151626125768.shtml


\n

3.5 定义一个方法:


\n

protected virtual void OnTextChanged(object sender, TextChangedEventArgs e)
{
TextChangedEventHandler handler = Events[eventTextChanged] as TextChangedEventHandler;
if (handler != null)
{
Text = e.TextValue;
handler(this, e);


\n

}
}


\n


当们重写这个方法时,将会激发TextChanged事件。并将事件处理类中的TextValue属性。附加上去。
3。6 实现ICallbackEventHandler接口
接口的两个方法。


\n



\n

public void RaiseCallbackEvent(string eventArgument)
{

TextChangedEventArgs args = new TextChangedEventArgs(eventArgument);
OnTextChanged(this, args);
}

\n

public string GetCallbackResult()
{


\n

return ReturnString;
}


\n


3。7静态构造器

\n

static ajaxText()
{
eventTextChanged = new object();
}


\n

一个给定的类(或结构)只能定义一个static构造器
无论生成多少个实例,一个类的static构造器只执行一次
不能为static构造器指定访问修饰符,不能带有任何参数
static构造器在程序员创建第一个实例的时候,在头一次访问static成员之前被调用
static构造器在所有实例级别的构造器之前执行


\n

4。控件代码:

\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;
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 // TOD 在此处添加构造函数逻辑
192 //
193 }
194 }
195}
196
197
5。示例代码:
aspx页面:


\n


<%@ 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”>


\n

<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 />
密码: &nbsp; &nbsp;<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>


\n


cs页面:


\n


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;


\n

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;
}


\n

}
protected void Button1_Click(object sender, EventArgs e)
{
string s = (string)ViewState["valid"];
if (s == “0″)
{


\n

Response.Write(” 不能进行提交,因为未通过验证”);
}
else {
Response.Write(“可以正常注册了,我们就可以进行数据库操作了”);
}
}
}


\n


控件代码:见上
上述那个(4.0)
6.结语
终于完成了,休息一下了,呵呵,过两天,就出第三系列,如有不妥的地方,请大家更正。
7.前一系列:
马步功:http://www.dwww.cn/news/2007-10/200710151626125768.shtml

示例代码:/Files/suiqirui19872005/ajaxTextDemo1.rar


\n

\n