建立自訂的隱藏式用戶端元件

更新:2007 年 11 月

此主題說明如何在從用戶端 Sys.Component 基底類別 (Base Class) 衍生的 ASP.NET 中建立 AJAX 隱藏式 (Non-Visual) 用戶端元件,以及如何在網頁中使用該元件。

在此教學課程中,您將了解如何進行下列作業:

  • 使用原型 (Prototype) 設計模式以 ECMAScript (JavaScript) 定義隱藏式元件類別。

  • 將隱藏式元件註冊為從 Component 基底類別衍生的類別。

  • 初始化隱藏式元件的基底 Component 類別並叫用 (Invoke) 其方法。

  • 建立會引發變更通知的屬性。

  • 在網頁中使用該元件,並繫結到元件的事件。

概觀提供做為隱藏式元件的計時器範例。此計時器會引發您可以處理的事件。

本主題著重在用戶端隱藏式元件型物件。這些元件從 Component 衍生,而且通常沒有 UI 表示。有其他兩種 ASP.NET AJAX 用戶端元件物件的類型可以擴充基本元件功能:從 Sys.UI.Behavior 衍生的行為,以及從 Sys.UI.Control 衍生的控制項。下表摘要說明元件、行為與控制項之間的差異。

用戶端元件物件型別

摘要

元件

  • Component 基底類別衍生。

  • 通常沒有 UI 表示,例如計時器元件會定期引發事件,但不會在網頁上看到此元件。

  • 沒有關聯的 DOM 項目。

  • 封裝用戶端程式碼,以便供應用程式重複使用。

行為

  • Behavior 基底類別衍生,該類別延伸 Component 基底類別。

  • 延伸 DOM 項目的行為,例如可附加到現有的文字方塊的浮水印行為。

  • 可以建立 UI 項目,雖然它們通常不會修改關聯之 DOM 項目的基本行為。

  • 可從 DOM 項目透過自訂屬性 (expando) 直接存取。如果已設定行為,屬性將有行為名稱,否則將有型別名稱 (不完整)。

  • 不需要與另一個用戶端物件的關聯,例如從 ControlBehavior 類別衍生的類別。

  • 可以在其 element 屬性參考控制項或非控制項 HTML 項目。

控制項

  • Control 基底類別衍生,該類別延伸 Component 基底類別。

  • 代表做為用戶端物件的 DOM 項目,通常會變更原始 DOM 項目的一般行為以提供新功能。例如,功能表控制項可能會從 ul 項目 (Element) 讀取 li 項目 (Item),以做為其來源資料,但不會顯示項目符號清單。

  • 可由 DOM 項目透過控制項 expando 直接存取。

必要條件

若要在此主題中執行範例,您需要:

建立隱藏式用戶端元件的基本功能

ASP.NET AJAX 隱藏式用戶端元件封裝 JavaScript 程式碼,以供應用程式重複使用。隱藏式元件的其中一個範例是會定期引發事件的計時器元件。

藉由衍生自 Component 基底類別,您的自訂元件會自動繼承下列功能:

  • 跨瀏覽器模型,可用於管理用戶端物件事件的處理常式繫結。

  • 自動在用戶端應用程式中將元件註冊為可處置物件,該物件實作 Sys.IDisposable 介面。

  • 當屬性變更時引發通知事件的功能。

  • 執行元件屬性設定批次處理的功能。對於指令碼大小與處理時間,這種方式比使用個別屬性 get 與 set 存取子 (Accessor) 處理所有邏輯來得有效率。

  • 覆寫 Sys.Component.initialize 方法以初始化任何屬性與事件接聽程式 (Event Listener)。

實作衍生自元件類別的用戶端元件

若要實作衍生自 Component 的自訂用戶端元件,您必須依照下列步驟:

  • 使用原型設計模式定義元件類別。

  • 初始化元件的基底 Component 執行個體 (Instance)。

  • 公開 (Expose) 任何屬性存取子,並選擇性地引發 propertyChanged 通知事件。

  • 覆寫 dispose 方法以釋放資源,例如清除事件處理常式。

以下章節提供關於實作步驟的詳細資訊。

使用原型設計模式定義元件類別

包含元件類別的 ASP.NET AJAX 用戶端類別是在 JavaScript 中以原型設計模式定義。若要使用原型設計模式定義元件類別,您必須執行下列動作:

  • 註冊元件類別的命名空間。

  • 建立元件的建構函式 (Constructor),然後在建構函式中定義任何私用欄位並設定其初始值。

  • 定義元件的原型。

  • 將元件函式註冊為衍生自 Component 的類別。

如需詳細資訊,請參閱使用原型模型建立用戶端元件類別

初始化基底類別

您可在元件的建構函式中叫用繼承的 Type.initializeBase 方法,以初始化已註冊之類別的基底型別 (Base Type)。隱藏式元件類別註冊為具有 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 addHandlerSys.UI.DomEvent removeHandler 方法來繫結或解除繫結事件處理常式。如需詳細資訊,請參閱Sys.UI.DomEvent 類別

釋放資源

若您的自訂元件必須在處置元件之前釋放資源,請覆寫 dispose 方法,並在覆寫方法中釋放資源。這樣可確保在處置元件之前會立即釋放資源。必須釋放的資源可能包含 DOM 事件的處理常式。透過驗證 DOM 項目與元件物件之間任何可能的循環參考都已移除,確保物件可從記憶體中移除。如需詳細資訊,請參閱釋放元件資源

在網頁中使用隱藏式元件

若要在 ASP.NET AJAX 應用程式網頁中使用自訂用戶端元件,您必須執行下列動作:

  • 在 Web 網頁中註冊元件的指令碼程式庫。

  • 建立元件執行個體。

以下章節提供關於這些步驟的詳細資訊。

在 Web 網頁中註冊元件的指令碼程式庫

您可以使用宣告方式或程式設計方式,在具有 ScriptManager 控制項的網頁上註冊用戶端控制項所需的指令碼。下列範例說明註冊元件指令碼之 ScriptManager 控制項的宣告式標記。

<form id="form1" >
  <asp:ScriptManager  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 方法,來通知應用程式指令碼已完成載入。在大部分情況下,內嵌在組件 (Assembly) 中的指令碼不應該呼叫此方法。如需詳細資訊,請參閱Sys.Application.notifyScriptLoaded 方法

建立自訂元件執行個體

您必須透過呼叫 Sys.Component.create 方法或 $create 捷徑來產生用戶端元件。您必須將參數傳遞到 $create 方法,以指定元件類型。您也必須傳遞包含必要 ID 值、選擇性初始屬性值與選擇性事件處理常式繫結的 JSON 物件。

下列範例說明如何透過呼叫 $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 方法

建立自訂 Demo.Timer 元件

在本節中,您將會建立名為 Demo.Timer 的自訂用戶端元件,它會擴充 Component 基底類別,然後在網頁中使用該元件。Demo.Timer 是一個簡單的計時器元件,它定義 tick 事件、公開 enabled 屬性與 interval 屬性,並引發 interval 屬性的變更通知事件。使用 Demo.Timer 元件的網頁開發人員可以處理 tick 事件。開發人員也可以繫結到屬性變更事件,並在每次 interval 屬性更新時採取動作。

建立 Demo.Timer 元件的程式碼

  1. 在具備 AJAX 能力的 ASP.NET Web 應用程式的根目錄中,建立名為 DemoTimer.js 的檔案。

  2. 將下列程式碼加入至該檔案中:

    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.addHandlerSys.EventHandlerList.removeHandler 方法,以加入或移除指定的處理常式。

Demo.Timer 類別會覆寫基底類別的 dispose 方法,以更新 enabled 屬性,並向消費者告知元件已停用。enabled 屬性的 set 存取子會引發 propertyChanged 事件以傳送通知。程式碼會叫用私用 _stopTimer 方法以停止引發 tick 事件。最後,程式碼會呼叫基底 dispose 方法以讓應用程式釋放該元件。

在 Web 網頁中使用 Demo.Timer 元件

網頁中之 ASP.NET AJAX 用戶端元件的執行個體可由該 Web 網頁中的自訂伺服器控制項或用戶端指令碼來管理。在本節中,您將學習如何使用 Web 網頁中的用戶端指令碼來建立元件執行個體。

建立網頁以使用 Demo.Timer 元件

  1. 在您要放置 DemoTimer.js 檔案的目錄中,建立名為 DemoTimer.aspx 的檔案,並將下列標記與程式碼加入至該檔案中:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head >
            <title>Demo Timer Component</title>
    </head>
    <body>
        <form id="form1" > 
            <div>
                <asp:ScriptManager ID="ScriptManager1" >
                    <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>
    
  2. 在相同的目錄中,建立名為 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 事件的處理常式。在此函式中,Demo.Timer 元件會叫用 $create 方法並傳遞下列引數,以在用戶端指令碼中產生:

  • type 引數是您稍早建立的 Demo.Timer 類別。

  • properties 引數是由 JSON 物件所組成,此物件包含必要的元件 ID 值,其後跟著指定屬性名稱與初始值的屬性名稱/值組。為了示範之用,我們將 interval 屬性的初始值設定為 2000 毫秒,讓計時器每隔 2 秒引發一次 tick 事件(在實際執行應用程式中,您可能要將此間隔設定為較大的值,以降低網路流量)。元件的 enabled 屬性設定為 true,以便計時器在執行個體化之後立即啟動。

  • events 引數包含具有成對的事件名稱與處理常式的物件。在此案例中,onTick 處理常式被指派到 tick 事件,此事件定義於網頁的 script 項目中。

DemoTimer.aspx 檔裝載該元件的 ASP.NET 網頁。在網頁的 ScriptManager 控制項中,asp:ScriptReference 項目的 path 屬性會參考 DemoTimer.js 檔案的路徑,此檔案定義 Demo.Timer 元件類別。

請參閱

工作

動態指派指令碼參考

概念

搭配資料繫結控制項使用 ASP.NET UpdatePanel 控制項

使用 PageRequestManager 事件

參考

Sys.Component 類別

ScriptManager