ASP.NET AJAX 客户端库使用多层设计。最底层是一组允许面向对象模式的 JavaScript 语言增强以及一组对核心 JavaScript 数据类型的扩展。ASP.NET AJAX 还有一组构建在那个框架的核心客户端类和一个客户端页面模型。这个模型包括用于 Web 服务器回调特性的类、用于支持 UpdatePanel 之类的 Web 控件的特定类,以及已经封装页面及其元素的控件类。
1. Application 类
网页模型的入口点是 Application 类。在浏览器里加载在 ASP.NET AJAX 中可用的 Web 页面之后,就会创建 Sys.Application 类的一个实例。
Application 对象管理页面的组件并加载所有由 ScriptManager 注册的外部脚本文件。
ScriptManager 插入创建 Application 对象的代码,Application 对象为服务器端的 ScriptManager 完成全部客户端工作。
Application 对象引发两个主要事件。load 事件在页面第一次被处理以及每次回发(包括异步回发)后发生。unload 事件在用户离开当前页面打开新页面时发生。要处理这些事件,只需添加下面所示名称的 JavaScript 函数即可:
function pageLoad() {
alert("Begin loaded");
}
function pageUnload() {
alert("Begin unloaded");
}
Application 类还提供了一个 init 事件,它在所有脚本都已经被加载但对象还没有创建时发生。init 事件只在页面第一次被处理时发生一次。异步回发后不会发生。通过如下代码可以将事件处理程序附加到 init 事件上:
Sys.Application.add_init(ShowInit);
2. PageRequestManager 类
另一个同样很重要的类就是 PageRequestManager 。如果页面支持局部呈现,并在服务器端使用了一个或多个 UpdatePanel 控件,PageRequestManager 类就会被创建。PageRequestManager 类引发一组事件,你可以在客户端 JavaScript 代码里响应它们。
PageRequestManager 事件表
initializeRequest | 异步回发开始前发生。此时,可通过传送到事件处理程序的 Sys.WebForms.InitializeRequestEventArgs 对象的 Cancel 属性取消回发 |
beginRequest | 在异步回发请求发送前发生,但在 initializeRequest 之后。此时,可初始化页面的等候指示器等工作。该事件提供一个 Sys.WebForms.BeginRequestEventArgs 对象,通过它可以确定是哪一个元素引发了回发 |
pageLoading | 在异步回发请求接收到响应之后但页面被更新前发生。此时,可移除等候指示器。该事件提供一个 Sys.WebForms.PageLoadingEventArgs 对象,它提供作为异步回发响应结果的要更新的面板信息 |
pageLoaded | 在异步回发请求接收到响应之后且页面被更新后发生。该事件提供一个 Sys.WebForms.PageLoadedEventArgs 对象,它详细描述哪些面板已被更新和创建 |
endRequest | 在异步响应被处理后(在 pageLoaded 事件之后)或在处理响应过程中(发生错误时)发生。可以在这时候检查错误并提供定制的错误通知。该事件提供一个 Sys.WebForms.EndRequestEventArgs 对象,详细描述发生的错误 |
3. 客户端 AJAX 控件
欲详细了解完整的网页框架技术,可以参考下面地址:
这一部分,我打算介绍一个来自 ASP.NET AJAX 文档的示例:一个鼠标滑过时会更新自己外观的按钮。为了便于阻止,这个按钮的所有代码被放在 JavaScript 文件 HoverButton.js 中。
通过之前介绍过的原型模式创建这个控件。注册命名空间、定义控件的构造函数、然后使用 protoType 属性定义公共接口。在这个示例中,类的名字是 HoverButton,它公开按钮被单击、鼠标滑过、鼠标移出时触发的事件。
这是代码的大体结构:
Type.registerNamespace("CustomControls");
CustomControls.HoverButton = function (element) {
CustomControls.HoverButton.initializeBase(this, [element]);
this._clickDelegate = null;
this._hoverDelegate = null;
this._unhoverDelegate = null;
}
CustomControls.HoverButton.prototype = { ... }
CustomControls.HoverButton.registerClass("CustomControls.HoverButton",Sys.UI.Control);
自定义控件的构造函数在开始时必须调用 initializeBase() ,它触发 Control 基类的构造函数!
原型包含获取和设置按钮文本以及 3 个事件附加处理程序的方法。ASP.NET AJAX 所包含的事件模型比单纯的 JavaScript 要高级。这个事件模型的一个优势是能处理浏览器兼容问题。
使用 addHandler() 和 removeHandler() 方法在 JavaScript 里注册和解除事件处理程序。下面是实现的大致代码:
CustomControls.HoverButton.prototype = {
get_text: function () {
return this.get_element().innerHTML;
},
set_text: function (value) {
this.get_element().innerHTML = value;
},
add_click: function (handler) {
this.get_events().addHandler("click", handler);
},
remove_click: function (handler) {
this.get_events().removeHandler("click", handler);
},
add_hover:function(){ ...},
remove_hover:function(){ ... }
...
initialize:function(){ ... },
dispose:function(){ ... },
...
}
HoverButton 类的原型里还有两个方法:initialize() 方法(它在创建 HoverButton 对象时被自动调用)以及一个对象被释放时所调用的 dispose() 方法。
initialize() 方法建立 HoverButton 类中定义的自定义事件与页面 JavaScript 事件间的链接。例如,下面这段代码建立 hover 事件,这样当鼠标在按钮上移动或按钮获得焦点时它就会发生:
var element = this.get_element();
if (this._hoverDelegate == null) {
this._hoverDelegate = Function.createDelegate(this, this._hoverHandler);
}
Sys.UI.DomEvent.addHandler(element, "mouseover", this._hoverDelegate);
Sys.UI.DomEvent.addHandler(element, "focus", this._hoverDelegate);
_hoverHandler 代理定义在原型的最后。它只是触发关联的事件处理程序:
_hoverHandler: function(event) {
var h = this.get_events().getHandler('hover');
if(h){
h(this,Sys.EventArgs.Empty);
}
}
最后,initialize() 方法调用 Control 类中的基方法 initialize();
CustomControls.HoverButton.callBaseMethod(this,"initialize");
dispose() 方法的任务比较简单。它只检查是否有事件处理程序,如果存在就移除它们。它为 hover 事件做这些事情:
var element = this.get_element();
if (this._hoverDelegate) {
Sys.UI.DomEvent.removeHandler(element, 'hover', this._hoverDelegate);
delete this._hoverDelegate;
}
它使用下面代码调用 dispose() 方法的基类实现结束:
CustomControls.HoverButton.callBaseMethod(this, 'dispose');
下面给出上述的完整代码,有兴趣的朋友可以仔细阅读一下:
Type.registerNamespace("CustomControls");
// Constructor
CustomControls.HoverButton = function (element) {
CustomControls.HoverButton.initializeBase(this, [element]);
this._clickDelegate = null;
this._hoverDelegate = null;
this._unhoverDelegate = null;
}
CustomControls.HoverButton.prototype = {
// text property accessors.
get_text: function () {
return this.get_element().innerHTML;
},
set_text: function (value) {
this.get_element().innerHTML = value;
},
// Bind and unbind to click event.
add_click: function (handler) {
this.get_events().addHandler('click', handler);
},
remove_click: function (handler) {
this.get_events().removeHandler('click', handler);
},
// Bind and unbind to hover event.
add_hover: function (handler) {
this.get_events().addHandler('hover', handler);
},
remove_hover: function (handler) {
this.get_events().removeHandler('hover', handler);
},
// Bind and unbind to unhover event.
add_unhover: function (handler) {
this.get_events().addHandler('unhover', handler);
},
remove_unhover: function (handler) {
this.get_events().removeHandler('unhover', handler);
},
// Release resources before control is disposed.
dispose: function () {
var element = this.get_element();
if (this._clickDelegate) {
Sys.UI.DomEvent.removeHandler(element, 'click', this._clickDelegate);
delete this._clickDelegate;
}
if (this._hoverDelegate) {
Sys.UI.DomEvent.removeHandler(element, 'focus', this._hoverDelegate);
Sys.UI.DomEvent.removeHandler(element, 'mouseover', this._hoverDelegate);
delete this._hoverDelegate;
}
if (this._unhoverDelegate) {
Sys.UI.DomEvent.removeHandler(element, 'blur', this._unhoverDelegate);
Sys.UI.DomEvent.removeHandler(element, 'mouseout', this._unhoverDelegate);
delete this._unhoverDelegate;
}
CustomControls.HoverButton.callBaseMethod(this, 'dispose');
},
initialize: function () {
var element = this.get_element();
if (!element.tabIndex) element.tabIndex = 0;
if (this._clickDelegate === null) {
this._clickDelegate = Function.createDelegate(this, this._clickHandler);
}
Sys.UI.DomEvent.addHandler(element, 'click', this._clickDelegate);
if (this._hoverDelegate === null) {
this._hoverDelegate = Function.createDelegate(this, this._hoverHandler);
}
Sys.UI.DomEvent.addHandler(element, 'mouseover', this._hoverDelegate);
Sys.UI.DomEvent.addHandler(element, 'focus', this._hoverDelegate);
if (this._unhoverDelegate === null) {
this._unhoverDelegate = Function.createDelegate(this, this._unhoverHandler);
}
Sys.UI.DomEvent.addHandler(element, 'mouseout', this._unhoverDelegate);
Sys.UI.DomEvent.addHandler(element, 'blur', this._unhoverDelegate);
CustomControls.HoverButton.callBaseMethod(this, 'initialize');
},
_clickHandler: function (event) {
var h = this.get_events().getHandler('click');
if (h) h(this, Sys.EventArgs.Empty);
},
_hoverHandler: function (event) {
var h = this.get_events().getHandler('hover');
if (h) h(this, Sys.EventArgs.Empty);
},
_unhoverHandler: function (event) {
var h = this.get_events().getHandler('unhover');
if (h) h(this, Sys.EventArgs.Empty);
}
}
CustomControls.HoverButton.registerClass('CustomControls.HoverButton', Sys.UI.Control);
// Since this script is not loaded by System.Web.Handlers.ScriptResourceHandler
// invoke Sys.Application.notifyScriptLoaded to notify ScriptManager
// that this is the end of the script.
if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
现在就可以在页面中使用它了。首先要把自己的脚本注册到 ScriptManager,如下所示:
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Scripts>
<asp:ScriptReference Path="~/HoverButton.js" />
</Scripts>
</asp:ScriptManager>
这保证了代码在 ASP.NET AJAX 客户端库之后被加载,并且能够完全访问客户端模型。这里只用一个最普通的按钮进行测试即可:
<button type="button" id="Button1" value="Click Me"></button>
最后,要创建客户端控件并关联事件处理程序。在页面第一次加载时,可以使用 ASP.NET AJAX $create 假名(它触发 Sys.UI.Component.create() 方法)创建控件。此时,需要提供控件的完全限定类名、其他要设置的属性(如文本、样式、事件处理程序)以及页面底层对象的引用(可以通过 $get 假名获得)。
这是创建 HoverButton、设置控件的初始属性、为 hover 事件附加处理程序的代码:
function pageLoad(sender, args) {
$create(CustomControls.HoverButton,
{ text: 'A HoverButton Control',
element: { style: { fontWeight: "bold", borderWidth: "2px"} }
},
{ click: start, hover: doSomethingOnHover, unhover: doSomethingOnUnHover },
null, $get('Button1'));
}
通过调用 $create 注册某个组件之后,你可以随时使用 $find 获取对它的引用。这个事件处理程序在鼠标滑过按钮时改变它的文本:
function doSomethingOnHover(sender, args) {
hoverMessage = "The mouse is over the button."
$get('HoverLabel').innerHTML = hoverMessage;
$find('Button1').set_text(hoverMessage);
}
知道 $find 和 $get 之间的区别非常重要。$get 假名获取页面的 HTML 元素(如<button>)。$find 假名获取一个完整的 ASP.NET AJAX 客户端组件(如 HoverButton 对象)。显然,要和自定义控件里的属性交互,就必须使用 $find 获取控件对象。
下面是页面测试类的所有代码,不包括样式代码:
<asp:ScriptManager runat="server" ID="ScriptManager1">
<Scripts>
<asp:ScriptReference Path="HoverButton.js" />
</Scripts>
</asp:ScriptManager>
<script type="text/javascript">
1:
2:
3: function pageLoad(sender, args) {
4: $create(CustomControls.HoverButton,
5: { text: 'A HoverButton Control',
6: element: { style: { fontWeight: "bold", borderWidth: "2px"} }
7: },
8: { click: start, hover: doSomethingOnHover, unhover: doSomethingOnUnHover },
9: null, $get('Button1'));
10: }
11:
12: function doSomethingOnHover(sender, args) {
13: hoverMessage = "The mouse is over the button."
14: $get('HoverLabel').innerHTML = hoverMessage;
15: $find('Button1').set_text(hoverMessage);
16: }
17:
18: function doSomethingOnUnHover(sender, args) {
19: $get('HoverLabel').innerHTML = "";
20: }
21:
22: function start(sender, args) {
23: alert("The start function handled the HoverButton click event.");
24: }
</script>
<button type="button" id="Button1"></button>
<div id="HoverLabel"></div>
测试效果:
创建客户端 ASP.NET AJAX 组件并不简单。虽然没有任何高层次的复杂度,但有很多细节要管理,松散类型 JavaScript 语言有限的错误捕获能力可能使调试变得一团混乱。
由于这些原因,大多数的 ASP.NET 开发人员倾向使用现成的服务器端控件以及那些已经提供了 ASP.NET AJAX 特性的组件,而不是自己开发。