次の方法で共有


ASP.NET AJAX UpdatePanel トリガーについて理解する

作成者: Scott Cate

Visual Studio のマークアップ エディターで作業しているときに、UpdatePanel コントロールの子要素が 2 つあることに (IntelliSense から) 気付く場合があります。 その 1 つが Triggers 要素です。これは、要素がある UpdatePanel コントロールの部分的なレンダリングをトリガーするページ上のコントロール (または、使用している場合はユーザー コントロール) を指定します。

はじめに

Microsoft の ASP.NET テクノロジは、オブジェクト指向のイベントドリブン プログラミング モデルを提供し、コンパイルされたコードの利点と統合します。 ただし、そのサーバー側処理モデルにはテクノロジに固有のいくつかの欠点があり、その多くは Microsoft ASP.NET 3.5 AJAX 拡張機能に含まれる新機能によって対処できます。 これらの拡張機能を使用すると、ページ全体の更新を必要としないページの部分的なレンダリング、クライアント スクリプト (ASP.NET プロファイル API を含む) を介して Web サービスにアクセスする機能、ASP.NET サーバー側コントロール セットに表示される多くの制御スキームをミラーするように設計された広範なクライアント側 API など、豊富なクライアント機能を実現できます。

このホワイトペーパーでは、ASP.NET AJAX UpdatePanel コンポーネントの XML トリガー機能について説明します。 XML トリガーを使用すると、特定の UpdatePanel コントロールの部分的なレンダリングを引き起こす可能性があるコンポーネントをきめ細かく制御できます。

このホワイトペーパーは、.NET Framework 3.5 のベータ 2 リリースと Visual Studio 2008 に基づいています。 以前は ASP.NET 2.0 を対象とするアドオン アセンブリであった ASP.NET AJAX 拡張機能は、.NET Framework 基本クラス ライブラリに統合されました。 また、このホワイトペーパーでは、Visual Web Developer Express ではなく Visual Studio 2008 を使用することを前提としており、Visual Studio のユーザー インターフェイスに従ってチュートリアルを解説します (ただし、コード一覧は開発環境に関係なく完全に互換性があります)。

トリガー

既定では、指定した UpdatePanel のトリガーに、ポストバックを呼び出す子コントロール (たとえば、AutoPostBack プロパティが true に設定されている TextBox コントロールなど) が自動的に含まれます。 ただし、マークアップを使用して、トリガーを宣言的に含めることもできます。これは、UpdatePanel コントロール宣言の <triggers> セクション内で行われます。 トリガーには Triggers コレクション プロパティを使用してアクセスできますが、Page_Load イベント内でページの ScriptManager オブジェクトの RegisterAsyncPostBackControl(Control) メソッドを使用して、実行時 (設計時にコントロールが使用できない場合など) に部分的なレンダリング トリガーを登録することをお勧めします。 ページはステートレスであるため、作成されるたびにこれらのコントロールを再登録する必要があります。

ChildrenAsTriggers プロパティを false に設定することで、子トリガーの自動インクルージョンを無効にする (これにより、ポストバックを作成する子コントロールが部分レンダリングを自動的にトリガーしない) こともできます。 これにより、どの特定のコントロールがページ レンダリングを呼び出すかを割り当てる際の柔軟性を最大限に高めることができます (推奨)。その結果、開発者は、発生する可能性のあるイベントを処理するのではなく、特定のイベントへの応答をオプトインできます。

UpdatePanel コントロールが入れ子になっていて、UpdateMode が [条件付き] に設定されている場合に、子 UpdatePanel がトリガーされ、親がトリガーされない場合は、子 UpdatePanel のみが更新されます。 ただし、親 UpdatePanel が更新されると、子 UpdatePanel も更新されます。

<Triggers> 要素

Visual Studio のマークアップ エディターで作業しているときに、UpdatePanel コントロールの子要素が 2 つあることに (IntelliSense から) 気付く場合があります。 最も頻繁に見える要素は <ContentTemplate> 要素であり、これは基本的に、更新パネルによって保持されるコンテンツ (部分レンダリングを有効にするコンテンツ) をカプセル化します。 もう 1 つの要素が <Triggers> 要素で、これは、<Triggers> 要素がある UpdatePanel コントロールの部分的なレンダリングをトリガーするページ上のコントロール (または、使用している場合はユーザー コントロール) を指定します。

<Triggers> 要素には、2 つの子ノード (<asp:AsyncPostBackTrigger><asp:PostBackTrigger>) のうち、いくつを含めても構いません。 両方とも 2 つの属性 (ControlIDEventName) を受け入れ、現在のカプセル化の単位内で任意のコントロールを指定できます (たとえば、UpdatePanel コントロールが Web ユーザー コントロール内にある場合は、ユーザー コントロールがあるページでコントロールを参照しないでください)。

<asp:AsyncPostBackTrigger> 要素は、このトリガーが子である UpdatePanel だけでなく、カプセル化の単位で UpdatePanel コントロールの子として存在するコントロールからのすべてのイベントをターゲットにできる点で特に便利です。 したがって、どのコントロールも、ページの部分的な更新をトリガーするようにできます。

同様に、<asp:PostBackTrigger> 要素を使用してページの部分的なレンダリングをトリガーすることもできますが、これには、サーバーへのフル ラウンドトリップが必要です。 このトリガー要素は、通常はコントロールが部分的なページ レンダリングをトリガーする場合 (たとえば、UpdatePanel コントロールの Button 要素に <ContentTemplate> コントロールがある場合) に、ページ全体のレンダリングを強制する際にも使用できます。 ここでも、PostBackTrigger 要素は、現在のカプセル化の単位内の任意の UpdatePanel コントロールの子である任意のコントロールを指定できます。

<Triggers> 要素のリファレンス

マークアップの子孫:

Tag 説明
<asp:AsyncPostBackTrigger> このトリガー参照を含む UpdatePanel のページを部分的に更新するコントロールとイベントを指定します。
<asp:PostBackTrigger> ページ全体の更新 (フル ページ更新) を引き起こすコントロールとイベントを指定します。 このタグは、部分的なレンダリングをトリガーするコントロールに対して、完全な更新を強制するために使用できます。

チュートリアル: Cross-UpdatePanel トリガー

  1. 部分的なレンダリングが有効に設定された ScriptManager オブジェクトを使用して、新しい ASP.NET ページを作成します。 このページに 2 つの UpdatePanel を追加します。1 つ目には、1 つの Label コントロール (Label1) と 2 つの Button コントロール (Button1 と Button2) を含めます。 Button1 は "クリックして両方を更新"、Button2 は "クリックしてこれを更新" といった文言を表示します。 2 つ目の UpdatePanel には、Label コントロール (Label2) のみを含めます。ただし、ForeColor プロパティを既定以外の値に設定して区別します。
  2. 両方の UpdatePanel タグの UpdateMode プロパティを Conditional に設定します。

リスト 1: default.aspx のマークアップ:



<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
   <head runat="server">
      <title>Untitled Page</title>
   </head>
   <body>
      <form id="form1" runat="server">
         <asp:ScriptManager EnablePartialRendering="true"
            ID="ScriptManager1" runat="server"></asp:ScriptManager>
         <div>
            <asp:UpdatePanel ID="UpdatePanel1" runat="server"
               UpdateMode="Conditional">
               <ContentTemplate>
                  <asp:Label ID="Label1" runat="server" />
                  <br />
                  <asp:Button ID="Button1" runat="server"
                     Text="Update Both Panels" OnClick="Button1_Click" />
                  <asp:Button ID="Button2" runat="server"
                     Text="Update This Panel" OnClick="Button2_Click" />
               </ContentTemplate>
            </asp:UpdatePanel>
            <asp:UpdatePanel ID="UpdatePanel2" runat="server"
               UpdateMode="Conditional">
               <ContentTemplate>
                  <asp:Label ID="Label2" runat="server" ForeColor="red" />
               </ContentTemplate>
               <Triggers>
                  <asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
               </Triggers>
            </asp:UpdatePanel>
         </div>
      </form>
   </body>
</html>

  1. Button1 の Click イベント ハンドラーで、Label1.Text と Label2.Text を、時間に依存するもの (DateTime.Now.ToLongTimeString() など) に設定します。 Button2 の Click イベント ハンドラーでは、時間に依存する値を Label1.Text のみに設定します。

リスト 2: default.aspx.cs の Codebehind (トリミング):

public partial class _Default : System.Web.UI.Page
{
    protected void Button1_Click(object sender, EventArgs e)
    {
        Label1.Text = DateTime.Now.ToLongTimeString();
        Label2.Text = DateTime.Now.ToLongTimeString();
    }
    protected void Button2_Click(object sender, EventArgs e)
    {
        Label1.Text = DateTime.Now.ToLongTimeString();
    }
}
  1. F5 キーを押し、プロジェクトをビルドして実行します。 [Update Both Panels] をクリックすると、両方のラベルのテキストが変わります。ただし、[Update This Panel] をクリックすると、Label1 のみが更新されます。

Screenshot that shows the first button that states Update Both Panels and the second button that states Update This Panel.

(クリックするとフルサイズの画像が表示されます)

内部的な処理

先ほど作成した例を利用して、ASP.NET AJAX がどのように動作しているか、UpdatePanel のクロスパネル トリガーがどのように機能するかを確認できます。 これを行うには、生成されたページ ソース HTML と、FireBug という Mozilla Firefox 拡張機能を使用します。これを使用すると、AJAX ポストバックを簡単に調べることができます。 また、Lutz Roeder の .NET Reflector ツールも使用します。 これらのツールはどちらもオンラインで自由に利用でき、インターネット検索で見つけることができます。

ページのソース コードを調べても、通常と特に変わりありません。UpdatePanel コントロールは <div> コンテナーとしてレンダリングされ、<asp:ScriptManager>から提供されたものがスクリプト リソースに含まれているのを確認できます。 また、AJAX クライアント スクリプト ライブラリの内部にある、PageRequestManager に対する新しい AJAX 固有の呼び出しもあります。 最後に、2 つの UpdatePanel コンテナーが表示されます。その 1 つには、レンダリングされた <input> ボタンと、<span> コンテナーとしてレンダリングされた 2 つの <asp:Label> コントロールがあります。 (FireBug で DOM ツリーを調べると、ラベルが淡色表示され、表示できるコンテンツが生成されていないことが示されます)。

[Update This Panel] ボタンをクリックし、上部の UpdatePanel が現在のサーバー時刻で更新されていることを確認します。 FireBug で、[Console] タブを選択して、要求を確認できるようにします。 最初に POST 要求パラメーターを調べます。

Screenshot that shows a Firebug dialog with Console selected.

(クリックするとフルサイズの画像が表示されます)

UpdatePanel が、ScriptManager1 パラメーターを介してどのコントロール ツリーを具体的に処理したか (UpdatePanel1 コントロールの Button1) をサーバー側の AJAX コードに示している点に注目してください。 次に、[Update Both Panels] ボタンをクリックします。 次に、応答を調べると、パイプで区切られた一連の変数が文字列に設定されていることがわかります。具体的には、一番上の UpdatePanel (UpdatePanel1) で、その HTML 全体がブラウザーに送信されます。 AJAX クライアント スクリプト ライブラリは、UpdatePanel の元の HTML コンテンツを .innerHTML プロパティを介して新しいコンテンツに置き換えるので、サーバーは変更されたコンテンツを HTML としてサーバーから送信します。

次に、[Update Both Panels] ボタンをクリックし、サーバーからの結果を確認します。 結果は非常に似ています。どちらの UpdatePanel もサーバーから新しい HTML を受信します。 前のコールバックと同様に、追加のページ状態が送信されます。

ここでわかるように、AJAX ポストバックを実行するための特別なコードは使用されていないため、AJAX クライアント スクリプト ライブラリは、追加のコードなしでフォームのポストバックをインターセプトできます。 サーバー コントロールは、JavaScript を自動的に使用してフォームを自動的に送信しないようにします。ASP.NET は、フォーム検証と状態のコードを自動的に挿入します。これは主に、自動スクリプト リソース インクルージョン、PostBackOptions クラス、ClientScriptManager クラスによって実現されます。

たとえば、CheckBox コントロールを考えてみます。NET Reflector でクラスの逆アセンブリを調べてみましょう。 これを行うには、System.Web アセンブリが開かれていることを確認し、System.Web.UI.WebControls.CheckBox クラスに移動して RenderInputTag メソッドを開きます。 AutoPostBack プロパティをチェックする条件を探します。

Screenshot that shows code that begins with on Click equals.

(クリックするとフルサイズの画像が表示されます)

CheckBox コントロールで自動ポストバックが有効になっている場合 (AutoPostBack プロパティが true の場合)、結果の <input> のタグは、onclick 属性の ASP.NET イベント処理スクリプトを使用してレンダリングされます。 フォームの送信のインターセプトにより、ASP.NET AJAX の非侵入型のページへの挿入が許可され、不正確な文字列置換を利用することで発生する可能性のある破壊的変更を回避できます。 さらに、これにより、どのカスタム ASP.NET コントロールでも、UpdatePanel コンテナー内での使用をサポートするコードを追加することなく、ASP.NET AJAX の機能を利用できます。

<triggers> 機能は、_updateControls に対する PageRequestManager 呼び出しで初期化された値に対応します (ASP.NET AJAX クライアント スクリプト ライブラリは、ライブラリ外での使用を目的としていない、アンダースコアで始まるメソッド、イベント、フィールド名を内部としてマークする規則を利用しています)。 これにより、AJAX ポストバックを引き起こすコントロールを確認できます。

たとえば、ページに 2 つのコントロールを追加し、1 つのコントロールを UpdatePanel の外部に完全に残し、1 つを UpdatePanel 内に残します。 上部の UpdatePanel 内に CheckBox コントロールを追加し、リスト内でいくつかの色を定義した DropDownList をドロップします。 新しいマークアップを次に示します。

リスト 3: 新しいマークアップ

<%@ Page Language="C#" AutoEventWireup="true"
 CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
 <head id="Head1" runat="server">
 <title>Untitled Page</title>
 </head>
 <body>
 <form id="form1" runat="server">
 <asp:ScriptManager EnablePartialRendering="true"
 ID="ScriptManager1" runat="server"></asp:ScriptManager>
 <div>
 <asp:UpdatePanel ID="UpdatePanel1" runat="server"
 UpdateMode="Conditional">
 <ContentTemplate>
 <asp:Label ID="Label1" runat="server" /><br />
 <asp:Button ID="Button1" runat="server"
 Text="Update Both Panels" OnClick="Button1_Click" />
 <asp:Button ID="Button2" runat="server"
 Text="Update This Panel" OnClick="Button2_Click" />
 <asp:CheckBox ID="cbDate" runat="server"
 Text="Include Date" AutoPostBack="false"
 OnCheckedChanged="cbDate_CheckedChanged" />
 </ContentTemplate>
 </asp:UpdatePanel>
 <asp:UpdatePanel ID="UpdatePanel2" runat="server"
 UpdateMode="Conditional">
 <ContentTemplate>
 <asp:Label ID="Label2" runat="server"
 ForeColor="red" />
 </ContentTemplate>
 <Triggers>
 <asp:AsyncPostBackTrigger ControlID="Button1" 
 EventName="Click" />
 <asp:AsyncPostBackTrigger ControlID="ddlColor" 
 EventName="SelectedIndexChanged" />
 </Triggers>
 </asp:UpdatePanel>
 <asp:DropDownList ID="ddlColor" runat="server"
 AutoPostBack="true"
 OnSelectedIndexChanged="ddlColor_SelectedIndexChanged">
 <asp:ListItem Selected="true" Value="Red" />
 <asp:ListItem Value="Blue" />
 <asp:ListItem Value="Green" />
 </asp:DropDownList>
 </div>
 </form>
 </body>
</html>

新しいコードビハインドを次に示します。

リスト 4: コードビハインド

public partial class _Default : System.Web.UI.Page
{
    protected void Button1_Click(object sender, EventArgs e)
    {
        if (cbDate.Checked)
        {
            Label1.Text = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss");
            Label2.Text = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss");
        }
        else
        {
            Label1.Text = DateTime.Now.ToLongTimeString();
            Label2.Text = DateTime.Now.ToLongTimeString();
        }
    }
    protected void Button2_Click(object sender, EventArgs e)
    {
        if (cbDate.Checked)
        {
            Label1.Text = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss");
        }
        else
        {
            Label1.Text = DateTime.Now.ToLongTimeString();
        }
    }
    protected void cbDate_CheckedChanged(object sender, EventArgs e)
    {
        cbDate.Font.Bold = cbDate.Checked;
    }
    protected void ddlColor_SelectedIndexChanged(object sender, EventArgs e)
    {
        Color c = Color.FromName(ddlColor.SelectedValue);
        Label2.ForeColor = c;
    }
}

このページの目的は、ドロップダウン リストが 3 つの色のいずれかを選択して 2 番目のラベルを表示すること、およびチェック ボックスが、太字かどうか、ラベルに日付と時刻を表示するかどうかを決定することです。 チェック ボックスが AJAX の更新を実行しないようにする必要がありますが、ドロップダウン リストにはそれが (UpdatePanel 内に格納されていない場合でも) 必要です。

Screenshot that shows a web browser called Untitled Page and a drop down list with the color Blue selected below the button that says Update Both Panels.

(クリックするとフルサイズの画像が表示されます)

上記のスクリーン ショットで明らかなように、直近にクリックされたボタンは右側の [Update This Panel] で、これにより、一番下の時刻とは無関係に上部の時刻が更新されました。 日付もクリックするたびにオフになりましたが、下部のラベルには日付が表示されます。 最後の確認事項は下部のラベルの色で、ラベルのテキストよりも最近に更新されています。これは、コントロールの状態が重要であることを示し、ユーザーは AJAX ポストバックを通じて保持されることを期待しています。 ただし、時刻は更新されませんでした。 コントロールがサーバーに再レンダリングされたときに ASP.NET ランタイムによって解釈されたページの __VIEWSTATE フィールドの永続化によって、時刻は自動的に再設定されました。 ASP.NET AJAX サーバー コードは、コントロールが状態を変更しているメソッドを認識しません。これは、単に表示状態から再設定し、適切なイベントを実行します。

ただし、Page_Load イベント内の時間を初期化した場合、時間は正しくインクリメントされたはずです。 そのため、開発者は、適切なイベント ハンドラー中に適切なコードが実行されていることに注意し、コントロール イベント ハンドラーが適切な場合は、Page_Load を使用しないようにする必要があります。

まとめ

ASP.NET AJAX 拡張機能の UpdatePanel コントロールは多機能で、更新の原因となるコントロール イベントを識別するためのさまざまなメソッドを利用できます。 これは、子コントロールによる自動的な更新をサポートしますが、ページ上の他の場所のコントロール イベントにも応答できます。

サーバー処理負荷の可能性を減らすには、UpdatePanel の ChildrenAsTriggers プロパティを false に設定し、イベントを (既定で含めるのではなく) オプトインすることをお勧めします。 これにより、不要なイベントによって、検証や入力フィールドの変更など、望ましくない可能性のある効果が発生することも防止されます。 このようなバグは特定が困難な場合があります。ページがユーザーに対して透過的に更新されるためで、原因がすぐに判明しない可能性があります。

ASP.NET AJAX フォームのインターセプト後のモデルの内部動作を調べることで、これが、ASP.NET によって既に提供されているフレームワークを利用していることを確認できました。 そうすることで、同じフレームワークを使用して設計されたコントロールとの最大限の互換性を維持し、ページ用に記述された追加の JavaScript への侵入を最小限にします。

経歴

Rob Paveza は、アリゾナ州テンペにある大手双方向マーケティング会社、Terralever (www.terralever.com) のシニア .NET アプリケーション開発者です。 お問い合わせ先は robpaveza@gmail.com、ブログは http://geekswithblogs.net/robp/ です。

Scott Cate 氏は 1997 年から Microsoft Web テクノロジに従事しており、ナレッジ ベース ソフトウェア ソリューションに焦点を当てた ASP.NET ベースのアプリケーションの作成を専門とする myKB.com (www.myKB.com) の社長です。 Scott 氏へのお問い合わせは、メール アドレス scott.cate@myKB.com または彼のブログ ScottCate.com を介して行うことができます。