次の方法で共有


カスタムの非ビジュアル クライアント コンポーネントの作成

更新 : 2007 年 11 月

このトピックでは、ASP.NET でクライアントの Sys.Component 基本クラスから派生する AJAX 非ビジュアル クライアント コンポーネントを作成する方法と、そのコンポーネントをページ内で使用する方法を示します。

このチュートリアルでは、次の作業を行う方法について説明します。

  • プロトタイプ デザイン パターンを使用して、ECMAScript (JavaScript) で非ビジュアル コンポーネント クラスを定義します。

  • 非ビジュアル コンポーネントを Component 基本クラスの派生クラスとして登録します。

  • 非ビジュアル コンポーネントの基本クラスである Component を初期化し、同クラスのメソッドを呼び出します。

  • 変更通知を発生させるプロパティを作成します。

  • コンポーネントをページ内で使用し、コンポーネントのイベントに関連付けます。

概要では、非ビジュアル クライアント コンポーネントの例としてタイマを取り上げます。このタイマは、ハンドラで処理できるイベントを発生させます。

ここでは、クライアントの非ビジュアル コンポーネントをベースとするオブジェクトについて重点的に説明します。これらのコンポーネントは、Component から派生し、通常は UI として表示されません。コンポーネントの基本的な機能を拡張した ASP.NET AJAX クライアント コンポーネント オブジェクトには、これ以外に 2 つの種類があります。1 つは Sys.UI.Behavior から派生する動作、もう 1 つは Sys.UI.Control から派生するコントロールです。コンポーネント、動作、およびコントロールの相違点を次の表に示します。

クライアント コンポーネント オブジェクトの種類

概要

コンポーネント

  • Component 基本クラスから派生します。

  • 通常は UI として表示されません (一定の間隔でイベントを発生させるが、ページには表示されないタイマ コンポーネントなど)。

  • 関連付けられた DOM 要素がありません。

  • 複数のアプリケーションで再利用することを目的としたクライアント コードをカプセル化します。

動作

  • Component 基本クラスを拡張した Behavior 基本クラスから派生します。

  • DOM 要素の動作を拡張します (既存のテキスト ボックスにアタッチできる透かしの動作など)。

  • UI 要素を作成できます。ただし、関連付けられた DOM 要素の基本の動作は、通常は変更されません。

  • DOM 要素からカスタムの属性 (expando) を通じて直接アクセスできます。属性の名前は、動作に名前がある場合はそれと同じになりますが、それ以外の場合は完全修飾名ではない型名になります。

  • 他のクライアント オブジェクト (Control クラスや Behavior クラスから派生したクラスなど) との関連付けが不要です。

  • element プロパティで、コントロールまたはコントロール以外の HTML 要素のどちらも参照できます。

コントロール

  • Component 基本クラスを拡張した Control 基本クラスから派生します。

  • DOM 要素をクライアント オブジェクトとして表します。多くの場合、元の DOM 要素の通常の動作を変更して新しい機能を追加します。たとえば、メニュー コントロールは、ソース データとして ul 要素から li の各項目を読み取りますが、箇条書きとして表示されることはありません。

  • DOM 要素からコントロールの expando を通じて直接アクセスできます。

前提条件

このトピックの例を実行するための要件は次のとおりです。

非ビジュアル クライアント コンポーネントの基本的な機能の作成

ASP.NET AJAX 非ビジュアル クライアント コンポーネントは、複数のアプリケーションで再利用することを目的とした JavaScript コードをカプセル化します。非ビジュアル コンポーネントの例としては、あらかじめ設定した間隔でイベントを発生させるタイマ コンポーネントがあります。

Component 基本クラスから派生することにより、カスタム コンポーネントが自動的に継承する機能は次のとおりです。

  • イベント ハンドラとクライアント オブジェクトのイベントとの関連付けをブラウザ間で管理するためのモデル。

  • Sys.IDisposable インターフェイスを実装するコンポーネントを、クライアント アプリケーションで破棄できるオブジェクトとして自動的に登録する機能。

  • プロパティが変更されたときに通知イベントを発生させる機能。

  • コンポーネントのプロパティ設定のバッチ処理を実行する機能。この機能により、プロパティの各 get アクセサおよび set アクセサのすべてのロジックを処理するよりも、スクリプトのサイズが小さくなり、処理時間も短縮できます。

  • Sys.Component.initialize メソッドをオーバーライドして、プロパティおよびイベント リスナを初期化する機能。

Component クラスから派生したクライアント コンポーネントの実装

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 アプリケーションのページでカスタム クライアント コンポーネントを使用するには、次の手順を実行します。

  • Web ページでコンポーネントのスクリプト ライブラリを登録します。

  • コンポーネントのインスタンスを作成します。

以降のセクションでは、これらの手順について詳しく説明します。

Web ページにおけるコンポーネントのスクリプト ライブラリの登録

ページ上でクライアント コントロールに必要なスクリプトを登録するには、宣言またはプログラム内で 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 クラスの概要を参照してください。

Bb386520.alert_note(ja-jp,VS.90).gifメモ :

ScriptManager コントロールを使用して登録するスタンドアロンのスクリプト ファイルでは、必ず notifyScriptLoaded メソッドを呼び出して、スクリプトの読み込みが完了したことをアプリケーションに通知する必要があります。ただし、アセンブリに埋め込まれたスクリプトでは、多くの場合、このメソッドを呼び出さないようにする必要があります。詳細については、「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 コンポーネントの作成

ここでは、Component 基本クラスを拡張する Demo.Timer という名前のカスタム クライアント コンポーネントを作成し、そのコンポーネントをページ内で使用します。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 の 2 つのパブリック プロパティを宣言して初期化します。プロパティ定義には、値を格納するためのプライベート フィールドの他、各プロパティの 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 が呼び出され、アプリケーションがコンポーネントを解放できるようになります。

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 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>
    
  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 という 2 つの関数を含む JavaScript コードを使用して、TestDemoTimer.js を読み込みます。OnTick 関数は、Demo.Timer コンポーネントの tick イベントを処理し、span HTML 要素内のカウンタ値を更新します。

applicationInitHandler 関数は、app_init イベントのハンドラです。この関数では、$create メソッドを呼び出し、次の引数を渡すことにより、クライアント スクリプト内で Demo.Timer コンポーネントをインスタンス化します。

  • 引数 type は、前の手順で作成した Demo.Timer クラスです。

  • 引数 properties では、必須のコンポーネント ID 値の後に、プロパティ名と初期値を指定するプロパティの名前と値のペアが格納された JSON オブジェクトを指定します。わかりやすくするため、ここでは interval プロパティの初期値を 2,000 ミリ秒に設定し、タイマが tick イベントを 2 秒ごとに発生させるようにします。ただし、実働アプリケーションでは、ネットワーク トラフィックを軽減するため、間隔をもっと大きな値に設定する場合が多いと考えられます。さらに、コンポーネントの enabled プロパティを true に設定し、タイマがインスタンス化後すぐに起動するようにします。

  • 引数 events には、イベント名とそのハンドラを組み合わせたオブジェクトを指定します。この例では、onTick ハンドラが tick イベントに割り当てられています (ページの script 要素で定義)。

DemoTimer.aspx ファイルは、コンポーネントをホストする ASP.NET Web ページです。ページの ScriptManager コントロールでは、asp:ScriptReference 要素の path 属性で、Demo.Timer コンポーネント クラスを定義する DemoTimer.js ファイルのパスが参照されています。

参照

処理手順

スクリプト参照の動的な割り当て

概念

ASP.NET UpdatePanel コントロールとデータ バインド コントロールの使用

PageRequestManager のイベントの処理

参照

Sys.Component クラス

ScriptManager