特定の非同期ポストバックの優先的処理
更新 : 2007 年 11 月
既定では、ページで複数の非同期ポストバックが実行された場合、直前に行われたポストバックが優先されます。しかし、場合によっては、特定の非同期ポストバックを優先して、他のポストバックをキャンセルする方がよいことがあります。
このチュートリアルでは、優先的に処理するポストバックを制御する方法を説明します。これを行うには、PageRequestManager クラスの initializeRequest イベントに対応するイベント ハンドラを作成します。PageRequestManager クラスで発生するイベントの順序については、「PageRequestManager のイベントの処理」を参照してください。
前提条件
独自の開発環境でこの手順を実装するための要件は次のとおりです。
Microsoft Visual Studio 2005 または Microsoft Visual Web Developer Express Edition。
AJAX 対応の ASP.NET Web サイト。
特定のポストバック要素を優先するスクリプトの作成
まず、ブラウザで非同期ポストバックを管理する ECMAScript (JavaScript) コードを作成します。
特定のポストバック要素を優先するスクリプトを作成するには
ASP.NET Web サイトで、JScript ファイルを追加し、PostbackPrecedence.js という名前を付けます。
次のスクリプトをファイルに追加します。
Sys.Application.add_load(ApplicationLoadHandler) function ApplicationLoadHandler(sender, args) { if (!Sys.WebForms.PageRequestManager.getInstance().get_isInAsyncPostBack()) { Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(InitializeRequest); } } var divElem = 'AlertDiv'; var messageElem = 'AlertMessage'; var exclusivePostBackElement = 'Button1'; var lastPostBackElement; function InitializeRequest(sender, args) { var prm = Sys.WebForms.PageRequestManager.getInstance(); if (prm.get_isInAsyncPostBack() && args.get_postBackElement().id === exclusivePostBackElement) { if (lastPostBackElement === exclusivePostBackElement) { args.set_cancel(true); ActivateAlertDiv('visible', 'A previous postback is still executing. The new postback has been canceled.'); setTimeout("ActivateAlertDiv('hidden','')", 1500); } else if (lastPostBackElement !== exclusivePostBackElement) { prm.abortPostBack(); } } else if (prm.get_isInAsyncPostBack() && args.get_postBackElement().id !== exclusivePostBackElement) { if (lastPostBackElement === exclusivePostBackElement) { args.set_cancel(true); ActivateAlertDiv('visible', 'A previous postback is still executing. The new postback has been canceled.'); setTimeout("ActivateAlertDiv('hidden','')", 1500); } } lastPostBackElement = args.get_postBackElement().id; } function ActivateAlertDiv(visString, msg) { var adiv = $get(divElem); var aspan = $get(messageElem); adiv.style.visibility = visString; aspan.innerHTML = msg; } if(typeof(Sys) !== "undefined") Sys.Application.notifyScriptLoaded();
Sys.Application.add_load(ApplicationLoadHandler) function ApplicationLoadHandler(sender, args) { if (!Sys.WebForms.PageRequestManager.getInstance().get_isInAsyncPostBack()) { Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(InitializeRequest); } } var divElem = 'AlertDiv'; var messageElem = 'AlertMessage'; var exclusivePostBackElement = 'Button1'; var lastPostBackElement; function InitializeRequest(sender, args) { var prm = Sys.WebForms.PageRequestManager.getInstance(); if (prm.get_isInAsyncPostBack() && args.get_postBackElement().id === exclusivePostBackElement) { if (lastPostBackElement === exclusivePostBackElement) { args.set_cancel(true); ActivateAlertDiv('visible', 'A previous postback is still executing. The new postback has been canceled.'); setTimeout("ActivateAlertDiv('hidden','')", 1500); } else if (lastPostBackElement !== exclusivePostBackElement) { prm.abortPostBack(); } } else if (prm.get_isInAsyncPostBack() && args.get_postBackElement().id !== exclusivePostBackElement) { if (lastPostBackElement === exclusivePostBackElement) { args.set_cancel(true); ActivateAlertDiv('visible', 'A previous postback is still executing. The new postback has been canceled.'); setTimeout("ActivateAlertDiv('hidden','')", 1500); } } lastPostBackElement = args.get_postBackElement().id; } function ActivateAlertDiv(visString, msg) { var adiv = $get(divElem); var aspan = $get(messageElem); adiv.style.visibility = visString; aspan.innerHTML = msg; } if(typeof(Sys) !== "undefined") Sys.Application.notifyScriptLoaded();
このスクリプトは、次のタスクを実行します。
Sys.Application クラスの load イベントのイベント ハンドラを定義します。このハンドラは、PageRequestManager クラスの initializeRequest イベントに対し、InitializeRequest という名前のイベント ハンドラを登録します。
InitializeRequest ハンドラを定義します。このハンドラでは、非同期ポストバックが現在実行されているかどうか確認し、ポストバックの発生元である要素の名前を特定します。ポストバックの発生元要素が、排他的ポストバック要素 (優先するポストバック要素) である場合は、InitializeRequestEventArgs クラスの cancel プロパティを設定して、新たに発生したポストバックをキャンセルします。
メッセージを表示するため、ページ上にある <div> 要素の表示と非表示を切り替える ActivateAlertDiv 関数を定義します。
UpdatePanel コントロールに対するスクリプトの使用
この手順では、作成したスクリプトをページ内で使用します。ページには 2 つのボタンがあり、一方のポストバックはもう一方のポストバックよりも優先的に処理されます。
一方のポストバックを優先するページを作成するには
1 つのファイルで構成される新しい ASP.NET Web ページを作成し、デザイン ビューに切り替えます。
ツールボックスの [AJAX Extensions] タブで、ScriptManager コントロールをダブルクリックしてページに追加します。
UpdatePanel コントロールを 2 回ダブルクリックして、コントロールの 2 つのインスタンスをページに追加します。
各 UpdatePanel コントロールに、ツールボックスの [標準] タブから、Label コントロールと Button コントロールを追加します。
両方のパネルで、Label コントロールの Text の値を "Panel Initially Rendered" に設定します。
各 Button コントロールをダブルクリックして各ボタンの Click イベントに対応するハンドラを追加します。
次のコードを Click ハンドラに追加します。これにより、遅延が人為的に作成され、ポストバック発生元のパネルに現在の時刻が表示されます。
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) System.Threading.Thread.Sleep(4000) Label1.Text = "Last update from server " & DateTime.Now.ToString() End Sub Protected Sub Button2_Click(ByVal sender As Object, ByVal e As EventArgs) System.Threading.Thread.Sleep(1000) Label2.Text = "Last update from server " & DateTime.Now.ToString() End Sub
protected void Button1_Click(object sender, EventArgs e) { System.Threading.Thread.Sleep(4000); Label1.Text = "Last update from server " + DateTime.Now.ToString(); } protected void Button2_Click(object sender, EventArgs e) { System.Threading.Thread.Sleep(1000); Label2.Text = "Last update from server " + DateTime.Now.ToString(); }
メモ : このチュートリアルでは、Click イベントのハンドラで意図的に遅延を発生させています。実際には、ユーザーが遅延を導入することはありません。サーバー トラフィックや、処理に時間がかかるサーバー コード (長時間実行するデータベース クエリなど) によって遅延が発生します。
ソース ビューに切り替えて、ページ上の <head> 要素に次の <style> ブロックを追加します。
<style type="text/css"> body { font-family: Tahoma; } #UpdatePanel1, #UpdatePanel2 { width: 400px; height: 100px; border: solid 1px gray; } div.MessageStyle { background-color: #FFC080; top: 95%; left: 1%; height: 20px; width: 600px; position: absolute; visibility: hidden; } </style>
<style type="text/css"> body { font-family: Tahoma; } #UpdatePanel1, #UpdatePanel2 { width: 400px; height: 100px; border: solid 1px gray; } div.MessageStyle { background-color: #FFC080; top: 95%; left: 1%; height: 20px; width: 600px; position: absolute; visibility: hidden; } </style>
このスタイル ルールでは、UpdatePanel コントロールによって描画される <div> 要素と、ポストバックがキャンセルされるときにユーザーに警告を表示する <div> 要素のサイズを定義します。
ページ上の <form> 要素内に次のマークアップを追加します。
<div id="AlertDiv" class="MessageStyle"> <span id="AlertMessage"></span> </div>
<div id="AlertDiv" class="MessageStyle"> <span id="AlertMessage"></span> </div>
このマークアップでは、既に他のポストバックが処理中であるために、非同期ポストバックがキャンセルされるときにメッセージを表示する <div> 要素を定義します。
デザイン ビューに切り替えます。
一方の UpdatePanel コントロールの中をクリックし、ツールボックスの [AJAX Extensions] から UpdateProgress コントロールを追加します。
UpdateProgress コントロールの中をクリックし、「Panel1 updating …」と入力します。
これにより、ProgressTemplate プロパティが設定されます。
[プロパティ] ウィンドウで UpdateProgress コントロールを選択し、その AssociatedUpdatePanelID プロパティを "UpdatePanel1" に設定します。
デザイナに表示されるページは、次の図のようになります。
もう一方の UpdatePanel コントロールの中をクリックし、UpdateProgress コントロールをもう 1 つ追加します。
UpdateProgress コントロールの中をクリックし、「Panel2 updating …」と入力します。
これにより、ProgressTemplate プロパティが設定されます。
[プロパティ] ウィンドウで UpdateProgress コントロールを選択し、その AssociatedUpdatePanelID プロパティを "UpdatePanel2" に設定します。
デザイナに表示されるページは、次の図のようになります。
ScriptManager コントロールを選択します。
[プロパティ] ウィンドウで、Scripts プロパティをクリックします。次に、省略記号 ([…]) ボタンをクリックして、[ScriptReference コレクション エディタ] ダイアログ ボックスを表示します。
[追加] をクリックしてスクリプト参照を追加します。
スクリプト参照の Path プロパティを PostbackPrecedence.js (前の手順で作成した JavaScript ファイル) に設定します。
ScriptManager の Scripts コレクションを使用してスクリプト参照を追加すると、Microsoft AJAX Library の読み込み後にスクリプトが確実に読み込まれます。
[OK] をクリックして [ScriptReference コレクション エディタ] ダイアログ ボックスを閉じます。
変更内容を保存し、Ctrl キーを押しながら F5 キーを押して、ブラウザでページを表示します。
最初のパネルのボタンをクリックしてから、2 番目のパネルのボタンをクリックします。
新しいポストバックがキャンセルされたことを示すメッセージが表示されます。最初のパネルのボタンによるポストバックが終了するまで、新しいポストバックは開始されません。スクリプト ファイルでは、この動作を強制的に実行するためのロジックを定義しています。
2 番目のパネルのボタンをクリックしてから、最初のパネルのボタンをクリックします。
2 番目のパネルのボタンは優先されません。これは、スクリプト ファイルでそのように指定されていないからです。このため、警告メッセージは表示されず、最初のパネルのボタンによる新しいポストバックが開始されます。これは、最新のポストバックが優先されるという非同期ポストバックの既定の動作です。
<%@ Page Language="VB" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) System.Threading.Thread.Sleep(4000) Label1.Text = "Last update from server " & DateTime.Now.ToString() End Sub Protected Sub Button2_Click(ByVal sender As Object, ByVal e As EventArgs) System.Threading.Thread.Sleep(1000) Label2.Text = "Last update from server " & DateTime.Now.ToString() End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title>Postback Precedence Example</title> <style type="text/css"> body { font-family: Tahoma; } #UpdatePanel1, #UpdatePanel2 { width: 400px; height: 100px; border: solid 1px gray; } div.MessageStyle { background-color: #FFC080; top: 95%; left: 1%; height: 20px; width: 600px; position: absolute; visibility: hidden; } </style> </head> <body> <form id="form1" runat="server"> <div> <asp:ScriptManager ID="ScriptManager1" runat="server"> <Scripts> <asp:ScriptReference Path="PostBackPrecedence.js" /> </Scripts> </asp:ScriptManager> <asp:UpdatePanel ID="UpdatePanel1" UpdateMode="Conditional" runat="Server" > <ContentTemplate> <strong>UpdatePanel 1</strong><br /> This postback takes precedence.<br /> <asp:Label ID="Label1" runat="server">Panel initially rendered.</asp:Label><br /> <asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" /> <asp:UpdateProgress ID="UpdateProgress1" runat="server" AssociatedUpdatePanelID="UpdatePanel1"> <ProgressTemplate> Panel1 updating... </ProgressTemplate> </asp:UpdateProgress> </ContentTemplate> </asp:UpdatePanel> <asp:UpdatePanel ID="UpdatePanel2" UpdateMode="Conditional" runat="Server" > <ContentTemplate> <strong>UpdatePanel 2</strong><br /> <asp:Label ID="Label2" runat="server">Panel initially rendered.</asp:Label><br /> <asp:Button ID="Button2" runat="server" Text="Button" OnClick="Button2_Click" /> <asp:UpdateProgress ID="UpdateProgress2" runat="server" AssociatedUpdatePanelID="UpdatePanel2"> <ProgressTemplate> Panel2 updating... </ProgressTemplate> </asp:UpdateProgress> </ContentTemplate> </asp:UpdatePanel> <div id="AlertDiv" class="MessageStyle"> <span id="AlertMessage"></span> </div> </div> </form> </body> </html>
<%@ Page Language="C#" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> protected void Button1_Click(object sender, EventArgs e) { System.Threading.Thread.Sleep(4000); Label1.Text = "Last update from server " + DateTime.Now.ToString(); } protected void Button2_Click(object sender, EventArgs e) { System.Threading.Thread.Sleep(1000); Label2.Text = "Last update from server " + DateTime.Now.ToString(); } </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title>Postback Precedence Example</title> <style type="text/css"> body { font-family: Tahoma; } #UpdatePanel1, #UpdatePanel2 { width: 400px; height: 100px; border: solid 1px gray; } div.MessageStyle { background-color: #FFC080; top: 95%; left: 1%; height: 20px; width: 600px; position: absolute; visibility: hidden; } </style> </head> <body> <form id="form1" runat="server"> <div> <asp:ScriptManager ID="ScriptManager1" runat="server"> <Scripts> <asp:ScriptReference Path="PostBackPrecedence.js" /> </Scripts> </asp:ScriptManager> <asp:UpdatePanel ID="UpdatePanel1" UpdateMode="Conditional" runat="Server" > <ContentTemplate> <strong>UpdatePanel 1</strong><br /> This postback takes precedence.<br /> <asp:Label ID="Label1" runat="server">Panel initially rendered.</asp:Label><br /> <asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" /> <asp:UpdateProgress ID="UpdateProgress1" runat="server" AssociatedUpdatePanelID="UpdatePanel1"> <ProgressTemplate> Panel1 updating... </ProgressTemplate> </asp:UpdateProgress> </ContentTemplate> </asp:UpdatePanel> <asp:UpdatePanel ID="UpdatePanel2" UpdateMode="Conditional" runat="Server" > <ContentTemplate> <strong>UpdatePanel 2</strong><br /> <asp:Label ID="Label2" runat="server">Panel initially rendered.</asp:Label><br /> <asp:Button ID="Button2" runat="server" Text="Button" OnClick="Button2_Click" /> <asp:UpdateProgress ID="UpdateProgress2" runat="server" AssociatedUpdatePanelID="UpdatePanel2"> <ProgressTemplate> Panel2 updating... </ProgressTemplate> </asp:UpdateProgress> </ContentTemplate> </asp:UpdatePanel> <div id="AlertDiv" class="MessageStyle"> <span id="AlertMessage"></span> </div> </div> </form> </body> </html>
レビュー
このチュートリアルでは、特定の非同期ポストバックを、他の非同期ポストバックが開始される前に、優先的に処理する (つまり処理を完了する) ための方法を説明しました。この動作を強制的に実行するためのロジックは、ページのスクリプト参照として指定された JavaScript ファイルで定義します。このスクリプトをカスタマイズして、現在のすべての非同期ポストバックが完了するまで、新しいポストバックが開始されないようにすることもできます。ただし、優先的に処理するポストバックを指定するときは、アプリケーションのデザインを慎重に検討する必要があります。