連載:Microsoft AJAX Library&JavaScriptプログラミング

第2回 PageRequestManagerクラスでUpdatePanelコントロールを極める!

山田 祥寛(http://www.wings.msn.to/
2007/11/16


Back Issue
1
MS AJAX Libで実践オブジェクト指向JavaScript

 ASP.NET AJAXが提供するAJAX Extensionsコントロールの中でも、UpdatePanelコントロールは最も手軽に利用でき、また、実際頻繁に利用するであろうコントロールだ。別稿「DBプログラミング 7つのヒント− 同時実行制御からASP.NET AJAXまで −」でも紹介したように、UpdatePanelコントロールを利用することで、ページの部分的な更新を(基本的に)コーディングレスで実装することができる。

 もっとも、実際のアプリケーション構築においては、手軽というだけではなかなか済まない局面も頻々と登場するものだ。ありがちな例を挙げるならば、ページの部分更新を行うに際して、そのトリガとなるボタンを2度押しできないように、無効化するような仕組みを考えてみよう。

[更新]ボタンをクリック([更新]ボタンが無効になる)



非同期処理が完了する([更新]ボタンは有効になる)


ページの部分更新(非同期通信)中、トリガとなるボタンを無効化
[更新]ボタンがクリックされると非同期処理が開始され、その処理が完了すると、UpdatePanelコントロールにより「23:11:39」の部分のみが更新される。非同期処理の間は[更新]ボタンが無効になる。

 仕組み自体はごくシンプルなものであるが、いざ実装しようとすると――実はこれ、UpdatePanelコントロールだけでは実現できない。非同期通信の開始前後で、ボタンの有効/無効を切り替えるには、どうしてもJavaScriptのコードが必要になるのだ。しかも、そもそもJavaScriptのコードを記述しようにも、UpdatePanelそのものは通信の開始/終了といったイベントを通知してくれるわけではないので、クライアントサイドではどのタイミングでJavaScriptのコードを実行してよいのかが分からないのだ。

 では、たった1つのボタンの有効/無効を切り替えるためだけに、UpdatePanelコントロールは使えなくなってしまうのだろうか。わざわざUpdatePanelコントロールの便利な機能を放棄し、ブリッジ機能*1を使って部分更新のためのコードを一から記述しなければならないのだろうか。

 いやいや、そのようなことはない。そこで登場するのが、今回の主テーマであるPageRequestManagerクラスなのである。

*1 ブリッジ機能については「.NET TIPS:[ASP.NET AJAX]クライアントサイド・スクリプトからXML Webサービスを非同期呼び出しするには?(サーバサイド編)」を参照されたい。

PageRequestManagerクラスはUpdatePanelコントロールを拡張する

 PageRequestManagerクラスはMicrosoft AJAX Library(以降、MS AJAX Lib)に含まれるクラスの1つ(Sys.WebForms名前空間に属する)で、UpdatePanelコントロールによって発生した非同期通信(部分更新)を管理するための機能を提供する。

 PageRequestManagerクラスの機能を直感的に理解したいならば、まずPageRequestManagerクラスが公開するイベントを見てみるのが手っ取り早いだろう。

 以下の図は、クライアントサイドのライフサイクルにおいて通知される主要なイベントとその発生順序を示したものだ。

図1 ASP.NET AJAXにおけるクライアント・ライフサイクル・イベント
オレンジ色の四角で示されたものがPageRequestManagerクラスで公開されているイベントで、水色の四角で示されたものがSys.Applicationクラスで公開されているイベント。

 この図1で示されたイベントのうち、オレンジ色の四角で示されたものがPageRequestManagerクラスで公開されているイベント、水色の四角で示されたものがSys.Applicationクラス(後述のコラムも参照)で公開されているイベントである。

 MS AJAX Libでは、クライアントサイドにおけるライフサイクル・イベントのうち、アプリケーション(ページ)全体にかかわるものをApplicationクラスで、非同期ポストバックにかかわるものをPageRequestManagerクラスで、それぞれ管理しているというわけだ。そして、PageRequestManagerクラスで公開されているこれらイベントを利用することで、UpdatePanelコントロールによって発生した非同期ポストバックの前後にJavaScriptによる任意の処理を差し挟むことが可能になる。

 PageRequestManagerクラスとは、いうなれば、UpdatePanelコントロールの機能を拡張するためのクライアント側の窓口なのである。

■PageRequestManagerクラスの基本

 さて、PageRequestManagerクラスの概要が理解できたところで、以下では具体的なコーディングの方法を見ていくことにしよう。

 ここで紹介するのはごくシンプルなサンプルで、非同期ポストバックが発生する直前に、イベント発生元の要素名をメッセージ表示するだけのコードだ。2つのラベルにはそれぞれ現在時刻を表示するものとする(上は非同期および同期ポストバックによって更新、下は同期ポストバックによってのみ更新される)。

[更新]ボタンをクリック



非同期処理が完了


非同期ポストバック時にメッセージ表示
非同期ポストバック時にメッセージを表示し、処理終了後にメッセージをクリア

 動作自体はあまり意味がないサンプルであるが、まずは単純なコードで、PageRequestManagerクラスの基本的な構文を理解していただきたい。

 用意するのは、以下のようなWebフォームとイベント・ハンドラである。

Webフォーム(Basic.aspx)のレイアウト
配置しているコントロールとそのプロパティ設定は以下の表のとおり。

コントロール(ID) プロパティ 設定値
ScriptManager(manager)
UpdatePanel(panel)
Label(lblAsync) Text △(ブランク)
Button(btnUpdate) Text 更新
Label(lblSync) Text △(ブランク)
Label(lblStatus) Text △(ブランク)
各コントロールのプロパティ設定

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
  System.Threading.Thread.Sleep(3000) ' 時間稼ぎのためのダミーコード
  lblSync.Text = DateTime.Now.ToLongTimeString()
  lblAsync.Text = DateTime.Now.ToLongTimeString()
End Sub
リスト1 ポストバック時に現在時刻をリフレッシュするためのコード(Basic.aspx)

 ここまではごく基本的な内容であるので、特筆すべき点は特にない(UpdatePanelコントロールの基本的な用法に自信のない方は、まず「ASP.NET AJAX ファーストルック −ASP.NETアプリケーションをプログラミングなしでAjax化−」を参照されたい)。

 注目していただきたいのはここから、クライアントサイドで非同期ポストバックを監視するためのコードだ。

<script type="text/javascript">

// ページ・ロード時に呼び出されるpageLoad関数
function pageLoad() {

  // PageRequestManagerクラスをインスタンス化
  var mng = Sys.WebForms.PageRequestManager.getInstance();

  // initializeRequestイベント・ハンドラを定義
  mng.add_initializeRequest(

    // 非同期ポストバックの開始前に、
    // イベント発生元の要素(ID値)を表示
    function(sender, args) {
      $get('lblStatus').innerHTML = args.get_postBackElement().id
                    + 'から非同期ポストバックが実行されました。';
    }
  );

  // 非同期ポストバックの完了後にメッセージをクリア
  mng.add_endRequest(
    function(sender, args) {
      $get('lblStatus').innerHTML = '';
    }
  );
}
</script>
リスト2 非同期ポストバックの開始前後でメッセージを表示/非表示するためのJavaScriptコード(Basic.aspx)

 PageRequestManagerオブジェクトを生成するには、(new演算子ではなく)getInstanceメソッドを呼び出す必要がある(リスト2− )。また、前回も紹介したように、MS AJAX Libの名前空間(ここでは「Sys.WebForms」)はVisual Basic/C#のように略記できない――常に完全修飾名で記述する必要がある点にも、あらためて注意しておこう。

 PageRequestManagerオブジェクトにイベント・ハンドラを登録するのは、add_<イベント名>メソッドの役割だ*2

*2 イベント・ハンドラの関連付けを削除するには、remove_<イベント名>メソッドを使用する。

 ここでは、非同期ポストバックの開始前と完了後にそれぞれ処理を行いたいので、add_initializeRequest/add_endRequestメソッドでそれぞれイベント・ハンドラを登録している。イベント・ハンドラの一般的なシグニチャは、以下のとおりである。

function(sender, args)
イベント・ハンドラの一般的なシグニチャ

 第1引数senderは「イベントを発生したオブジェクト(ここではPageRequestManagerクラス)」を、第2引数argsは発生したイベントに関する情報である「Sys.EventArgs派生オブジェクト」を表す。この構文はASP.NETのイベント・ハンドラにも酷似しているので、直感的にも理解しやすいはずだ。

 イベント・ハンドラの中では、引数args(Sys.EventArgs派生オブジェクト)を介することで、イベントに関連する情報にアクセスすることができる。Sys.EventArgs派生クラスが公開しているプロパティは、それぞれ対応するイベントによって異なるので、以下に主要なものをまとめておこう。

イベント プロパティ 機能
initializeRequest cancel 非同期ポストバックをキャンセルするか
[R]postBackElement 非同期ポストバックの発生元要素
[R]request リクエスト情報(Sys.Net.WebRequestExecutorオブジェクト)
beginRequest [R]postBackElement 非同期ポストバックの発生元要素
[R]request リクエスト情報(Sys.Net.WebRequestExecutorオブジェクト)
pageLoading [R]dataItems 非同期ポストバック時に取得したデータ
[R]panelsDeleting 非同期ポストバックの結果、削除されたUpdatePanelの配列
[R]panelsUpdating 非同期ポストバックの結果、更新されたUpdatePanelの配列
pageLoaded [R]dataItems 非同期ポストバック時に取得したデータ
[R]panelsCreated 非同期ポストバックの結果、作成されたUpdatePanelの配列
[R]panelsUpdated 非同期ポストバックの結果、更新されるUpdatePanelの配列
endRequest [R]dataItems 非同期ポストバック時に取得したデータ
[R]error 非同期ポストバック時に発生した例外情報
errorHandled 例外をハンドラ内で処理したか
[R]response レスポンス情報(Sys.Net.WebRequestExecutorオブジェクト)
Sys.EventArgs派生クラスのプロパティ(PageRequestManagerクラス)
[R]で示したものは読み取り専用のプロパティ。

 ここでは、initializeRequestイベント・ハンドラで、Sys.EventArgs派生クラスのpostBackElementプロパティを介して、イベント発生元の要素を取得し、そのidプロパティ(ID値)を表示しているわけだ(リスト2− )。

 なお、これも前回触れたとおりであるが、MS AJAX Libではプロパティとフィールドという概念を明確に区別している。プロパティにアクセスする場合にはプロパティ名を直接ではなく、アクセサ・メソッド――get_<プロパティ名>メソッド、set_<プロパティ名>メソッドにより参照、設定を行う点に注意してほしい。

[参考]Sys.Applicationクラスが公開するイベント

 本文でも述べたように、Sys.Applicationクラスはページ全体にかかわるイベントを公開している。Sys.Applicationクラスで公開しているイベントとイベントの発生タイミングは、以下のとおりだ。図1を見ても分かるように、loadイベントだけは同期/非同期ポストバックにかかわらず発生する点に注意してほしい。

イベント 発生タイミング
init すべてのスクリプトがロードされた後(オブジェクトは未生成)
load すべてのスクリプトがロードされた後(オブジェクトも生成/初期化済み)
unload ページ上の全オブジェクトを破棄する前
Sys.Applicationクラスの主なイベント

 これらイベントに対応するハンドラを追加するには、PageRequestManagerクラスと同様、add_<イベント名>メソッドをコールすればよい。例えば、loadメソッドを追加するには、以下のように記述する。

Sys.Application.add_load(
  function() {
    // ロード時に行う任意の処理
  }
);
リスト3 loadイベントに対応するハンドラを登録するコード

 ただし、load/unloadイベント・ハンドラについては、必ずしもadd_<イベント名>メソッドで明示的に関連付けを行う必要はない。代わりに、MS AJAX Libで予約されたpageLoad/pageUnload関数を直接に定義してもよい。つまり、リスト3と次のリスト4は同意である。

function pageLoad() {
  // ロード時に行う任意の処理
}
リスト4 loadイベントに対応するハンドラを登録するコード(リスト3と同じ意味)


 INDEX
  Microsoft AJAX Library&JavaScriptプログラミング
  第2回 PageRequestManagerクラスでUpdatePanelコントロールを極める!
  1.UpdatePanelコントロールを拡張するPageRequestManagerクラスの基本
    2.ボタンの2重クリックを防止するには?/実行中の非同期ポストバックをキャンセルするには?
    3.非同期ポストバックの優先順位を制御するには?
    4.非同期ポストバックの完了を通知するには?/非同期ポストバック時に発生した例外を処理するには?
    5.部分更新時にサーバサイドから追加情報を取得するには?
 
インデックス・ページヘ  「Microsoft AJAX Library&JavaScriptプログラミング」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間