マスター ページからコンテンツ ページと対話する (C#)

作成者: Scott Mitchell

マスター ページのコードからコンテンツ ページのメソッドを呼び出し、プロパティの設定などを行う方法について説明します。

はじめに

前のチュートリアルでは、コンテンツ ページから、マスター ページの操作をプログラムで行う方法について説明しました。 その際、最後に追加された 5 つの製品を一覧表示する GridView コントロールを追加してマスター ページを更新しました。 それから、ユーザーが新しい製品を追加できるコンテンツ ページを作成しました。 そのコンテンツ ページでは、新しい製品を追加すると、追加されたばかりの製品が含まれるように GridView を更新することをマスター ページに指示する必要がありました。 この機能は、GridView にバインドされたデータを更新するパブリック メソッドをマスター ページに追加し、コンテンツ ページからそのメソッドを呼び出すことで実現しました。

コンテンツ ページとマスター ページの対話の最も一般的な形式は、コンテンツ ページから発生します。 とはいえ、マスター ページで現在のコンテンツ ページを動作させることも可能です。ユーザーがコンテンツ ページに表示されるデータを変更できるようにするユーザー インターフェイス要素がマスター ページに含まれている場合は、このような機能が必要になる可能性があります。 GridView コントロールに製品情報を表示するコンテンツ ページと、クリックするとすべての製品の価格を 2 倍にする Button コントロールを含むマスター ページについて考えてみましょう。 前のチュートリアルの例と同様に、価格を 2 倍にするボタンをクリックすると新しい価格が表示されるように GridView を更新する必要がありますが、このシナリオでは、マスター ページでコンテンツ ページを動作させる必要があります。

このチュートリアルでは、コンテンツ ページで定義された機能をマスター ページから呼び出す方法について説明します。

イベントとイベント ハンドラーを使用したプログラムによる操作の開始

マスター ページからコンテンツ ページの機能を呼び出すことは、他の方法よりも困難です。 コンテンツ ページには 1 つのマスター ページしかないため、コンテンツ ページからプログラムによる操作を開始すると、どのようなパブリック メソッドとプロパティが使用できるかがわかります。 しかし、マスター ページには、それぞれに独自のプロパティとメソッドのセットが存在する、複数の異なるコンテンツ ページが含まれることがあります。 実行されるまでどのコンテンツ ページが呼び出されるかがわからないとすると、どうしたらそのコンテンツ ページで何らかのアクションを実行するコードをマスター ページに記述できるでしょうか?

ASP.NET Web コントロール (Button コントロールなど) について考えてみましょう。 Button コントロールは、任意の数の ASP.NET ページに表示できますが、クリックされたことをページに知らせるメカニズムが必要になります。 これには "イベント" を使います。 Button コントロールがクリックされると、Click イベントを発生させます。そのボタンを含む ASP.NET ページは、必要に応じて "イベント ハンドラー" を介してその通知に応答できます。

この同じパターンを使用して、コンテンツ ページにマスター ページのトリガー機能を設定できます。

  1. マスター ページにイベントを追加します。
  2. マスター ページがコンテンツ ページと通信する必要が生じると、その都度イベントを発生させます。 たとえば、ユーザーが価格を 2 倍にしたことをマスター ページがコンテンツ ページに伝える必要がある場合、価格が 2 倍にされたら、すぐにそのイベントを発生させます。
  3. 何らかのアクションを実行する必要があるイベント ハンドラーをコンテンツ ページに作成します。

このチュートリアルの残りの部分では、「概要」で例として示されている、データベース内の製品を一覧表示するコンテンツ ページと、価格を 2 倍にする Button コントロールを含むマスター ページを実装します。

手順 1: コンテンツ ページに製品を表示する

最初に行う手順は、Northwind データベースの製品を一覧表示するコンテンツ ページを作成することです (前のチュートリアル「コンテンツ ページからマスター ページと対話する」で Northwind データベースをプロジェクトに追加しました)。新しい ASP.NET ページを Products.aspx という名前の ~/Admin フォルダーに追加し、Site.master マスター ページにバインドします。 図 1 は、このページが Web サイトに追加された後のソリューション エクスプローラーを示しています。

管理フォルダーに新しい ASP.NET ページを追加する

図 01: Admin フォルダーに新しい ASP.NET ページを追加します (クリックするとフルサイズの画像が表示されます)

マスター ページでタイトル、メタ タグ、その他の HTML ヘッダーを指定する」チュートリアルで、ページのタイトルが明示的に設定されていない場合に生成する BasePage という名前のカスタム ベース ページ クラスを作成したことを思い出してください。 Products.aspx ページの分離コード クラスに移動し、(System.Web.UI.Page からではなく) BasePage から派生させます。

最後に、この新しいレッスンのエントリを含むように Web.sitemap ファイルを更新します。 [コンテンツ ページからコンテンツ ページと対話する] レッスンの <siteMapNode> の下に、次のマークアップを追加します。

<siteMapNode url="~/Admin/Products.aspx" title="Master to Content Page Interaction" />

この <siteMapNode> 要素の追加は、レッスンの一覧に反映されます (図 5 参照)。

Products.aspx に戻ります。 MainContent のコンテンツ コントロールで、GridView コントロールを追加し、名前を ProductsGrid にします。 GridView を ProductsDataSource という名前の新しい SqlDataSource コントロールにバインドします。

GridView を新しい SqlDataSource コントロールにバインドする

図 02: GridView を新しい SqlDataSource コントロールにバインドします (クリックするとフルサイズの画像が表示されます)

Northwind データベースを使用するようにウィザードを構成します。 前のチュートリアルを行っている場合は、既に Web.configNorthwindConnectionString と名前が付けられた接続文字列があるはずです。 図 3 に示すように、ドロップダウン リストからこの接続文字列を選択します。

Northwind データベースを使用するように SqlDataSource を構成する

図 03: Northwind データベースを使用するように SqlDataSource を構成します (クリックするとフルサイズの画像が表示されます)

次に、ドロップダウン リストから Products テーブルを選択し、ProductNameUnitPrice 列を返して、データ ソース コントロールの SELECT ステートメントを指定します (図 4 を参照)。 [次へ]、[完了] の順にクリックして、データ ソースの構成ウィザードを完了します。

Products テーブルから ProductName フィールドと UnitPrice フィールドを返す

図 04: Products テーブルから ProductNameUnitPrice フィールドを返します (クリックするとフルサイズの画像が表示されます)。

これですべて完了です。 ウィザードを完了すると、Visual Studio は GridView に 2 つの BoundField を追加して、SqlDataSource コントロールによって返される 2 つのフィールドをミラーリングします。 GridView コントロールと SqlDataSource コントロールのマークアップは次のとおりです。 図 5 にブラウザーで表示した場合の結果を示します。

<asp:GridView ID="ProductsGrid" runat="server" AutoGenerateColumns="False" 
 DataSourceID="ProductsDataSource">
 <Columns>
 <asp:BoundField DataField="ProductName" HeaderText="ProductName" 
 SortExpression="ProductName" />
 <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" 
 SortExpression="UnitPrice" />
 </Columns>
</asp:GridView>

<asp:SqlDataSource ID="ProductsDataSource" runat="server" 
 ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" 
 SelectCommand="SELECT [ProductName], [UnitPrice] FROM [Products]">
</asp:SqlDataSource>

各製品とその価格は GridView に記載されています

図 05: 各製品とその価格が GridView に表示されます (クリックするとフルサイズの画像が表示されます)。

Note

必要に応じて GridView の外観を整えてください。 表示される UnitPrice 値を通貨として書式設定する、背景色やフォントを使用してグリッドの外観を改善するなど、いくつかのお勧めの設定があります。 ASP.NET でのデータの表示と書式設定の詳細については、「データの操作」チュートリアル シリーズをご覧ください。

手順 2: マスター ページに価格を 2 倍にするボタンを追加する

次の手順は、Button Web コントロールをマスター ページに追加することです。このコントロールをクリックすると、データベース内のすべての製品の価格が 2 倍になります。 Site.master マスター ページを開き、ツールボックスからデザイナーにボタンをドラッグし、前のチュートリアルで追加した RecentProductsDataSource SqlDataSource コントロールの下に配置します。 ボタンの ID プロパティを DoublePrice に設定し、その Text プロパティを "Double Product Prices" に設定します。

次に、マスター ページに SqlDataSource コントロールを追加し、名前を DoublePricesDataSource にします。 この SqlDataSource は、UPDATE ステートメントを実行してすべての価格を 2 倍にするために使用されます。 具体的には、その ConnectionStringUpdateCommand プロパティを適切な接続文字列と UPDATE ステートメントに設定する必要があります。 次に、DoublePrice ボタンがクリックされたときに、この SqlDataSource コントロールの Update メソッドを呼び出す必要があります。 ConnectionStringUpdateCommand プロパティを設定するには、SqlDataSource コントロールを選択し、プロパティ ウィンドウに移動します。 ConnectionString プロパティは、ドロップダウン リストの Web.config に既に格納されている接続文字列を一覧表示します。図 6 に示すように、NorthwindConnectionString オプションを選択します。

NorthwindConnectionString を使用するように SqlDataSource を構成する

図 06: NorthwindConnectionString を使用するように SqlDataSource を構成します (クリックするとフルサイズの画像が表示されます)

UpdateCommand プロパティを設定するには、プロパティ ウィンドウで UpdateQuery オプションを見つけます。 このプロパティを選択すると、省略記号付きのボタンが表示されます。このボタンをクリックすると、図 7 に示す [コマンドおよびパラメーター エディター] ダイアログ ボックスが表示されます。 ダイアログ ボックスのテキスト ボックスに次の UPDATE ステートメントを入力します。

UPDATE Products SET UnitPrice = UnitPrice * 2

このステートメントを実行すると、Products テーブルの各レコードの UnitPrice 値が 2 倍になります。

SqlDataSource の UpdateCommand プロパティを設定する

図 07: SqlDataSource の UpdateCommand プロパティを設定します (クリックするとフルサイズの画像が表示されます)

これらのプロパティを設定すると、Button コントロールと SqlDataSource コントロールの宣言型マークアップは次のようになります。

<asp:Button ID="DoublePrice" runat="server" 
 Text="Double Product Prices" />

<asp:SqlDataSource ID="DoublePricesDataSource" runat="server" 
 UpdateCommand="UPDATE Products SET UnitPrice = UnitPrice * 2" 
 ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" 
 ProviderName="<%$ ConnectionStrings:NorthwindConnectionString.ProviderName %>">
</asp:SqlDataSource>

あとは、DoublePrice ボタンをクリックしたときに、Update メソッドが呼び出されるようにすることです。 DoublePrice ボタンの Click イベント ハンドラーを作成し、次のコードを追加します。

protected void DoublePrice_Click(object sender, EventArgs e)
{
    // Double the prices
    DoublePricesDataSource.Update();
}

この機能をテストするには、手順 1 で作成した ~/Admin/Products.aspx ページにアクセスし、[Double Product Prices] ボタンをクリックします。 このボタンをクリックするとポストバックが発生し、DoublePrice ボタンの Click イベント ハンドラーが実行され、すべての製品の価格が 2 倍になります。 その後、ページが再レンダリングされ、マークアップが返され、ブラウザーに再表示されます。 ただし、コンテンツ ページの GridView には、[Double Product Prices] ボタンがクリックされる前と同じ価格が表示されます。 これは、最初に GridView に読み込まれたデータの状態がビュー状態で格納されていたため、特に指示がない限りポストバックでは再読み込みされないためです。 別のページにアクセスして ~/Admin/Products.aspx ページに戻ると、更新された価格が表示されます。

手順 3: 価格が 2 倍になったときにイベントを発生させる

~/Admin/Products.aspx ページの GridView は価格の倍額をすぐに反映しないため、ユーザーは [Double Product Prices] ボタンをクリックしなかったか、機能しなかったと考える可能性があります。 ボタンを何度もクリックして、価格を何度も 2 倍にしてしまうかもしれません。 この問題を解決するには、コンテンツ ページのグリッドに、2 倍に設定された直後に新しい価格が表示されるようにする必要があります。

このチュートリアルで既に説明したように、ユーザーが DoublePrice ボタンをクリックするたびにマスター ページでイベントを発生させる必要があります。 イベントは、あるクラス (イベント発行者) が別の一連のクラス (イベント サブスクライバー) に、関係する何かが発生したことを通知する方法です。 この例では、マスター ページがイベント発行者で、DoublePrice ボタンがクリックされたときに、検知するコンテンツ ページがサブスクライバーです。

クラスは、"イベント ハンドラー" を作成して、イベントをサブスクライブします。このイベント ハンドラーは、発生するイベントに応答して実行されるメソッドです。 パブリッシャーは、"イベント デリゲート" を定義することによって発生するイベントを定義します。 イベント デリゲートは、イベント ハンドラーが受け入れる必要がある入力パラメーターを指定します。 .NET Framework では、イベント デリゲートは値を返さず、次の 2 つの入力パラメーターを受け取ります。

  • イベント ソースを識別する Object
  • System.EventArgs の派生クラス

イベント ハンドラーに渡される 2 番目のパラメーターには、イベントに関する追加情報を含めることができます。 基本 EventArgs クラスは情報を渡しませんが、.NET Framework には EventArgs を拡張して追加のプロパティを扱う、多くのクラスが含まれています。 たとえば、CommandEventArgs インスタンスは Command イベントに応答するイベント ハンドラーに渡され、2 つの情報プロパティ (CommandArgumentCommandName) を含みます。

Note

イベントの作成、発生、処理の詳細については、「イベントとデリゲート」と「単純な英語でのイベント デリゲート」をご覧ください。

イベントを定義するには、次の構文を使用します。

public event eventDelegate eventName;

ユーザーが DoublePrice ボタンをクリックし、他の追加情報を渡す必要がない場合にのみコンテンツ ページに通知する必要があるため、イベント デリゲート EventHandler を使用できます。このデリゲートは、2 番目のパラメーターとして System.EventArgs 型のオブジェクトを受け入れるイベント ハンドラーを定義します。 マスター ページでイベントを作成するには、マスター ページの分離コード クラスに次のコード行を追加します。

public partial class Site : System.Web.UI.MasterPage
{
    public event EventHandler PricesDoubled;

    ...
}

上記のコードは、PricesDoubled という名前のマスター ページにパブリック イベントを追加します。 価格が 2 倍になった後、このイベントを発生させる必要があります。 イベントを発生させるには、次の構文を使用します。

if (eventName != null)
    eventName(sender, eventArgs);

sendereventArgs は、サブスクライバーのイベント ハンドラーに渡す値です。

DoublePrice Click イベント ハンドラーを次のコードで更新します。

protected void DoublePrice_Click(object sender, EventArgs e)
{
    // Double the prices
    DoublePricesDataSource.Update();

    // Refresh RecentProducts
    RecentProducts.DataBind();

    // Raise the PricesDoubled event
    if (PricesDoubled != null)
    PricesDoubled(this, EventArgs.Empty);
}

以前と同様に、Click イベント ハンドラーは、DoublePricesDataSource SqlDataSource コントロールの Update メソッドを呼び出して、すべての製品の価格を 2 倍にすることから始めます。 その後、イベント ハンドラーに 2 つの点が追加されます。 まず、RecentProducts GridView のデータが更新されます。 この GridView は、前のチュートリアルのマスター ページに追加され、最近追加された 5 つの製品が表示されます。 これらの 5 つの製品の 2 倍にされた価格が表示されるように、このグリッドを更新する必要があります。 その後、PricesDoubled イベントが発生します。 マスター ページ自体 (this) への参照がイベント ソースとしてイベント ハンドラーに送信され、空の EventArgs オブジェクトがイベント引数として送信されます。

手順 4: コンテンツ ページでのイベントの処理

この時点で、DoublePrice Button コントロールがクリックされるたびに、マスター ページは PricesDoubled イベントを発生させます。 しかし、これはまだ半分にすぎません。サブスクライバー内のイベントを処理する必要があります。 これには、イベント ハンドラーを作成し、イベント接続コードを追加する 2 つの手順が含まれます。この手順によって、イベントの発生時にイベント ハンドラーが実行されます。

まず、Master_PricesDoubled という名前のイベント ハンドラーを作成します。 マスター ページで PricesDoubled イベントを定義した方法のため、イベント ハンドラーの 2 つの入力パラメーターの型は、それぞれ ObjectEventArgs である必要があります。 イベント ハンドラーで ProductsGrid GridView の DataBind メソッドを呼び出して、データをグリッドに再バインドします。

private void Master_PricesDoubled(object sender, EventArgs e)
{
    // Rebind data to ProductsGrid
    ProductsGrid.DataBind();
}

イベント ハンドラーのコードは完了しましたが、マスター ページの PricesDoubled イベントをこのイベント ハンドラーにまだ接続していません。 サブスクライバーは、次の構文を使用してイベント ハンドラーにイベントを接続します。

publisher.eventName += new eventDelegate(methodName);

publisher は、イベントの eventName を提供するオブジェクトへの参照であり、methodName は、eventDelegate に対応するシグネチャを持つサブスクライバーで定義されているイベント ハンドラーの名前です。 つまり、イベント デリゲートが EventHandler の場合、methodName は、値を返さず、Object および EventArgs 型の 2 つの入力パラメーターをそれぞれ受け入れるサブスクライバー内のメソッドの名前である必要があります。

このイベント接続コードは、最初のページ アクセスと後続のポストバックで実行する必要があり、イベントが発生する前のページ ライフサイクルの時点で発生する必要があります。 イベント接続コードを追加するのに適したタイミングは、ページ ライフサイクルの非常に早い段階で発生する PreInit ステージです。

~/Admin/Products.aspx を開いて Page_PreInit イベント ハンドラーを作成します。

protected void Page_PreInit(object sender, EventArgs e)
{
    // TODO: Put event wiring logic here
}

この接続コードを完了するには、コンテンツ ページからマスター ページへのプログラムによる参照が必要です。 前のチュートリアルで説明したように、これを行うには 2 つの方法があります。

  • 緩い型指定の Page.Master プロパティを適切なマスター ページの種類にキャストする
  • .aspx ページに @MasterType ディレクティブを追加し、厳密に型指定された Master プロパティを使用する

ここでは後者のアプローチを使用してみましょう。 ページの宣言型マークアップの先頭に次の @MasterType ディレクティブを追加します。

<%@ MasterType VirtualPath="~/Site.master" %>

次に、Page_PreInit イベント ハンドラーに次のイベント接続コードを追加します。

protected void Page_PreInit(object sender, EventArgs e)
{
    // Create an event handler for the master page's PricesDoubled event
    Master.PricesDoubled += new EventHandler(Master_PricesDoubled);
}

このコードを配置すると、DoublePrice ボタンがクリックされるたびにコンテンツ ページの GridView が更新されます。

図 8 と 9 は、この動作を示しています。 図 8 は、最初にアクセスした時点のページを示しています。 RecentProducts GridView (マスター ページの左側の列) と ProductsGrid GridView (コンテンツ ページ内) の両方の価格の値に注目してください。 図 9 は、DoublePrice ボタンがクリックされた直後の同じ画面を示しています。 ご覧のように、新しい価格は両方の GridView に瞬時に反映されます。

初期価格値

図 08: 最初の価格の値 (クリックするとフルサイズの画像が表示されます)

Just-Doubled 価格は GridViews に表示されます

図 09: 2 倍の価格が GridView に表示されています (クリックするとフルサイズの画像が表示されます)

まとめ

理想は、マスター ページとそのコンテンツ ページは互いに完全に分離されており、対話のレベルは必要ないことです。 しかし、マスター ページまたはコンテンツ ページで、マスター ページまたはコンテンツ ページから変更できるデータを表示している場合は、データが変更されたときに表示を更新できるように、マスター ページからコンテンツ ページに通知する (またはその逆) ことが必要になる可能性があります。 前のチュートリアルでは、プログラムでコンテンツ ページをマスター ページと対話させる方法について説明しました。このチュートリアルでは、マスター ページで対話を開始する方法について考慮しました。

コンテンツ ページとマスター ページの間のプログラムによる対話は、コンテンツ ページまたはマスター ページから発生しますが、使用される対話パターンは開始する場所によって異なります。 この違いは、コンテンツ ページには 1 つのマスター ページしかありませんが、マスター ページにはさまざまなコンテンツ ページが存在する可能性があることに起因します。 マスター ページをコンテンツ ページと直接対話させるのではなく、マスター ページでイベントを発生させ、何らかのアクションが実行されたことを通知させる方法をお勧めします。 アクションを検知するコンテンツ ページで、イベント ハンドラーを作成できます。

プログラミングに満足!

もっと読む

この記事で説明したトピックの詳細については、次のリソースを参照してください。

作成者について

複数の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジに取り組んでいます。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の著書は、「Sams Teach Yourself ASP.NET 3.5 in 24 Hours」です。 Mitchell 氏には、mitchell@4GuysFromRolla.com から、または http://ScottOnWriting.NET で彼のブログを介して連絡できます。

特別な感謝

このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は Suchi Banerjee でした。 今後の MSDN の記事を確認することに関心がありますか? ご希望なら、mitchell@4GuysFromRolla.com でメッセージをお送りください