我的AjaxPage对象(Asp.NET 1.1)-借用2.0的Callback实现无刷新回调
最近一段时间在博客园逛的时候,关于Ajax和Atlas的讨论很多很热,看了一些例子和代码,觉得和原来1.1的Postback方式有比较大的区别,而我目前所做的系统的架构和设计都是基于1.1的,如何转移到将来的ASP.NET 2.0,使用无刷新机制来提升客户体验,同时又保留原来的架构和设计,是我最近经常想到的问题。
不久之后,我看到了几位大侠对于ASP.NET 2.0 Callback的描述,称它能够实现轻量级的Ajax,也可以保留Postback方式,这让我豁然开朗,这不就是我想要的吗?如果能够把这种方式移植到1.1中,我们的系统就不用做太大的修改就可以在需要与客户频繁交互的地方采取Callback方式来实现Ajax效果,习惯1.1的程序员也容易了解和转换到Callback方式,并作为将来学习Ajax和Atlas的铺垫和过渡。
以下是大侠的文章
Teddy:深度解析Asp.Net2.0中的Callback机制
出走的影子:2.0正式版中callback的一些变化+使用示例(ASP.NET 2.0)
How to: Implement Callbacks in ASP.NET Web Pages
说干就干,下载.NET 2.0 Framework,随便运行一个页面,将WebResource.axd保存下来,改名为Asp2_Callback.js,客户端Callback所需要的JavaScript脚本就有了。接下来就该Reflector上场了,(真的感谢这个工具让我们能够透过表象看到ASP.NET的内在)。观察一下2.0中的Page对象,果然针对Callback添加了一些方法和属性,找出来加以改写,就成了一个基于1.1的支持Callback的AjaxPage对象了,下面是改写后的代码,帮助您揭开Callback实现之迷。
1using System;
2using System.IO;
3using System.Web;
4using System.Web.UI;
5using System.Text;
6using System.Collections;
7using System.Collections.Specialized;
8
9namespace CallbackDemo
10{
11 /// <summary>
12 /// 从System.Web.UI.Page继承,扩展了对Callback的支持
13 /// </summary>
14 public class AjaxPage:System.Web.UI.Page
15 {
16 private bool m_iscallback;
17 private string m_callbackid;
18 private NameValueCollection m_requestValueCollection;
19 private ICallbackEventHandler m_callbackControl;
20 private string m_formid;
21
22 /// <summary>
23 /// 判断本次提交是Callback还是Postback
24 /// </summary>
25 public bool IsCallback
26 {
27 get {return m_iscallback;}
28 }
29
30 /// <summary>
31 /// 回传的控件ID,此控件的RaiseCallbackEvent和GetCallbackResult方法将被触发
32 /// </summary>
33 public string CallbackID
34 {
35 get {return m_callbackid;}
36 }
37
38 public string FormID
39 {
40 get
41 {
42 if (m_formid==null)
43 m_formid = GetFormID();
44 return m_formid;
45 }
46 set {m_formid = value;}
47 }
48
49 public AjaxPage()
50 {
51 //如果将页面作为CallbackControl.则需要设置一个默认ID
52 this.ID = "AjaxPage";
53 m_iscallback = false;
54 m_callbackid = string.Empty;
55 m_requestValueCollection = null;
56 m_formid = null;
57 }
58
59 protected override void OnInit(EventArgs e)
60 {
61 //注册必要的脚本和方法
62 this.RegisterStartupScript("callbackscript","<SCRIPT LANGUAGE=\"javascript\" SRC=\""+this.Request.ApplicationPath+"/asp2_Callback.js\"></SCRIPT>");
63 this.RegisterStartupScript("mycallback",BuildCallbackCommand());
64 //根据Post的方式返回Request.QueryString或者Request.Form集合
65 m_requestValueCollection = this.DeterminePostBackMode();
66 if (this.m_requestValueCollection != null)
67 {
68 //检查回传值确定是否是Callback方式回传
69 m_callbackid = this.m_requestValueCollection["__CALLBACKID"];
70 if (m_callbackid != null)
71 {
72 m_iscallback = true;
73 }
74 }
75 base.OnInit(e);
76 }
77
78 protected override void OnLoad(EventArgs e)
79 {
80 base.OnLoad(e);
81 if (IsCallback)
82 PrepareCallback(CallbackID);
83 }
84
85 protected override void Render(HtmlTextWriter writer)
86 {
87 if (this.IsCallback)
88 {
89 this.RenderCallback();
90 }
91 else
92 base.Render(writer);
93 }
94
95 /// <summary>
96 /// 根据callbackControlID查找控件,如果该控件实现了ICallbackEventHandler接口就触发RaiseCallbackEvent方法
97 /// </summary>
98 /// <param name="callbackControlID">Callback控件ID</param>
99 private void PrepareCallback(string callbackControlID)
100 {
101 this.Response.Cache.SetNoStore();
102 try
103 {
104 string callbackparam = this.m_requestValueCollection["__CALLBACKPARAM"];
105 if (this.ID == callbackControlID)
106 this.m_callbackControl = (ICallbackEventHandler)this;
107 else
108 this.m_callbackControl = this.FindControl(callbackControlID) as ICallbackEventHandler;
109 if (this.m_callbackControl == null)
110 {
111 throw new InvalidOperationException(string.Format("Page_CallBackTargetInvalid, Callback Control ID:{0}", new object[] { callbackControlID }));
112 }
113 this.m_callbackControl.RaiseCallbackEvent(callbackparam);
114 }
115 catch (Exception exception)
116 {
117 this.Response.Clear();
118 this.Response.Write('e');
119 if (this.Context.IsCustomErrorEnabled)
120 {
121 this.Response.Write(string.Format("Page_CallBackError"));
122 return;
123 }
124 this.Response.Write(HttpUtility.HtmlEncode(exception.Message));
125 return;
126 }
127 }
128
129 /// <summary>
130 /// 如果是Callback方式则在Render中执行该方法,将m_callbackControl.GetCallbackResult()返回到客户端
131 /// </summary>
132 private void RenderCallback()
133 {
134 string cbsc = this.m_requestValueCollection["__CALLBACKLOADSCRIPT"];
135 bool flag = !(cbsc==null || cbsc==string.Empty);
136 try
137 {
138 string callbackidx = null;
139 if (flag)
140 {
141 callbackidx = this.m_requestValueCollection["__CALLBACKINDEX"];
142 if ((callbackidx==null) || (callbackidx==string.Empty))
143 {
144 throw new HttpException(string.Format("Page_CallBackInvalid"));
145 }
146 for (int idx = 0; idx < callbackidx.Length; idx++)
147 {
148 if (!char.IsDigit(callbackidx, idx))
149 {
150 throw new HttpException(string.Format("Page_CallBackInvalid"));
151 }
152 }
153 this.Response.Write("<script>parent.__pendingCallbacks[");
154 this.Response.Write(callbackidx);
155 this.Response.Write("].xmlRequest.responseText=\"");
156 }
157 if (this.m_callbackControl != null)
158 {
159 string callbackrlt = this.m_callbackControl.GetCallbackResult();
160 this.Response.Write('s');
161 this.Response.Write(callbackrlt);
162 }
163 if (flag)
164 {
165 this.Response.Write("\";parent.__pendingCallbacks[");
166 this.Response.Write(callbackidx);
167 this.Response.Write("].xmlRequest.readyState=4;parent.WebForm_CallbackComplete();</script>");
168 }
169 }
170 catch (Exception exception1)
171 {
172 this.Response.Clear();
173 this.Response.Write('e');
174 if (this.Context.IsCustomErrorEnabled)
175 {
176 this.Response.Write(string.Format("Page_CallBackError"));
177 return;
178 }
179 this.Response.Write(HttpUtility.HtmlEncode(exception1.Message));
180 }
181 }
182
183 生成Callback方法
228
229
230 /// <summary>
231 /// WebForm_MyCallback是对系统提供的WebForm_DoCallback方法进行封装,以加入theForm参数
232 /// </summary>
233 /// <returns></returns>
234 private string BuildCallbackCommand()
235 {
236 StringBuilder sb = new StringBuilder();
237 sb.Append("<script type=\"text/javascript\" language=\"javascript\">\r\n");
238 sb.Append("var theForm = document.forms['"+FormID+"'];\r\n");
239 sb.Append("if (!theForm) {theForm = document."+FormID+";}\r\n");
240 sb.Append("function WebForm_MyCallback(eventTarget, eventArgument, eventCallback, context, errorCallback, useAsync)\r\n");
241 sb.Append("{\r\n");
242 sb.Append(" context.innerHTML = \"Processing\";\r\n");
243 sb.Append(" __theFormPostData = \"\";\r\n");
244 sb.Append(" WebForm_InitCallback();\r\n");
245 sb.Append(" WebForm_DoCallback(eventTarget, eventArgument, eventCallback, context, errorCallback, useAsync)\r\n");
246 sb.Append("}\r\n");
247 sb.Append("</Script>\r\n");
248 sb.Append("\r\n");
249 return sb.ToString();
250 }
251
252 /// <summary>
253 /// 重写VerifyRenderingInServerForm方法,当Callback方式回传时就不再进行Verify
254 /// </summary>
255 /// <param name="control"></param>
256 public override void VerifyRenderingInServerForm(Control control)
257 {
258 if (!this.IsCallback)
259 base.VerifyRenderingInServerForm(control);
260 }
261
262 /// <summary>
263 /// 因为Form无法直接读取,只能采用遍历控件查找
264 /// </summary>
265 /// <returns></returns>
266 public virtual string GetFormID()
267 {
268 for (int i=0;i<this.Controls.Count;i++)
269 {
270 if (this.Controls[i] is System.Web.UI.HtmlControls.HtmlForm)
271 return (this.Controls[i] as System.Web.UI.HtmlControls.HtmlForm).ID;
272 }
273 return "Form1";
274 }
275
276 }
277}
278
2using System.IO;
3using System.Web;
4using System.Web.UI;
5using System.Text;
6using System.Collections;
7using System.Collections.Specialized;
8
9namespace CallbackDemo
10{
11 /// <summary>
12 /// 从System.Web.UI.Page继承,扩展了对Callback的支持
13 /// </summary>
14 public class AjaxPage:System.Web.UI.Page
15 {
16 private bool m_iscallback;
17 private string m_callbackid;
18 private NameValueCollection m_requestValueCollection;
19 private ICallbackEventHandler m_callbackControl;
20 private string m_formid;
21
22 /// <summary>
23 /// 判断本次提交是Callback还是Postback
24 /// </summary>
25 public bool IsCallback
26 {
27 get {return m_iscallback;}
28 }
29
30 /// <summary>
31 /// 回传的控件ID,此控件的RaiseCallbackEvent和GetCallbackResult方法将被触发
32 /// </summary>
33 public string CallbackID
34 {
35 get {return m_callbackid;}
36 }
37
38 public string FormID
39 {
40 get
41 {
42 if (m_formid==null)
43 m_formid = GetFormID();
44 return m_formid;
45 }
46 set {m_formid = value;}
47 }
48
49 public AjaxPage()
50 {
51 //如果将页面作为CallbackControl.则需要设置一个默认ID
52 this.ID = "AjaxPage";
53 m_iscallback = false;
54 m_callbackid = string.Empty;
55 m_requestValueCollection = null;
56 m_formid = null;
57 }
58
59 protected override void OnInit(EventArgs e)
60 {
61 //注册必要的脚本和方法
62 this.RegisterStartupScript("callbackscript","<SCRIPT LANGUAGE=\"javascript\" SRC=\""+this.Request.ApplicationPath+"/asp2_Callback.js\"></SCRIPT>");
63 this.RegisterStartupScript("mycallback",BuildCallbackCommand());
64 //根据Post的方式返回Request.QueryString或者Request.Form集合
65 m_requestValueCollection = this.DeterminePostBackMode();
66 if (this.m_requestValueCollection != null)
67 {
68 //检查回传值确定是否是Callback方式回传
69 m_callbackid = this.m_requestValueCollection["__CALLBACKID"];
70 if (m_callbackid != null)
71 {
72 m_iscallback = true;
73 }
74 }
75 base.OnInit(e);
76 }
77
78 protected override void OnLoad(EventArgs e)
79 {
80 base.OnLoad(e);
81 if (IsCallback)
82 PrepareCallback(CallbackID);
83 }
84
85 protected override void Render(HtmlTextWriter writer)
86 {
87 if (this.IsCallback)
88 {
89 this.RenderCallback();
90 }
91 else
92 base.Render(writer);
93 }
94
95 /// <summary>
96 /// 根据callbackControlID查找控件,如果该控件实现了ICallbackEventHandler接口就触发RaiseCallbackEvent方法
97 /// </summary>
98 /// <param name="callbackControlID">Callback控件ID</param>
99 private void PrepareCallback(string callbackControlID)
100 {
101 this.Response.Cache.SetNoStore();
102 try
103 {
104 string callbackparam = this.m_requestValueCollection["__CALLBACKPARAM"];
105 if (this.ID == callbackControlID)
106 this.m_callbackControl = (ICallbackEventHandler)this;
107 else
108 this.m_callbackControl = this.FindControl(callbackControlID) as ICallbackEventHandler;
109 if (this.m_callbackControl == null)
110 {
111 throw new InvalidOperationException(string.Format("Page_CallBackTargetInvalid, Callback Control ID:{0}", new object[] { callbackControlID }));
112 }
113 this.m_callbackControl.RaiseCallbackEvent(callbackparam);
114 }
115 catch (Exception exception)
116 {
117 this.Response.Clear();
118 this.Response.Write('e');
119 if (this.Context.IsCustomErrorEnabled)
120 {
121 this.Response.Write(string.Format("Page_CallBackError"));
122 return;
123 }
124 this.Response.Write(HttpUtility.HtmlEncode(exception.Message));
125 return;
126 }
127 }
128
129 /// <summary>
130 /// 如果是Callback方式则在Render中执行该方法,将m_callbackControl.GetCallbackResult()返回到客户端
131 /// </summary>
132 private void RenderCallback()
133 {
134 string cbsc = this.m_requestValueCollection["__CALLBACKLOADSCRIPT"];
135 bool flag = !(cbsc==null || cbsc==string.Empty);
136 try
137 {
138 string callbackidx = null;
139 if (flag)
140 {
141 callbackidx = this.m_requestValueCollection["__CALLBACKINDEX"];
142 if ((callbackidx==null) || (callbackidx==string.Empty))
143 {
144 throw new HttpException(string.Format("Page_CallBackInvalid"));
145 }
146 for (int idx = 0; idx < callbackidx.Length; idx++)
147 {
148 if (!char.IsDigit(callbackidx, idx))
149 {
150 throw new HttpException(string.Format("Page_CallBackInvalid"));
151 }
152 }
153 this.Response.Write("<script>parent.__pendingCallbacks[");
154 this.Response.Write(callbackidx);
155 this.Response.Write("].xmlRequest.responseText=\"");
156 }
157 if (this.m_callbackControl != null)
158 {
159 string callbackrlt = this.m_callbackControl.GetCallbackResult();
160 this.Response.Write('s');
161 this.Response.Write(callbackrlt);
162 }
163 if (flag)
164 {
165 this.Response.Write("\";parent.__pendingCallbacks[");
166 this.Response.Write(callbackidx);
167 this.Response.Write("].xmlRequest.readyState=4;parent.WebForm_CallbackComplete();</script>");
168 }
169 }
170 catch (Exception exception1)
171 {
172 this.Response.Clear();
173 this.Response.Write('e');
174 if (this.Context.IsCustomErrorEnabled)
175 {
176 this.Response.Write(string.Format("Page_CallBackError"));
177 return;
178 }
179 this.Response.Write(HttpUtility.HtmlEncode(exception1.Message));
180 }
181 }
182
183 生成Callback方法
228
229
230 /// <summary>
231 /// WebForm_MyCallback是对系统提供的WebForm_DoCallback方法进行封装,以加入theForm参数
232 /// </summary>
233 /// <returns></returns>
234 private string BuildCallbackCommand()
235 {
236 StringBuilder sb = new StringBuilder();
237 sb.Append("<script type=\"text/javascript\" language=\"javascript\">\r\n");
238 sb.Append("var theForm = document.forms['"+FormID+"'];\r\n");
239 sb.Append("if (!theForm) {theForm = document."+FormID+";}\r\n");
240 sb.Append("function WebForm_MyCallback(eventTarget, eventArgument, eventCallback, context, errorCallback, useAsync)\r\n");
241 sb.Append("{\r\n");
242 sb.Append(" context.innerHTML = \"Processing\";\r\n");
243 sb.Append(" __theFormPostData = \"\";\r\n");
244 sb.Append(" WebForm_InitCallback();\r\n");
245 sb.Append(" WebForm_DoCallback(eventTarget, eventArgument, eventCallback, context, errorCallback, useAsync)\r\n");
246 sb.Append("}\r\n");
247 sb.Append("</Script>\r\n");
248 sb.Append("\r\n");
249 return sb.ToString();
250 }
251
252 /// <summary>
253 /// 重写VerifyRenderingInServerForm方法,当Callback方式回传时就不再进行Verify
254 /// </summary>
255 /// <param name="control"></param>
256 public override void VerifyRenderingInServerForm(Control control)
257 {
258 if (!this.IsCallback)
259 base.VerifyRenderingInServerForm(control);
260 }
261
262 /// <summary>
263 /// 因为Form无法直接读取,只能采用遍历控件查找
264 /// </summary>
265 /// <returns></returns>
266 public virtual string GetFormID()
267 {
268 for (int i=0;i<this.Controls.Count;i++)
269 {
270 if (this.Controls[i] is System.Web.UI.HtmlControls.HtmlForm)
271 return (this.Controls[i] as System.Web.UI.HtmlControls.HtmlForm).ID;
272 }
273 return "Form1";
274 }
275
276 }
277}
278
ICallbackEventHandler接口
/// <summary>
/// 希望具有Callback功能的控件必须实现此接口
/// </summary>
public interface ICallbackEventHandler
{
string GetCallbackResult();
void RaiseCallbackEvent(string eventArgument);
}
/// 希望具有Callback功能的控件必须实现此接口
/// </summary>
public interface ICallbackEventHandler
{
string GetCallbackResult();
void RaiseCallbackEvent(string eventArgument);
}
关键的方法
PrepareCallback(CallbackID)
系统以Callback方式回传时执行此方法,根据返回的CallbackID查找相应的CallbackCtrl,传入callbackparam并执行其RaiseCallbackEvent方法。
RenderCallback()
系统以Callback方式回传时将不会执行原来的Render方法而改而执行Rendercallback,它的作用就是将callbackCtrl的GetCallbackResult()方法的结果返回到客户端交由脚本继续执行。
GetCallbackEventReference()
生成调用Callback方式需要的脚本,封装了2.0提供的WebForm_DoCallback方法,并可以加入自己的脚本语句。
要注意的地方
FormID
Form对象无法访问,只能通过外部指定或者通过遍历Controls查找,期待更好的方法
theForm
相对2.0,1.1在客户端脚本自动生成的是theform,所以在BuildCallbackCommand()方法中生成theForm对象,否则Callback脚本无法执行.
VerifyRenderingInServerForm()
重写该方法使Callback下不进行此验证以保证当我们使用RendControl()生成Html代码返回时,某些服务端控件不会强制验证而抛出异常
WebForm_InitCallback()
此方法执行后将收集页面的回传信息,使得Callback方式回传后可以使用Request.Form[key]方式直接访问.
完成了AjaxPage的封装后,只要继承于它就可以在项目中象2.0一样轻松使用Callback功能了,如何使用在大侠们的文章里已经有详细的说明和示例,这里就不多说了,直接奉上一个例子,演示同时执行Postback和Callback,可以看出后台的结构和代码并不需要很大的修改就可以作出无刷新页面的效果。本例需要连接MSSQLServer的NorthWind数据库,本地编译前请先修改数据库连接字符串。
源代码和例子下载
https://files.cnblogs.com/abei108/CallbackDemo.rar
注意:AjaxPage的功能已经扩展,请访问我的AjaxPage对象续-一分钟让你的UserControl变成UpdatePanel
示例缩略图
写在最后
这是我在网上认真写的第一篇博客,文笔有限加上仓促写成,如有错误请大家包涵,转载请注明出处。谢谢
posted on 2006-06-10 01:21 Gao.Steven 阅读(2305) 评论(9) 编辑 收藏 举报