ASP.NET Ajax框架提供了两种开发模型:服务器端编程和客户端编程。前者使用方便,开发人员可以不懂JS和Ajax,而是通过传统的ASP.NET开发方式完成Ajax应用的开发,但是其控制粒度较粗,他比较适用于在已有系统上实现局部的异步刷新功能。而后者开发难度相对较大,需要开发人员熟悉JavaScript,并熟悉ASP.NET AJAX提供的各种语法支持及客户端编程模型,但是其控制粒度精细,有利于提高应用的性能和质量。
我们可以不基于任何Ajax框架开发出Ajax程序,Ajax的核心是XmlHttpRequest对象,但是这种不基于框架开发Ajax程序非常麻烦,需要自己根据不同的浏览器对象创建不同的XmlHttpRequest对象,然后自己定义回调函数,在回调函数中实现逻辑,发送异步请求等。
MS基于上述的复杂的工作,实现了一个Ajax框架,即使ASP.Net Ajax框架,该框架与VS2008开发环境已经集成了,而在VS2005中还需要安装一个ASPAJAXExtSetup.msi, 并且进行Web.Config的配置,才可以实现ASP.Net Ajax框架,在有了ASP.net Ajax框架以后,我们就可以在JavaScript中通过WebService调用后台的.CS文件代码了。
第一部分 服务端部分
一.Asp.net Ajax 基础知识
Microsoft ASP.Net Ajax的安装包包括以下几部分:
一.ASP.NET 2.0 AJAX Extensions asp.net ajax的核心部分,包括最重要和最基本的一些控件、组件和功能,例如服务器端的ScriptManager控件、UpdatePanel控件,客户端的JavaScript面向对象方面扩展、调试类、Web Service代理等。
二.ASP.NET AJAX Futures CTP asp.net ajax的非核心部分,其中包括服务器端的扩展器控件(Extender Control)、Web部件,客户端的各种控件、拖放功能实现、ASP.NET AJAX XML脚本等,客户端组件有动画组件,行为组件,验证器组件等;客户端控件有ListView,Hyperlink,selector,Button,Label等空间。服务器端的样式扩展器控件有DragoverlayExtender等。。
三.Microsoft AJAX Library 包含了ASP.NET AJAX的客户端JavaScript文件,它里面包括的js文件就是Extensions.dll和preview.dll中包括的js文件的集合,当你不用asp.net ajax框架时,可以使用该安装文件,否则我们无需安装,只要安装了Extensions.dll和preview.dll就包括了该安装包中的所有的客户端脚本了。
四.ASP.NET AJAX Control Toolkit 包含了数十种基于ASP.NET AJAX的、组件化的、提供某个专一Ajax功能的ASP.NET AJAX服务器端控件和ASP.NET AJAX扩展器控件
Extensions.dll中包括的Microsoft AJAX Library部分的客户端js文件有:
MicrosoftAjax.js:该文件包含了ASP.NET AJAX的核心内容与功能,例如对JavaScript面向对象方面的扩展,Error、Type、StringBuilder等各种客户端类型,对调用Web Service即XMLHttpRequest对象的封装)的支持等。在此基础上,也扩充了包括最基础的客户端组件/控件、用于异步调用的XMLHttpExecutor、WebRequest对象、用于访问ASP.NET 2.0应用服务的ProfileService和AuthenticationService对象等ASP.NET AJAX的完整特性。该文件同样负责抹平不同浏览器之间JavaScript以及DOM访问的细微差别,以期提供给开发者经过封装的、统一的、标准的API。一般来讲,这些都是一个完善的ASP.NETAJAX应用程序所必需的功能,所以在默认条件下,ScriptManager将把这个文件作为ASP.NET AJAX的核心脚本文件发送至客户端。
MicrosoftAjaxTimer.js:该文件中定义了客户端的Timer组件,用来配合ASP.NET AJAX服
务器端Timer组件实现,提供定时触发事件(客户端以及服务器端)的功能。
MicrosoftAjaxWebForms.js:该文件中定义了一个非常重要的客户端对象:PageRequestManager。PageRequestManager旨在在客户端为开发人员提供ASP.NET服务器端页面生存周期一样的开发模型,以面向对象的方式控制、维护客户端异步回送的全过程。如果ScriptManager中启用了局部更新、异步回送的支持(EnablePartialRendering属性设置为true),则该文件将被自动发送至客户端。该脚本时服务端控件UpdatePanel得以正常工作的基础。
Preview.dll中包括的Microsoft AJAX Library部分的客户端js文件有:
PreviewScript.js:该文件包含了ASP.NET AJAX中全部的客户端组件/控件的实现,包括表示简单界面元素的TextBox、表示复杂绑定列表的ListView、验证用户输入的Validator、为HTML元素提供某些附加行为的Behavior、位于客户端的离线数据源DataSource、响应用户操作的Action等,以及对ASP.NET AJAX XML脚本的支持。同样,该文件还声明了这部分内容在不同浏览器之间兼容的代码。
PreviewDragDrop.js:该文件用于提供ASP.NET AJAX中鼠标拖动功能的实现。
PreviewWebParts.js与WhidbeyWebParts.js:该文件定义了和ASP.NET Web部件(WebPart)
协同工作的相关脚本。
PreviewGlitz.js:该文件用于实现某些界面中的特效,例如淡入淡出、动画效果等。
二.ScriptManager引用脚本
ASP.NET AJAX的程序集System.Web.Extensions.dll 和Microsoft.Web.Preview.dll 中包含了
Debug和Release两个版本的客户端脚本拷贝,ScriptManager将选择使用嵌入到该程序集中的这部分资源来生成客户端必需的脚本。客户端框架脚本将不会用直接引用的方式发送至客户端.
默认情况,ScriptManager会自动将MicrosoftAjax.js文件和MicrosoftAjaxWebForms.js文件发送至客户端。若页面中还需要更加完善的由其他ASP.NET AJAX客户端脚本提供的功能,或者一些自定义的脚本文件,则需要手工在ScriptManager中添加对这些文件的引用,如需要使用ASP.NET AJAX Futures CTP部分组件的功能.则需要使用<asp:ScriptReference Assembly="Microsoft.Web.Preview" />方式来引用prviewScript.js脚本文件
ScriptManager的EnablePartialRendering属性设置为true,这样ASP.NET AJAX会在页面中添加一些额外的JavaScript脚本来截获实现ASP.NET回送的__doPostBack()函数,并接下来使用XMLHttpRequest对象替代传统的整页回送,向服务器发出异步请求
三.客户端调用页面的方法
要将ScriptManager的EnablePageMethods属性设置为true,然后定义在服务器端的一个静态方法(注意方法一定要为静态,且必须添加[System.Web.Services.WebMethod]属性):
[System.Web.Services.WebMethod]
public static string SayHello(string name)
{
return string.Format("hello, {0}!", name);
}
随后即可在客户端JavaScript中用下面这行语句直接调用该方法(注意该方法定义在PageMethods中):
PageMethods.SayHello("Dflying", cb_sayHello);
其中cb_sayHello表示该异步调用完成后将要触发的回调函数。
四.引入程序集中内嵌的脚本资源:
<asp:ScriptManager runat="server">
<Scripts>
<asp:ScriptReference Assembly="Microsoft.Web.Preview" />
</Scripts>
</asp:ScriptManager>
ASP.NET AJAX的Value-add部分的脚本均要通过这种方式添加到页面中
五.引入单独的脚本文件:
<asp:ScriptManager runat="server">
<Scripts>
<asp:ScriptReference Path="MyPath/MyCustomScript.js" />
</Scripts>
</asp:ScriptManager>
<Scripts>标签下可以定义多个<asp:ScriptReference>,分别代表不同的脚本。
六.同一个scriptManager引入不同的Debug版本和Release版本文件
ScriptManager控件在设计时自然考虑到了这个需求,它提供了一个名为ScriptMode的枚举型属性,用以表示scriptmanager每次向客户端发送的是debug版本还是release版本的js脚本文件然而某些情况下,我们可能希望某个脚本不使用整个ScriptManager中的全局设置。例如,ScriptManager中指定了使用Release版本的脚本,但我们却希望其中的某一个脚本应用Debug版本来方便调试,这时,我们可以使用<asp:ScriptReference>标签(注意:不是<asp:ScriptManager>标签)中的同名属性——ScriptMode,来覆盖定义在ScriptManager中的设定,其默认值为Inherit,表示继承ScriptManager中的全局设置。需要特别注意的是,若网站部署环境的Machine.config文件中<deployment>元素的retail属性设置为true,那么无论<asp:ScriptManager>和<asp:ScriptReference>中的ScriptMode属性如何设置,ScriptManager都将直接发送Release版本的脚本。
ScriptManager将在解析每一个脚本时触发ResolveScriptReference事件,在服务器端ScriptManager1_ResolveScriptReference() 函数中, 我们可以通过e.Script参数得到目前待解析的这个ScriptReference对象
ScriptManager控件的LoadScriptsBeforeUI布尔值属性可用来设定ASP.NET AJAX脚本文件在最终HTML页面中出现的位置,其默认值为true,表示脚本文件的引用(即<script>标签)将出现在所有界面HTML元素之前,这样即可保证页面显示时已经加载并初始化了ASP.NET AJAX的客户端框架,用户又可以立即开始使用该程序
七.ScriptManager引入脚本支持本地化功能
若要使用ScriptManager内建的本地化功能,我们需要注意以下几点:
(1) 必须设定ScriptManager的EnableScriptLocalization属性为true。
(2) 必须使用ScriptPath直接引入脚本,而不能使用Assembly和Name属性引入内嵌在程序集中的脚本。
(3) 确保ResourceUICultures属性指定的各个区域都有与之相配的脚本文件。例如在上面的例子中,ResourceUICultures属性设置为了zh-CN和en-US,那么我们就要保证网站给出了Scripts/MyControl.zh-CN.js和Scripts/MyControl.en-US.js这两个脚本。
八.通知脚本资源加载完成:
在ASP.NET AJAX页面中,每一个将要与客户端框架协同运行的脚本文件的末尾都必须包含如下一行代码,用来发出这个通知:if(typeof(Sys) !== "undefined")Sys.Application.notifyScriptLoaded();因此,我们在编写自定义的脚本文件时,也要在末尾添加上面这一行。该脚本文件嵌入到某个程序集中,并通过<asp:ScriptReference>的Assembly和Name属性引入,那么ASP.NET AJAX将自动在文件末尾添加上述这一行通知脚本资源加载完成的声明。若脚本文件本身已经有了这一行,将导致这行代码被执行两次,进而引发异常。为了解决这个问题,<asp:ScriptReference>特意引入了NotifyScriptLoaded这个布尔值属性。其默认值为true,即自动为嵌入到程序集中的脚本添加通知脚本资源加载完成的声明。
九.管理webservice客户端代理:
<asp:ServiceReference>标签提供了一个布尔值的属性:InlineScript,其默认值为
false。若将其设置为true,则该Web Service的客户端代理将不再通过“Web Service文件路径+/js(或jsdebug)后缀”的形式单独取得,而是嵌入到页面HTML代码中,并随之一同发送至客户端,也就是所谓的“inline(内联)”。一般来讲,由于Web Service客户端代理文件都比较小,真正用来传输文件的时间并不多,所以传输这个文件所必须的准备工作——建立和关闭一次请求这个代理文件的HTTP连接——所花费的时间,在整个HTTP请求所花费的时间中也就相对地占有较大的比例。而若像这样将其内联到页面中,即可避免这一次额外的HTTP请求,进而也就略微地提高了页面加载的性能。
十.异常处理:
ScriptManager控件的一个非常重要的属性:AllowCustomErrorsRedirect,该属性为布尔类型,默认值为true,表示在异步更新发生异常时是否沿用Web.config<customErrors>节中的设定。Web.config的<customErrors>节中可以指定应用程序级别的错误处理页面,这将通过重定向至某个专门显示异常的页面来实现。而对于Ajax程序而言,页面跳转则是应该竭力避免的,因此一般来讲,最好将其设置为false。
ScriptManager的AsyncPostBackError事件将在异步回送引发异常时触发,AsyncPostBackErrorMessage属性表示了异步回送过程中发生的异常将显示出的消息
十一.以编程方式控制ScriptManager:
ScriptManager提供一个名为RegisterAsyncPostBackControl()的方法,该方法接受一个类型为Control的参数,使用该方法,ScriptManager的EnablePartialRendering属性值一定要设置成true。
UpdatePanel中包围的内容以及其指定的作为触发器的控件均自动在ScriptManager中注册为采用异步方式进行回送,而无需我们手工干预。修改ScriptManager的EnablePartialRendering、EnableScriptGlobalization、EnablePageMethods和EnableScriptLocalization等属性需要注意的是,修改这些属性值最好在Page的PreInit事件处理函数中进行。若晚于该阶段,ScriptManager将可能会抛出异常。
ScriptManager还提供了一个只读的布尔型属性:IsInAsyncPostBack。类似用传统ASP.NET模型中的Page.IsPostBack属性判断当前服务器执行的代码是否由回送引发,IsInAsyncPostBack可以用来判断当前服务器执行的代码是否是由异步回送所引发的。
protected void Page_Load(object sender, EventArgs e)
{
ScriptManager theScriptManager = ScriptManager.GetCurrent(this);
if (theScriptManager.IsInAsyncPostBack)
{
// 仅在异步回送中执行的代码。
}
}
ScriptManager的布尔型属性SupportsPartialRendering,用来判断当前的浏览器是否支持Ajax的局部更新功能;ScriptManager的布尔型属性IsDebuggingEnabled,用来判断当前的请求是否运行于Debug模式;ScriptManager提供的Scripts和Services两个属性将分别返回当前ScriptManager对象中引用的脚本文件和Web Service的集合;ScriptManager的作用远不止这些,它还提供了大量的用于注册页面中其他ASP.NET AJAX控件、脚本块以及数据的方法,均以“Register”开头。
当在模板页中使用scriptManager控件后,在具体的应用了模板页的页面中还需要引用其他脚本时,可以在该页面中使用scriptManagerProxy控件,该控件也可以引入脚本文件和webservice,和scriptmanager控件没什么区别。
十二. Updatepanel介绍
updatePanel的实现原理:首先scriptManager和updatePanel在服务器端达成一致,截获page的Render()方法并在页面上为需要进行异步回送的控件输出了一些专门得用于局部刷新的js脚步,然后在客户端,若这些控件通过_doPostBack()函数试图引发整页的回送,则上面提到的js脚步将截获这个调用,并将页面中各个用户输入控件的值,加上当前的视图状态(ViewState)用xmlhttpRequest对象发送回服务器。而此时服务器却对此丝毫不知,仍把它当做一次传统的回送,老实地引发一次页面的完整生存周期,并根据回送生成新的页面结构,随后又是page的Render()方法中,scriptmanager和updatePanel再一次截获了其中的实现过程,把不在UpdatePanel中的内容统统剔除,只发送给客户端真正更新了的部分。最后,客户端的xmlHttpRequest对象收到了这部分信息,并在不知不觉中悄悄地更新了UpdatePanel中定义的内容。
在客户端,updatePanel默认呈现为一对div元素。updatePanel的renderMode属性如果设置成inline,则在客户端,updatePanel 呈现为span元素。updatePanel还有个只读属性:IsInPartialRendering,因为是只读的,只会在服务端代码才用到,它只有在此次回送为异步回送,且updatePanel中的内容将被更新时才为true;
updatePanel可以调用其update()强制它刷新。
updatePanel的childrenAsTriggers属性设置为false,则该updatePanel的updateMode属性必须为Conditional。如果为always,则程序将在运行时抛出异常。
注意:异步回送的过程中整个页面的所有表单域(包括viewState)都将被完整发送回服务器。因此,在服务器端Render步骤之前,异步回送实际上合传统的整页回送没有任何区别,服务器端将仍旧进行Init,Load,事件处理等步骤,并可以访问到页面中任何控件的任何属性,无论其是否在某个updatePanel中,当然,异步回送过程中服务器端同样也可以设置updatePanel之外的某个控件的属性,只是render步骤中这个变化并不会被发送回客户端,所以客户端也不能看到这个修改。
asp.net ajax中还在客户端为异步回送的过程添加了若干个事件,包括initialize_Request,beginRequest,pageLoading,pageLoaded和endrequest等,让客户端也拥有了类似服务端的生命周期概念。这是配合客户端的PageRequestmanager对象来实现的。
动态加载含有updatePanel的用户控件时,要在Page_Init事件中进行,不可以在PageLoad中进行,因为scriptmanager和updatepanel是在pageLoad事件中进行协调和沟通,以便给客户端发送用于支持局部更新的js脚步。
动态加载控件可以如下:
page.LoadControl("aaa.ascx");
page.controls.Add("aaa.ascx");
在下一篇,将介绍ASP.NET Ajax的客户端部分。