MagicAjax.NET is a free open-source framework, designed to make it easier and more intuitive for developers to integrate AJAX technology into their web pages, without replacing the ASP.NET controls and/or writing tons of javascript code. MagicAjax initially appeared as a codeproject article. Now it is hosted on Sourceforge and you can find the latest release at the downloads section.
MagicAjax(http://www.magicajax.net/),他让你体验什么叫Easy AJAX,并支持.Net2.0。你无需对现有的webform方式开发有任何的改变,你只需配配web.config,拉拉控件就行了。
几句话介绍MagicAjax
转自老猫的理想
以最简单的方式整合AJAX功能到页面中,不需要修改ASP.NET控件,不需要写习新的控件,不需要撰写JAVASCRIPT
特点:
1,只需在WEB.CONFIG里加入一点点配置信息
2,只用一个AJAXPANNEL就实现AJAX功能
3,只要把需要无刷的控件放进AJAXPANNEL就可以了(这个好玩,局部无刷)
4,AJAXPANNEL跟ASP.NET的PANNEL控件差不多,可以在设计器里拖控件到里面(这点要提出异议,虽然他跟PANNEL一样,可是,PANNEL的设计时本来就不是令我很满意,他完全可以做得更好)
5,不需要写JAVASCRIPT(估计不少人对这个头痛)
6,实现起来太容易了,如果你有旧的站点要支持AJAX,你只要用AJAXPANNEL把这些控件包起来就OK
7,AJAX功能和普通的POSTBACK可以共存于一个WEBFORM,MAGICAJAX处理模块只处理包含在AJAXPANNEL中的控个的CALLBACK动作
8,VIEWSTATE的内容对于POSTBACK和CALLBACK来说,都是可见的,因此,无论是CALLBACK产生的VIEWSTATE也好,还是POSTBACK产生的,都是可见的(爽吧)
9,提供一个类似POSTBACK的AJAXCALL的事件模型
10,提供许多静态方法帮你处理AJAXCALL,例如发送JS到客户端
11,CALLPACK时有一个LOADING指示(跟GOOGLE差不多,不过,好像这个LOADING没法配置样式,郁闷,难道真要重编译吗?这点不好吧,毕竟这个作者应该考到)
12.MAGICAJAX的页面会缓存至IE缓存,当用户点回退按钮时,会看到他前一步的操作(爽呀,POSTBACK的那个REFRESH提示就不爽了)
13,支持IE和FIREFOX(嗯,不支持OPERA?),未来支持更多浏览器
14,还好,当浏览器不支持JS或者不是MAGICAJAX支持的类型的时候,页面将渲染成传统的形式
特性不少,最重要的是通过上面,你可以看到,它不用你大动干弋,而且兼容性确实不错,细节上处理的也好
使用步骤:
1,在WEB.CONFIG中加入配置节
<httpModules>
<add name="MagicAjax" type="MagicAjax.MagicAjaxModule, MagicAjax" />
</httpModules>
2,引入程序集,加入AJAXPANNEL控件
<%@ Register TagPrefix="ajax" Namespace="MagicAjax.UI.Controls" Assembly="MagicAjax" %>
<ajax:AjaxPanel id="AjaxPanel1" runat="server">
</ajax:AjaxPanel>
这个步骤不用手工了吧,直接在VS的TOOLBOX里ADD/REMOVE,然后将AJAX PANNEL添到里面,拖到WEBFORM上,VS.NET会帮你加入引用,生成标记了
3,把要使用AJAX功能的控件拖进AJAXPANNEL里,撰写这些控件的事件即可
处理AJAXCALL
一般可以用MagicAjaxContext.Current.IsAjaxCall判断是否是AJAXCALL事件,不过,它不准确,如果事件是由客户端引发的,就没问题,但是如果是另一个WEBFORM用TRANSFER来引发的话,它就判断错误,正确的方法是用MagicAjaxContext.Current.IsAjaxCallForPage
修改LOADING
方法是复制CORE里面的SCRIPT文件夹到你的站点某个文件夹里,然后在WEB.CONFIG里加入
<magicAjax outputCompareMode="HashCode" tracing="false" scriptPath="~/aspnet_client/magicAjax" >
注意大小写
是scriptPath,不是ScriptPath,
function CreateWaitElement() {
var elem = document.getElementById('__AjaxCall_Wait');
if (!elem) {
elem = document.createElement("div");
elem.id = '__AjaxCall_Wait';
elem.style.position = 'absolute';
elem.style.height = 17;
elem.border = "1px";
elem.style.paddingLeft = "3px";
elem.style.paddingRight = "3px";
elem.style.fontSize = "11px";
elem.style.borderColor = "#cccccc";
elem.style.borderWidth = "1";
elem.style.borderStyle="solid";
elem.style.backgroundColor = "efefef";
elem.style.color = "darkRed";
elem.innerHTML = '数据载入中...';
elem.style.visibility = 'hidden';
document.body.insertBefore(elem, document.body.firstChild);
}
waitElement = elem;
}
// end wait element
MagicAjax工作方式
从例子入手吧,一个button,一个label,点击button更新lable到当前时间。
设计器上的html:
<div style="width: 300px">
<ajax:AjaxPanel ID="AjaxPanel1" runat="server">
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" />
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label></ajax:AjaxPanel>
</div>
输出后的html:
<div>
<input type="hidden" name="__CONTROL_FINGERPRINTS_AjaxPanel1" id="__CONTROL_FINGERPRINTS_AjaxPanel1" value="" />
<input type="hidden" name="AjaxPanel1$RBS_Store" id="AjaxPanel1$RBS_Store" value="" />
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNTg1NTY3MzczD2QWAgIDD2QWAgIBDw9kFgIeCEFqYXhDYWxsBQVhc3luY2RkWqu19ZXiwLYiiNPPAP+GKoHYdzs=" />
</div>
<div style="width: 300px">
<span id='AjaxPanel1$RBS_Holder'><span id="Span1" AjaxCall="async">
<span id="Button1$ajaxdest" name="__ajaxmark"><input type="submit" name="Button1" value="Button" id="Submit1" /></span>
<span id="Label1$ajaxdest" name="__ajaxmark"><span id="Span2">Label</span></span></span></span>
</div>
<script type="text/javascript">
<!--
var RBS_Controls = new Array(document.getElementById("AjaxPanel1$RBS_Holder"));
var RBS_Controls_Store = new Array(document.forms[0]["AjaxPanel1$RBS_Store"]);
// -->
</script>
MagicAjaxModule是一个IHttpModule拦截请求。主要处理在:
void Application_AcquireRequestState(object sender, EventArgs e)
·如果是普通Page,直接输出页面。
·如果请求是“Get”,直接输出页面。
· 如果是ajax请求的话,由_request.Form["__AJAXCALL"]判断是否一个ajax请求,如果是则调用 HttpContext.Current.Handler.ProcessRequest(HttpContext.Current)并更新相关状态,包括ViewState,最后Flush()页面。在这个过程当中,输出都是交给AjaxCallHelper完成,而这个help最后产生一个 script,那么在客户端在接收到这个script,就直接执行:eval(responseText).比如例子中点击button后,最后产生的 script是:
AJAXCbo.ExtendedSetHtmlOfElementScript("<span id=\"Label1\">2005-12-8 18:03:22</span>","Label1$ajaxdest");
AJAXCbo.SetFieldScript("__CONTROL_FINGERPRINTS_AjaxPanel1","C04A0FC;Button1#7DA27781;Label1#F84162CA");
AJAXCbo.SetFieldScript("__VIEWSTATE","/wEPDwUKMTkwNzc1NDY4MQ9kFgICAw9kFgICAQ8PZBYCHghBamF4Q2FsbAUFYXN5bmMWAgIDDw8WAh4EVGV4dAUSMjAwNS0xMi04IDE4OjAzOjIyZGRk+HNmUx11Ztw2Z2CodiIhPxrEm4A=");
'AJAX_LOADING_OK';
由此可以看出,点击button后,返回给客户端,客户端负责解释和执行这个script。
第一行:更新label里面的时间
第二行:更新panel的隐含字段
第三行:更新ViewState
第四行:一个标志,表示请求成功
客户端的js工作流程:
1)首先,他把页面所有的ajaxPanel放在一个array里面:RBS_Controls,而每个panel都对应到另外一个array:RBS_Controls_Store 里面的各个panel的状态。
2)这时候大家注意到页面上这句js:
<script language='javascript'>
if (typeof(AJAXCbo) == 'undefined')
alert("Unable to find script library '/AjaxCallObject.js'. Copy the file to the required location, or change the 'scriptPath' setting at magicAjax section of web.config.");
else
AJAXCbo.HookAjaxCall(false,false,false);
</script>
AjaxCallObject.prototype.HookAjaxCall = function(bPageIsStored, bUnloadStoredPage, bTracing)会hook几个事件:
window.onload, window.onbeforeunload, window.onunload, document.forms[0].onsubmit(这是是重点)
3)点击panel里面的button发出一个submit,由于document.forms[0].onsubmit被hook了,他在这里引发: AJAXCbo.DoAjaxCall(target.name, "", cbType)并return false,就不会引发页面刷新。
4)在DoAjaxCall里面,一个for循环把form里面的内容序列化成字符串放在变量theData里面
5)调用XmlHttp工作:open->onreadystatechange(设置异步完成引发事件)->setRequestHeader->send
不管异步调用还是同步调用,最后都会引发:OnComplete事件,OnComplete就会调用eval(responseText)来执行服务段返回的script了。
AjaxPanel控件:
控件主要render HTML,配合客户端的js工作。
·构造函数初始化控件的panel里面的XmlHttp请求方式。(同步/异步)
·void AddedControl(Control control, int index)把panel里面的服务端控件加到_addedControls这个集合里面,上面就是把Button1和Lable1加载。
·void OnLoad(EventArgs e)注册这个panel的隐含字段,就是上面的__CONTROL_FINGERPRINTS_AjaxPanel1。
·void OnPreRender(EventArgs e) :如果不是嵌套的panel加入隐含字段AjaxPanel1$RBS_Store和两个变量(RBS_Controls和 RBS_Controls_Store)用处处理客户端AJAX调用。这里必要说明一下,这个隐含字段是用于类似ViewState的用途?
·void Render(HtmlTextWriter writer)把非嵌套的panel输出成<span>,就是上面的<span id='AjaxPanel1$RBS_Holder'> ,如果是嵌套的panel就只有顶层的panel有这个<span>。
·void RenderChildren(HtmlTextWriter writer)把panel里面的子控件输出,非服务端控件输出不变。如果是服务端控件就把这个控件放在一个<span>里面,并且id为:控件名称+$ajaxdest,name为:__ajaxmark。最后如果IsPageNoStoreMode是true则注册一个js,他会在客户端执行(AJAXCbo.SetFieldIfEmptyScript),把控件的状态放到 __CONTROL_FINGERPRINTS_AjaxPanel1这个隐含字段。
<script type='text/javascript'>
AJAXCbo.SetFieldIfEmptyScript("__CONTROL_FINGERPRINTS_AjaxPanel1","C04A0FC;Button1#7DA27781;Label1#31926D7E");
</script>
·void OnUnload(EventArgs e)IsPageNoStoreMode为true的话,会更新控件的更新控件的状态。
哈~好像写完了,最后给大家一个小秘诀。看看AjaxCallObject.js,当请求的时候,他会象GMail那样在右上脚出现一个Wait...的等待,很cool,你只要在这里做一个小更改,改CreateWaitElement那部分就能达到另外的效果。我这里把请求数据时,改成windows关机时,整个页面变灰的那种效果,类似的js如下:
<SCRIPT type="text/javascript">
<!--
function log_out()
{
ht1 = parent.frames.item(0).document.getElementsByTagName("html");
ht1[0].style.filter = "progid:DXImageTransform.Microsoft.BasicImage(grayscale=1)";
ht2 = document.getElementsByTagName("html");
ht2[0].style.filter = "progid:DXImageTransform.Microsoft.BasicImage(grayscale=1)";
if (confirm('你是否确认注销?'))
{
return true;
}
else
{
ht1[0].style.filter = "";
ht2[0].style.filter = "";
return false;
}
}
//-->
</SCRIPT>