在本文中,我们将共同讨论如何创建一个由基类Sys.Component派生的ASP.NET AJAX非可视化客户端组件,并将展示它在Web页面中的用法。
具体说来,你将学习如何实现:
◆使用prototype设计模式以JavaScript定义一个非可视化组件类。
◆把一个非可视化组件注册为一个由基类Component派生的类。
◆初始化该非可视化组件的基类—Component,并调用它的方法。
◆创建能够激发一个更改通知的属性。
◆打造一个完整的Demo.Timer非可视化组件。
◆在一个Web页面中使用该组件,并且绑定到它的事件。
◆把一个非可视化组件注册为一个由基类Component派生的类。
◆初始化该非可视化组件的基类—Component,并调用它的方法。
◆创建能够激发一个更改通知的属性。
◆打造一个完整的Demo.Timer非可视化组件。
◆在一个Web页面中使用该组件,并且绑定到它的事件。
注意,在ASP.NET AJAX 1.0框架中构建客户端组件,除了本文中介绍的方法(这些组件派生自Component)外,还存在另外两种类型的扩展基本组件功能的ASP.NET AJAX客户端组件对象:派生自Sys.UI.Behavior的Behavior和派生自Sys.UI.Control的Control。下列表格概括 了Component,Behavior和Control之间的区别。
另外,为了运行本文中的示例,你需要具备下列条件:
◆一个测试网站;
◆安装微软ASP.NET AJAX 1.0框架,在此不再赘述。
◆安装微软ASP.NET AJAX 1.0框架,在此不再赘述。
二、非可视化客户端组件的基本功能
一个ASP.NET AJAX非可视化客户端组件对将重用于应用程序中的JavaScript代码加以封装。非可视化组件的一个典型的示例是一个每隔一定时间激发事件的定时器组件。
通过派生于Component基类,你的定制组件能够自动地继承其中的许多特征,具体包括:
◆一个跨浏览器模型—用于管理绑定到客户端对象事件的处理器。
◆在客户端应用程序中把该组件自动注册为一个实现了Sys.IDisposable接口的可释放的对象。
◆当属性改变时激发通知事件。
◆实现组件属性设置的批处理,这比通过单个属性的get和set访问器来处理所有的逻辑更为有效(在脚本大小和处理时间方面)。
◆重载Sys.UI.Control.initialize方法以便初始化所有属性和事件监听器。
◆在客户端应用程序中把该组件自动注册为一个实现了Sys.IDisposable接口的可释放的对象。
◆当属性改变时激发通知事件。
◆实现组件属性设置的批处理,这比通过单个属性的get和set访问器来处理所有的逻辑更为有效(在脚本大小和处理时间方面)。
◆重载Sys.UI.Control.initialize方法以便初始化所有属性和事件监听器。
三、实现一个派生自Component的客户端组件
下面的列表概括了实现一个派生自Component的定制客户端组件需要的步骤。后面,我们将作详细分析。
◆使用prototype设计模式定义一个组件类。
◆初始化该组件的基组件实例。
◆暴露所有的属性访问器,并且选择性地激发一个propertyChanged通知事件。
◆重载dispose方法以便释放资源,例如clearing事件处理器。
◆初始化该组件的基组件实例。
◆暴露所有的属性访问器,并且选择性地激发一个propertyChanged通知事件。
◆重载dispose方法以便释放资源,例如clearing事件处理器。
(一)使用Prototype设计模式定义一个组件类
一个ASP.NET AJAX客户端类(它包括一个组件类)通过JavaScript使用prototype设计模式进行定义。下面的列表归纳了使用prototype设计模式定义一个组件类所需要的步骤:
◆注册你的组件类的命名空间。
◆创建该组件的构造器函数,并且在该构造器函数中定义任何的private字段,同时设置它们的初始值。
◆使用prototype设计模式定义该组件的原型。
◆把该组件函数注册为一个派生自Component的类。
◆创建该组件的构造器函数,并且在该构造器函数中定义任何的private字段,同时设置它们的初始值。
◆使用prototype设计模式定义该组件的原型。
◆把该组件函数注册为一个派生自Component的类。
(二)初始化基类
任何派生自一个基组件类(例如组件,控件,或行为)的组件类都必须在其构造器中初始化它的基类。这使该基类对象能够执行初始化任务—例如把该组件注册为一个可释放的对象—使用Sys.Application实例。
在该组件的构造器函数中,你可以调用继承的Type.initializeBase方法,—典型地,是在该构造器中的任何其它代码运行之前进行。该 initializeBase方法初始化一个注册类的基类型。一个非可视化组件类被注册为一个类—拥有一个Component基类型。当该 Component基类被初始化时,它的方法可用于该组件中,并且它会把该组件自动地注册为ASP.NET AJAX应用程序中一个可释放的对象。
下列示例展示了一个派生自Component的非可视化组件的构造器函数。该构造器调用了它继承的initializeBase方法。
Samples.SimpleComponent = function()
{ Samples.SimpleComponent.initializeBase(this); } |
(三)定义属性并激发propertyChanged通知
通常情况下,你在希望页面开发者绑定到的组件的类中进行属性定义。另外,你还可以选择从你的组件的属性中激发propertyChanged通知事 件。然后,使用你的组件页面开发者就可以绑定到这些事件上。一个派生自Component,Behavior或Control的ASP.NET AJAX组件继承该raisePropertyChanged方法,调用此方法将激发一个propertyChanged事件。
(四)初始化属性和事件监听器
如果你的定制组件必须初始化任何属性或事件监听器的话,那么,你可以在该组件的原型中重载Sys.UI.Control.initialize方 法。例如,一个派生自Component的非可视化组件可能会一个委托委托给一个事件—例如Window.onFocus。典型地,一个派生自 Control基类的客户端控件会把任何代理绑定到它的DOM元素事件上,并且把这些DOM元素属性设置为初始值。最后一步,你需要调用基类的 initialize方法以便使组件的基类最终完成初始化。
(五)释放资源
如果在释放该组件之前你的定制组件必须释放资源,那么,你需要重载dispose方法,在该重载方法中释放该资源。这可以确保在释放该组件前立即释 放该资源。应该释放的资源可能包括用于绑定到DOM事件的代理。通过确保去除存在于DOM元素与该组件对象之间的循环参考,你可以使该对象彻底从内存中删 除。
四、在Web页面中应用非可视化组件
为了在一个ASP.NET AJAX应用程序页面中使用前面创建的定制客户端组件,你需要下列步骤:
◆在Web页面中注册该组件的脚本库。
◆创建一个组件实例。
◆创建一个组件实例。
下面我们来详细讨论这些步骤。
(一)在Web页面中注册组件的脚本库
我们可以通过声明方式或编程方法通过一个ScriptManager控件来注册在页面中使用一个客户端控件所需要的脚本。
【注意】客户端组件可以受托管于一个实现了IScriptControl接口的定制服务器控件。这样的一个定制服务器控件可以自动地注册要求的组件脚本并且暴露设置组件属性和事件绑定的声明性标记。这样将使得页面开发者更容易使用此客户端组件。
下面的示例展示了一个注册组件脚本的ScriptManager控件的声明性标记:
<form id="form1" runat="server"> |
在此,元素的
【特别注意】所有要使用ScriptManager控件进行注册的脚本文件都必须调用notifyScriptLoaded方法以通知应用程序该脚本已经加载完毕。大多数情况下,嵌入式于一个程序集内的脚本不应该调用这个方法。
(二)创建定制组件实例
你可以通过调用Sys.Component.create方法(或$create快捷方式)来实例化一个客户端组件。为此,你需要把相应的参数传递 到$create方法以指定该组件的类型。你还要传递一个JSON对象,这个JSON对象用于包含一个要求的ID值以及可选的初始属性值和事件处理器绑 定。
下面这个示例展示了如何通过调用$create方法来实例化一个组件实例。
function pageLoad()
{ $create(Demo.Timer, {enabled:true,id:"demoTimer1", interval:2000}, {tick:OnTick}, null); } |
五、打造定制的Demo.Timer组件
从现在开始,我们来创建一个名字为Demo.Timer的定制客户端组件,然后把它应用于一个Web页面中。这里的Demo.Timer是一个简单 的定时器组件,它能够根据interval属性激发一个propertyChanged事件,并暴露一个enabled属性,还定义了一个tick事件。 使用这个Demo.Timer组件的页面开发者可以绑定到propertyChanged事件并且在每次更新interval属性时采取自己的行动。当 然,开发者还可以处理tick事件。
为了创建该Demo.Timer组件的代码,我们需要:
1.在ASP.NET AJAX应用程序的目录下创建一个名字为DemoTimer.js的文件。
2.然后,把下列JavaScript代码添加到该文件中:
2.然后,把下列JavaScript代码添加到该文件中:
Type.registerNamespace("Demo");
添加事件的处理器Demo.Timer = function() { Demo.Timer.initializeBase(this); this._interval = 1000; this._enabled = false; this._timer = null; } Demo.Timer.prototype = { //声明该prototype中的值类型 get_interval: function() { /// 以毫秒为单位的时间间隔值 return this._interval; }, set_interval: function(value) { if (this._interval !== value) { this._interval = value; this.raisePropertyChanged('interval'); if (!this.get_isUpdating() && (this._timer !== null)) { this._restartTimer(); } } }, get_enabled: function() { /// 当启动定时器时为True;否则为false。 return this._enabled; }, set_enabled: function(value) { if (value !== this.get_enabled()) { this._enabled = value; this.raisePropertyChanged('enabled'); if (!this.get_isUpdating()) { if (value) { this._startTimer(); } else { this._stopTimer(); } } } }, //事件 add_tick: function(handler) { /// 增加一个相应于tick事件的事件处理器 /// this.get_events().addHandler("tick", handler); }, remove_tick: function(handler) { ///删除tick事件的一个事件处理器 /// 删除事件的处理器 this.get_events().removeHandler("tick", handler); }, dispose: function() { //调用set_enabled以便激发属性改变事件 this.set_enabled(false); //保证的确停止了,这样在释放后就不必调用了 this._stopTimer(); //确保调用base.dispose() Demo.Timer.callBaseMethod(this, 'dispose'); }, updated: function() { Demo.Timer.callBaseMethod(this, 'updated'); //在批更新(this.beginUpdate(), this.endUpdate())后调用 if (this._enabled) { this._restartTimer(); } }, _timerCallback: function() { var handler = this.get_events().getHandler("tick"); if (handler) { handler(this, Sys.EventArgs.Empty); } }, _restartTimer: function() { this._stopTimer(); this._startTimer(); }, _startTimer: function() { //保存定时器cookie便于以后删除之用 this._timer = window.setInterval(Function.createDelegate(this, this._timerCallback), this._interval);
}, _stopTimer: function() { if(this._timer) { window.clearInterval(this._timer); this._timer = null; } } } //描述这个组件所有的属性、事件和方法的JSON对象。 //注意,此组件应该能够通过Sys.TypeDescriptor方法和通过xml-script进行寻址。 Demo.Timer.descriptor = { properties: [ {name: 'interval', type: Number}, {name: 'enabled', type: Boolean} ], events: [ {name: 'tick'} ] } Demo.Timer.registerClass('Demo.Timer', Sys.Component); //既然这个脚本不是通过System.Web.Handlers.ScriptResourceHandler //加载的,所以,我们在此调用 // Sys.Application.notifyScriptLoaded来通知ScriptManager。 //这是脚本的结束处,根据前面的揭示,这一句必须有 if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded(); |
【注】上面的JavaScript脚本集中体现了ASP.NET AJAX 1.0框架对于客户端JavaScript编程进行的简化。很显然,这里仍然基于JavaScript脚本语言中的prototype模型,但已对之进行了面向对象的进一步包装。
上面的代码首先通过调用Type.registerNamespace方法实现注册Demo命名空间。ASP.NET AJAX开发小组建议,最好在构造器中声明和初始化所有的private域,例如在这个示例中初始化了interval域。接下来,构造器调用继承的 initializeBase方法,以便使用基类Component中的方法。然后,被初始化的基类使用客户端应用程序把此Demo.Timer实例注册 为一个可释放的对象。
在这个prototype中,声明并初始化private域(例如interval)。在此,这个prototype定义了两个public属性: interval和enabled;还定义了针对每一个属性的get和set访问器方法。在每一个public属性的set访问器方法中,通过调用 propertyChanged方法激活一个propertyChanged事件。这样以来,每次改变此属性时,页面开发者都会得到通知。
上面的add_tick和remove_tick方法支持页面开发者添加和删除监听tick事件的委托处理器。这些方法通过该组件的事件处理器集合 来添加或删除指定的处理器。你可以通过组件的Sys.EventHandlerList对象来添加和删除你的组件类中的委托处理器。 EventHandlerList对象包含一个该组件的事件处理器的集合—通过继承的Sys.Component.events属性实现。在本示例中,该 代码调用返回的EventHandlerList对象的Sys.EventHandlerList.addHandler和 Sys.EventHandlerList.removeHandler方法,以便为了添加或删除指定的处理器。
这个Demo.Timer类重载了基类的dispose方法以便更新enabled属性并且向开发人员指示此组件已经被禁用。通过enabled属 性的set访问器可以激发propertyChanged事件以便发送通知。然后,调用private型_stopTimer方法以停止激发tick事 件。最后,调用基类中的dispose方法来支持应用程序释放掉该组件。
六、在Web页面中应用Demo.Timer组件
在一个页面中的客户端组件实例可以托管给一个服务器控件或由Web页面中的客户端脚本所使用。现在,我们来讨论如何创建一个组件实例—通过在Web页面中使用客户端脚本的方式。
启动Visual Studio 2005,选择“文件→新建网站…”,然后选择“ASP.NET AJAX-Enabled Web Site”模板,命名工程为“NoVisComponentTest”,并选择C#作为编程语言,最后点击OK。
把文件名Default.aspx更改为DemoTimer.aspx。然后,把该文件源码内容修改为:
<%@ Page Language="C#" %>
<%@ Register Assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
Namespace="System.Web.UI" TagPrefix="asp" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"[url]http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd[/url]">
<html xmlns="[url]http://www.w3.org/1999/xhtml[/url]"> <head id="Head1" runat="server"> <title>ASP.NET AJAX客户端非可视化组件编程测试</title> </head> <body> <form id="form1" runat="server"> <div> <br /> <h1> <span style="color: #000099">【ASP.NET AJAX客户端非可视化组件编程测试】</span><br /> </h1> <asp:ScriptManager id="ScriptManager1" runat="server" > <Scripts> <asp:ScriptReference Path="DemoTimer.js" /> </Scripts> </asp:ScriptManager> <span style="font-size: 32pt"> 定时器开始计数: <span style="color: #ff3333"><strong>
<span id="result">0</span> </strong></span></span> </div> <script type="text/javascript">
function OnTick(sender, args) { var result = $get("result"); result.innerText = parseInt(result.innerText) + 1; } var app = Sys.Application; app.add_load(applicationLoadHandler); function applicationLoadHandler(sender, args) { //创建DemoTimer组件实例。 //设置属性并绑定事件。 $create(Demo.Timer, {enabled:true,id:"demoTimer1",interval:2000}, {tick:OnTick}, null, null); } </script> </form> </body> </html> |
相应于DemoTimer.aspx页面的设计时刻快照如下图1所示:
图1:示例网页的设计时刻快照 |
在该示例页面中,在元素中定义了两个函数。其中,OnTick函数是一个将被绑定到Demo.Timer组件的tick事件的委托。这个OnTick函数负责处理tick事件并更新位于一个HTML元素中的计数器值。
在applicationLoadHandler事件处理器中,Demo.Timer组件在客户端脚本中被实例化—通过调用$Create方法并传递如下参数实现:
◆第一个类型参数被指定为你在前面创建的Demo.Timer类;
◆属性参数中包含了一个JSON对象—包含要求的组件ID值,后面跟着一 些可选的属性名字值对,用于指定属性的初始值。其中,interval属性被初始设置为2000毫秒;这样,每隔2秒定时器就会激发一个tick事件。该 组件的enabled属性被置为true;这样以来,一旦实例化它后,就立即启动它。
◆最后的可选事件参数中包含一个对象,该对象提供了与其初始事件处理器代理相匹配的事件名称。在本文示例中,该tick事件被委托给一个初始的onTick委托(定义于页面的元素中)。
◆属性参数中包含了一个JSON对象—包含要求的组件ID值,后面跟着一 些可选的属性名字值对,用于指定属性的初始值。其中,interval属性被初始设置为2000毫秒;这样,每隔2秒定时器就会激发一个tick事件。该 组件的enabled属性被置为true;这样以来,一旦实例化它后,就立即启动它。
◆最后的可选事件参数中包含一个对象,该对象提供了与其初始事件处理器代理相匹配的事件名称。在本文示例中,该tick事件被委托给一个初始的onTick委托(定义于页面的元素中)。
此外,在前面的ScriptManager控件中,结点的path属性参考定义Demo.Timer组件类的DemoTimer.js文件的路径。
【注意】由于新框架对于客户端JavaScript编程作了较大规模调整,所以请读者详细研究上面源码中的代码结构。
下图2展示了示例网页运行时刻快照。
下图2展示了示例网页运行时刻快照。
图2:示例网页运行时刻快照 |
注意到:每隔2秒钟,定时器就会激发一个tick事件,红色计数值加1。
七、总结
从本文中,我们看到,ASP.NET AJAX 1.0框架并不仅仅对AJAX编程中的构建客户端进行了简化,还提供了简化的面向对象JavaScript模型,从而有助于开发人员进一步开发出属于自己 的客户端定制组件。除了本文中介绍的方法(这些组件派生自Component)外,还存在另外两种类型的扩展基本组件功能的ASP.NET AJAX客户端组件对象:派生自Sys.UI.Behavior的Behavior和派生自Sys.UI.Control的Control。 ASP.NET AJAX 1.0框架帮助文档中提供了相应的示例,在此不再赘述。
本文转自朱先忠老师51CTO博客,原文链接:http://blog.51cto.com/zhuxianzhong/59829 ,如需转载请自行联系原作者