随着Ajax技术的发展和成熟,要进行页面无刷新的数据交互已经并不是什么困难的事情。然而,如何使用该技术与web系统更好的融合并提升用户体验仍然是一个值得探讨的话题。完全从头编写和开发前端的Ajax交互的js脚本,似乎工作量和难度都较大,编写相应的js脚本时还需要考虑到浏览器兼容性问题,开发和测试过程都很容易影响到项目的进度。网上可用的Ajax套件也不少,比如基于jquery的ajax控件或基于Yahoo UI的扩展包yui-ext都可以实现这样的功能。由于以上工具包都实现了完整的ajax交互并且还附带一些其它常规的脚本控制功能而显得较为庞大,对于需要开发一些轻量级的、要具备较高效率的系统来说似乎是“杀鸡焉用牛刀”了。
我们这里所要介绍的是基于.net技术的页面无刷新实时交互功能的实现,其实在.net框架下只需使用少量的脚本便可以实现页面的无刷新交互功能,无需其它的ajax控件的支持。
以信息管理系统中对作者信息的在线实时编辑为例来进行说明,示例图片如图1,我们需要实时修改的是第4条信息中的名为“张学荣”的作者信息。

我们想要实现的效果是:双击该信息的单元格,该单元格出现可编辑的文本框,内容可任意修改,修改完成后在页面其它地方单击即触发该单元格中信息的提交。在系统后台接收修改后的信息,修改数据库中相应的数据,反馈到前台,前台该单元格中的信息更新为修改后的数据,整个过程页面无需刷新。
这里涉及到前后台的配合,在.net框架下,要实现无刷新的信息提交,需要对前台页面和后台程序进行一些设置。以.net2.0版本为例,需要在前台页面相应的文本单元格中绑定数据并添加处理脚本:
<td align="center" id='Aut<%# Eval("ID") %>' ondblclick='OnlineEdit(this,"Aut",<%# Eval("ID") %>)' title='<%# Eval("Author")%>'><%# TextManage.TrimString(Eval("Author").ToString(),4) %></td>
这里对单元格进行了编号,添加了"Aut"前缀并绑定文章ID值以方便脚本读取和功能区分。脚本响应了鼠标双击事件,即ondblclick触发脚本OnlineEdit,其参数分别是当前单元格对象、编辑对象标识名称、当前信息ID值。OnlineEdit的脚本代码如下:
function OnlineEdit(obj, act, id)
{
var tag;
if(obj.firstChild!=null)
tag = obj.firstChild.tagName;
if ((typeof(tag) != "undefined" && tag.toLowerCase() == "input")||flag)
{
return;
}
var org = obj.innerHTML;
var val = obj.getAttribute("title");
var txt = document.createElement("INPUT");
txt.value = (val == 'N/A') ? '' : val;
txt.style.width = (obj.clientWidth-6) + "px" ;
obj.innerHTML = "";
obj.appendChild(txt);
txt.focus();
txt.onkeypress = function(e)
{
var evt = Utils.fixEvent(e);
var obj = Utils.srcElement(e);
if (evt.keyCode == 13)
{
obj.blur();
return false;
}
if (evt.keyCode == 27)
{
obj.parentNode.innerHTML = org;
}
}
txt.onblur = function(e)
{
var arg=encodeURIComponent(Utils.trim(txt.value))+"$"+act+id;
EditOnline(arg);
}
}简单叙述一下以上脚本的功能:通过当前对象获取原始完整信息,然后在该单元格中绘制一个文本框,文本框的内容即为原始信息(如图2)。对该单元格监听事件onkeypress,即键盘按下事件,如果发现用户按下ESC则返回原始信息取消编辑,若用户按下回车键则触发onblur事件。这里的onblur事件即失去焦点事件,用于提交信息,提交前对信息进行编码,最后通过EditOnline方法向后台异步提交。提交完成服务器返回信息后再调用回调函数EditOnCallback,这也是写入前台页面中的,代码如下:
function EditOnCallback(result)
{
var obj=document.getElementById(CurID);
if(result!="Error")
{
obj.removeAttribute("title");
obj.setAttribute("title",result);
var w = obj.clientWidth||obj.offsetWidth;
if(result.length>w/18)
obj.innerHTML=result.substring(0,w/18)+"...";
else
obj.innerHTML=result;
flag=false;
}
else
obj.innerHTML="<font color='red'>更新失败!</font>";
}该函数的作用是获取服务器返回的更新后的信息,判断信息长度,根据要求截断后显示在用户所编辑的单元格中。

为了让前后台能异步交互,就需要在程序页面需要在原有的页面类的基础上再继承ICallbackEventHandler类,这个类的作用就是处理异步信息的提交。除此以外,在后台页面还需要手工编写一些其它的代码:
public void RaiseCallbackEvent(string Id)
{
try
{
string content = "";
int index = Id.IndexOf("$");
if (index != -1)
{
content =System.Web.HttpUtility.UrlDecode(Id.Substring(0, index));
Id = Id.Substring(index+1);
}
ArticleInfo info = SysManage.ReadArticleInfo(Convert.ToInt32(Id.Substring(3)));
using (DataProvider provider = new DataProvider())
{
if (Id.IndexOf("Aut") != -1)
{
info.Author = content;
SysManage.ModifyArticle(info, provider);
this.a = content;
}
}
}
catch (Exception e)
{
this.a = "Error";
}
}以上均为异步提交函数的重写代码,这里的实现逻辑是,通过前台提交的数据以"$"符号分隔,符号前为更新的信息内容(由于内容经过前台转码,因此这里需要解码),后为更新的文章ID值。根据该ID值和数据信息更新数据库,然后将更新后的信息内容通过参数this.a返回,如果出错则返回Error。
另外,为了使前后台的数据能通过脚本交互,还需要在前台注入脚本代码,如下:
string str1 = "<script language=\"javascript\">\n//<!--\nfunction EditOnline(Id) \n{ \nif(!flag){\n CurID=Id.substring(Id.indexOf(\"$\")+1);\n flag=true;\n document.getElementById(CurID).innerHTML=\"<font color='Green'>提交中…</font>\";\n " + this.Page.ClientScript.GetCallbackEventReference(this, "Id", "EditOnCallback", null) + ";\n} \n return false;\n}\n//-->\n</script>";
Page.ClientScript.RegisterClientScriptBlock(base.GetType(), "OnlineEdit", str1);
该代码一方面与OnlineEdit(obj, act, id)脚本对接实现信息的提交,另一方面也同时处理正在提交过程中的提交事件,这里显示的是提交状态信息“提交中…”,为了防止信息编辑和提交过程中的逻辑冲突,这里用到了一个起互斥锁作用的变量flag。至此,在线实时编辑信息的功能便完成了,类似的方式还可以实现其它项目的编辑、数据的删除、状态更改等。