更新:2007 年 11 月
本主题演示如何在 ASP.NET 中创建一个从客户端 Sys.Component 基类派生的 AJAX 非可视客户端组件,以及如何在页中使用该组件。
在本教程中,您将了解如何执行以下操作:
-
使用原型设计模式通过 ECMAScript (JavaScript) 定义一个非可视组件类。
-
将非可视组件注册为从 Component 基类派生的类。
-
初始化非可视组件的 Component 基类并调用其方法。
-
创建引发更改通知的属性。
-
在页中使用组件并绑定到组件的事件。
本概述提供一个计时器示例作为非可视客户端组件。该计时器引发可以处理的事件。
本主题重点介绍基于非可视客户端组件的对象。这些组件从 Component 派生且通常没有 UI 表示形式。还有另外两种类型的、扩展基本组件功能的 ASP.NET AJAX 客户端组件对象:从 Sys.UI.Behavior 派生的行为和从 Sys.UI.Control 派生的控件。下表总结了组件、行为和控件之间的区别。
客户端组件对象类型 |
摘要 |
---|---|
组件 |
|
行为 |
|
控件 |
|

若要运行本主题中的示例,您需要:
-
一个支持 AJAX 的 ASP.NET 网站。如果您有一个已配置好的网站,则可以在本示例中使用该网站。有关如何创建虚拟目录或网站的信息,请参见如何:在 IIS 5.0 和 6.0 中创建和配置虚拟目录。

ASP.NET AJAX 非可视客户端组件封装可跨应用程序重用的 JavaScript 代码。一个按照设定的时间间隔引发事件的计时器组件便是非可视组件的一个示例。
通过从 Component 基类派生,自定义组件会自动继承以下功能:
-
用于管理与客户端对象事件的处理程序绑定的跨浏览器模型。
-
客户端应用程序中的组件的自动注册(作为实现 Sys.IDisposable 接口的可释放对象)。
-
在属性发生更改时引发通知事件的功能。
-
对组件属性设置执行批处理操作的功能。与在各个属性 get 和 set 访问器中处理所有逻辑相比,此操作在脚本大小和处理时间上更有效。
-
重写 Sys.Component.initialize 方法以初始化任何属性和事件侦听器。

若要实现从 Component 派生的自定义客户端组件,请按下列步骤操作:
-
使用原型设计模式定义一个组件类。
-
初始化组件的基 Component 实例。
-
公开所有属性访问器,并可以选择引发 propertyChanged 通知事件。
-
重写 dispose 方法以释放资源,例如清除事件处理程序。
以下各节提供了有关实现步骤的详细信息。
使用原型设计模式定义组件类
ASP.NET AJAX 客户端类(其中包含一个组件类)是使用原型设计模式通过 JavaScript 定义的。若要通过使用原型设计模式定义组件类,请执行以下操作:
-
注册组件类的命名空间。
-
创建组件的构造函数,并在构造函数中定义所有私有字段并设置其初始值。
-
定义组件的原型。
-
将组件函数注册为从 Component 派生的类。
有关更多信息,请参见使用原型模型创建客户端组件类。
初始化基类
在组件的构造函数中,调用继承的 Type.initializeBase 方法可初始化注册的类的基类型。可将非可视组件类注册为其基类型为 Component 的类。如果 Component 基类已初始化,则其方法可供组件使用,并且它会自动在支持 AJAX 的 ASP.NET 应用程序中将组件注册为可释放对象。有关更多信息,请参见 Sys.IDisposable 接口。
从 Component 派生的任何组件类都必须使用构造函数初始化其基类。在构造函数中运行任何其他代码之前,通常先调用 initializeBase。下面的示例演示一个从 Component 派生的非可视组件的构造函数。
Samples.SimpleComponent = function() { Samples.SimpleComponent.initializeBase(this); }
定义属性和引发属性更改通知
在页开发人员可以获取和设置的组件类中定义属性。从 Component 派生的 ASP.NET AJAX 组件继承 Sys.Component.raisePropertyChanged 方法,您可以调用该方法以便为您的组件属性引发 propertyChanged 事件。然后,使用您的组件的网页开发人员可以绑定到这些事件。有关更多信息,请参见定义自定义组件属性和引发 PropertyChanged 事件。
初始化属性和事件侦听器
如果自定义组件必须初始化任何属性或事件侦听器,则应在组件的原型中重写 Sys.Component.initialize 方法。例如,从 Component 派生的非可视组件可能会将某个委托分配给一个事件(如 window.onFocus)。最后,调用 initialize 基方法以使组件的基类完成初始化。
ASP.NET 提供了可以为组件和 DOM 元素提供标准事件管理的类和方法。若要管理组件的事件,请使用 Sys.EventHandlerList 类。例如,可以使用 Sys.EventHandlerList.addHandler 方法绑定事件,并使用 Sys.EventHandlerList.removeHandler 方法释放事件。有关更多信息,请参见 Sys.EventHandlerList 类。
若要管理 DOM 元素或 window 对象的事件处理程序,请使用 Sys.UI.DomEvent 类。例如,可以使用 Sys.UI.DomEvent addHandler 和 Sys.UI.DomEvent removeHandler 方法绑定和取消绑定事件处理程序。有关更多信息,请参见 Sys.UI.DomEvent 类。
释放资源
如果自定义组件在释放组件之前必须释放资源,则重写 dispose 方法并释放重写的方法中的资源。这样可确保恰好在释放组件之前释放资源。应释放的资源可能包含 DOM 事件的处理程序。通过验证 DOM 元素和组件对象之间任何可能的循环引用是否已被移除,确保可以从内存中移除该对象。有关更多信息,请参见释放组件资源。

若要在 ASP.NET AJAX 应用程序页中使用自定义客户端组件,请执行以下操作:
-
在网页中注册组件的脚本库。
-
创建一个组件实例。
以下各节提供了有关这些步骤的详细信息。
在网页中注册组件的脚本库
可以使用 ScriptManager 控件,通过声明方式或编程方式来注册页上的客户端控件所需的脚本。下面的示例演示注册组件脚本的 ScriptManager 控件的声明性标记。
<form id="form1" runat="server"> <asp:ScriptManager runat="server" ID="ScriptManager01"> <scripts> <asp:ScriptReference path="DemoTimer.js" /> </scripts> </asp:ScriptManager> </form>
asp:ScriptManager 元素在 scripts 节点内包含 asp:ScriptReference 元素。asp:ScriptReference 元素的 path 属性引用定义组件类的 .js 文件(在本示例中为 DemoTimer.js 文件)的路径。有关更多信息,请参见动态分配脚本引用和 ScriptManager 类概述。
作为使用 ScriptManager 控件注册脚本文件的替代方法,可以使用实现 IScriptControl 接口的自定义服务器控件来管理客户端组件。自定义服务器控件可以自动注册所需的组件脚本,并公开用于设置组件属性和事件绑定的声明性标记。如果您使用自定义服务器控件注册脚本,则页开发人员可以更加方便地使用您的组件。有关更多信息,请参见 IScriptControl 类概述。
![]() |
---|
若要使用 ScriptManager 控件注册的所有独立脚本文件都必须调用 notifyScriptLoaded 方法,以便通知应用程序脚本已完成加载。嵌入在程序集中的脚本在大多数情况下不应调用此方法。有关更多信息,请参见 Sys.Application.notifyScriptLoaded 方法。 |
创建自定义组件实例
可通过调用 Sys.Component.create 方法或 $create 快捷方式实例化客户端组件。可向 $create 方法传递参数来指定组件类型。此外,还可传递一个 JSON 对象,并在其中包含必需的 ID 值、可选的初始属性值和可选的事件处理程序绑定。
下面的示例演示如何通过调用 $create 方法实例化组件实例。
var app = Sys.Application; app.add_init(applicationInitHandler); function applicationInitHandler(sender, args) { $create(Demo.Timer, {enabled:true,id:"demoTimer1", interval:2000}, {tick:OnTick}, null); }
有关更多信息,请参见Sys.Component.create 方法和Sys.Component $create 方法。

在本节中,您将创建一个扩展 Component 基类的名为 Demo.Timer 的自定义客户端组件,然后在页中使用该组件。Demo.Timer 是一个简单的计时器组件,可定义 tick 事件,公开 enabled 属性和 interval 属性,以及针对 interval 属性引发更改通知事件。使用 Demo.Timer 组件的页开发人员可以处理 tick 事件。开发人员还可以绑定到 property-changed 事件,以便每次更新 interval 属性时执行操作。
为 Demo.Timer 组件创建代码
-
在支持 AJAX 的 ASP.NET Web 应用程序的根目录中,创建一个名为 DemoTimer.js 的文件。
-
向文件中添加以下代码:
Type.registerNamespace("Demo"); Demo.Timer = function() { Demo.Timer.initializeBase(this); this._interval = 1000; this._enabled = false; this._timer = null; } Demo.Timer.prototype = { // OK to declare value types in the prototype get_interval: function() { /// <value type="Number">Interval in milliseconds</value> 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() { /// <value type="Boolean">True if timer is enabled, false if disabled.</value> 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(); } } } }, // events add_tick: function(handler) { /// <summary>Adds a event handler for the tick event.</summary> /// <param name="handler" type="Function">The handler to add to the event.</param> this.get_events().addHandler("tick", handler); }, remove_tick: function(handler) { /// <summary>Removes a event handler for the tick event.</summary> /// <param name="handler" type="Function">The handler to remove from the event.</param> this.get_events().removeHandler("tick", handler); }, dispose: function() { // call set_enabled so the property changed event fires, for potentially attached listeners. this.set_enabled(false); // make sure it stopped so we aren't called after disposal this._stopTimer(); // be sure to call base.dispose() Demo.Timer.callBaseMethod(this, 'dispose'); }, updated: function() { Demo.Timer.callBaseMethod(this, 'updated'); // called after batch updates, 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() { // save timer cookie for removal later this._timer = window.setInterval(Function.createDelegate(this, this._timerCallback), this._interval); }, _stopTimer: function() { if(this._timer) { window.clearInterval(this._timer); this._timer = null; } } } Demo.Timer.registerClass('Demo.Timer', Sys.Component); // 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();
代码讨论
代码通过调用 Type.registerNamespace 方法注册 Demo 命名空间。建议您在构造函数中声明并初始化所有私有字段,例如,本示例中的 interval。构造函数调用继承的 initializeBase 方法,以便可以使用 Component 基类方法。初始化后的基类又在客户端应用程序中将 Demo.Timer 实例注册为一个可释放对象。
在原型中,代码声明并初始化两个公共属性:interval 和 enabled。属性定义包含用于存储属性值的私有字段,以及每个属性的 get 和 set 访问器。在每个公共属性的 set 访问器方法中,代码通过调用 raisePropertyChanged 方法引发 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 事件来发送通知。代码调用私有 _stopTimer 方法阻止引发 tick 事件。最后,代码调用基 dispose 方法以使应用程序可以释放该组件。

可以通过自定义服务器控件或通过在网页中使用客户端脚本,管理页中的 ASP.NET AJAX 客户端组件的实例。在本节中,您将学习如何使用网页中的客户端脚本创建组件实例。
创建使用 Demo.Timer 组件的页
-
在放置 DemoTimer.js 文件的目录中,创建一个名为 DemoTimer.aspx 的文件并在其中添加下列标记和代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www./TR/xhtml11/DTD/xhtml11.dtd"> <html > <head runat="server"> <title>Demo Timer Component</title> </head> <body> <form id="form1" runat="server"> <div> <asp:ScriptManager ID="ScriptManager1" runat="server"> <Scripts> <asp:ScriptReference Path="DemoTimer.js"/> </Scripts> </asp:ScriptManager> Timer Tick Count: <span id="result">0</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_init(applicationInitHandler); function applicationInitHandler(sender, args) { // Create the DemoTimer component instance. // Set properties and bind events. $create(Demo.Timer, {enabled:true,id:"demoTimer1",interval:2000}, {tick:OnTick}, null, null); } </script> </form> </body> </html>
-
在同一目录中,创建一个名为 TestDemoTimer.js 的文件并在其中添加以下代码:
function OnTick(sender, args) { var result = $get("result"); result.innerText = parseInt(result.innerText) + 1; } var app = Sys.Application; app.add_init(applicationInitHandler); function applicationInitHandler(sender, args) { // Create the DemoTimer component instance. // Set properties and bind events. $create(Demo.Timer, {enabled:true,id:"demoTimer1",interval:2000}, {tick:OnTick}, null, null); }
代码讨论
示例页通过使用包含两个函数(OnTick 和 applicationInitHandler)的 JavaScript 代码加载 TestDemoTimer.js。OnTick 函数处理 Demo.Timer 组件的 tick 事件并更新 <span> HTML 元素中的计数器值。
applicationInitHandler 函数是 app_init 事件的处理程序。在该函数中,通过调用 $create 方法并传递下列参数,在客户端脚本中实例化 Demo.Timer 组件:
-
type 参数是先前创建的 Demo.Timer 类。
-
properties 参数由 JSON 对象组成,该对象包含必需的组件 ID 值,后跟使用初始值指定属性名称的属性名称/值对。出于演示的目的,interval 属性最初设置为 2000 毫秒,以便计时器每两秒引发一次 tick 事件。(在成品应用程序中,可能会将间隔设置为较大值,以减少网络通信量。)将该组件的 enabled 属性设置为 true,以便在实例化该组件之后立即启动计时器。
-
events 参数包含一个对象,该对象包含与事件处理程序配对的事件名称。在这种情况下,onTick 处理程序将分配给在页的 script 元素中定义的 tick 事件。
DemoTimer.aspx 文件是一个承载该组件的 ASP.NET 网页。在页的 ScriptManager 控件中,<asp:ScriptReference> 元素的 path 属性引用定义 Demo.Timer 组件类的 DemoTimer.js 文件的路径。