印刷用ページ       送信     
クリックして評価とフィードバックをお寄せください
MSDN
MSDN ライブラリ
テクニカルドキュメント
.NET 開発
ASP.NET
 ユーザー コントロールの包括的な考察

  低帯域幅での表示をオンにする
ユーザー コントロールの包括的な考察

Scott Mitchell

March 2004
日本語版最終更新日 2006 年 9 月 11 日

適用対象:
Microsoft ASP.NET

概要: ユーザー コントロールが ASP.NET でどのように実装およびレンダリングされるかの解説を述べてから、ユーザー コントロールについて最もよくたずねられる質問を考察します。

この記事のソース コードをダウンロードしてください。

目次

はじめに
ユーザー コントロールの基本事項の概要
ユーザー コントロールの配置
ユーザー コントロールの処理方法
ユーザー コントロールの Web コントロールから起動されたイベントへの応答
ユーザー コントロールへのプロパティおよびメソッドの追加
postback の処理
ユーザー コントロール イベント
カスタム イベントの作成と起動
ユーザー コントロールの動的追加
まとめ
参考資料

はじめに

開発者は、Web アプリケーションの作業中に、ある特定の HTML マークアップおよびソース コードを多数の Web ページで繰り返す必要がたびたび生じます。 必要が生じるたびに、各ページでそのような HTML あるいはコードを繰り返すのは最も望ましくない方法です。 そのような繰り返しは、次のようないくつかの理由で害があります。

  • 第 1 に、そして最も明白に、コードを繰り返すと、他の場面でもっと有効に利用できたはずの時間がとられます。
  • 第 2 に、コードあるいは HTML マークアップの追加、削除、または編集のたびに、バグあるいはソフトウェア上の欠陥を持ち込む可能性があります。 当然ながら、コードを切り取ってページからページへ貼り付ければ、その確率は低下しますが、それでも脅威を拭い去ることはできません (たとえば、正しくない場所にコードを貼り付けることもありえます)。
  • 第 3 に、HTML およびコードが多数のファイルに点在している場合、それらの HTML あるいはソース コードに変更を加えるのは大きな悩みの種になります。 変更の必要な箇所をすべて見つけ出すのは、単に時間がかかるだけでなく、エラーも生じやすくなります。コードが重複している箇所を 1 つや 2 つは見逃す可能性が高いからです。

Microsoft ASP.NET の前身であるクラシック ASP では、開発者は、HTML とソース コードを再利用していました。すなわち、頻繁に使用するこのマークアップとスクリプトをインクルード ファイルに入れ、その後サーバー側のインクルード ファイルを使用して、共通の HTML あるいはスクリプトを必要とする Web ページに対して、インクルード ファイルの内容が自動的に挿入されるようにしていました。 しかし ASP 開発者がいくつものインクルード ファイルを扱うのは珍しいことではありません。 その一般的なものとしては、Web ページの主要コンテンツの上に表示される HTML マークアップを収めた「ヘッダ」HTML 用のものや、Web ページの主要コンテンツの下に表示される HTML マークアップを収めた「フッタ」HTML 用のもの、および共通のソース コード関数からなるものなどがあります。 (クラシック ASP のサーバー側インクルード ファイルの詳細については、The Low-Down on #includes (英語) をぜひお読みください。)

HTML マークアップ、Web コントロール、あるいはこの 2 つの組み合わせのようなユーザー インターフェイス要素の再使用が、ASP.NET のユーザー コントロールで実現されています。 ASP.NET Web ページと同様、ユーザー コントロールも作成されます。 ASP.NET Web ページと同様に、ユーザー コントロールも、HTML マークアップと Web コントロールの混成である HTML部分と、ソース コード部分で構成されます。 ユーザー コントロールを作成し終わったら、Web アプリケーション内の任意の数の ASP.NET Web ページで使用することができます。 ユーザー コントロールは簡単に作成することができ、さらに、再使用がもたらす恩恵のゆえに、ページのヘッダ、フッタ、ナビゲーション リンク、およびその他のよく使用されるユーザー インターフェイス要素のレンダリング用として、ユーザー コントロールを重宝する開発者も多くいます。

注意 : ユーザー コントロールには、サーバー側インクルード ファイルの多数の利点があります。 サーバー側インクルード ファイルは、別のファイルの内容をそのまま挿入するために使用されます。 それに対して、ユーザー コントロールは、ASP.NET Web コントロールとほぼ同様に、クラスとして実装されます。 ページ開発者は、ユーザー コントロールとプログラマチックに対話できます。それは、サーバー側インクルード ファイルでは不可能なことです。

単純なユーザー コントロールは、簡単に作成して配置できますが、開発者がユーザー コントロールを使用してさらに進歩した技法を試そうとすると、いつも問題に突き当たってしまいます。 この記事では、ASP.NET でどのようにユーザー コントロールが実装およびレンダリングされるかを検討し、以下のもっと進んだユーザー コントロール技法を習得する方法を検討してみます。

  • ユーザー コントロールにおいて Web コントロールで起動されたイベントに対応する。
  • ユーザー コントロールの配置先の ASP.NET Web ページからアクセス可能なユーザー コントロールでプロパティとメソッドを作成する。
  • ユーザー処置によって起きた後、ユーザー コントロールを収めたページによって処理されるイベントをユーザー コントロールに引き渡す。
  • ASP.NET ページへユーザー コントロールをプログラマチックにロードする。

ユーザー コントロールの基本事項の概要

ユーザー コントロールは、.ascx ファイルとして作成されるファイルであり、HTML 部分とソース コード部分で構成されます。 Microsoft Visual Studio .NET を使用する場合、ユーザー コントロールのソース コード部分は、分離コードクラスとして別のファイルに収められます。それはちょうど、ASP.NET Web ページのソース コード部分が別の分離コードクラス内に存在するのと同じです。 (Visual Studio .NET をご使用でない場合、ユーザー コントロールのソース コード部分は、サーバー側「スクリプト」ブロック内に表示されます。)

ユーザー コントロールがどのように実装およびレンダリングされるかの理解を深めるために、簡単なユーザー コントロール サンプルを作成してみましょう。 食料品を販売する会社に勤めていて、品揃えについての一般の認知度を高めるために、顧客向けの Web サイトを作成する任務を負っていると仮定します。 製品情報は、2 つの関連しあったテーブルを使用してデータベース内に保管されます。その 1 つは Categories (カテゴリ) であり、これには各製品カテゴリごとのレコードが収納されます。もう 1 つは、Products (製品) であり、これには各販売製品のレコードが収納されます。 どの製品も、必ずいずれか 1 つのカテゴリに当てはまります。このマッピングは、外部キー CategoryID 付きで Products テーブル内で保守されます。 (ここで使用する実際のデータは、Northwinds データベースのものです。)

Web サイトの設計にあたって、Beverage (飲料) カテゴリに属する製品を示すページをいくつか設けることにしたとします。 したがって、DataGrid を使用するユーザー コントロールを構築して、Beverage 製品を表示することがふさわしいと考えられます。 Visual Studio .NET を使用すれば、タイプが Web ユーザー コントロールの ASP.NET Web アプリケーションに対して新規のアイテムを追加することで、ユーザー コントロールを作成することができます。 そうすると、2 つのファイルが作成されます。その 1 つは、ユーザー コントロール用の HTML マークアップを収容する .ascx ファイルであり、もう 1 つは、ユーザー コントロールのソース コードを収容する分離コードクラス ファイルです。

少し時間をとって、DisplayBeverages.ascx という名前の新規のユーザー コントロールを作成します。 この場合、標準の ASP.NET Web ページの場合とほぼ同じようにして、ユーザー コントロールを作成することができます。 このユーザー コントロールの場合は、DataGrid をドラッグして .ascx ファイルのデザイナにドロップします。 ユーザー コントロールの分離コードクラスで、以下のコードを追加し、Beverages 製品を DataGrid にバインドします。

private void Page_Load(object sender, System.EventArgs e)
{
   if (!Page.IsPostBack)
      BindData();
}

private void BindData()
{
   OleDbConnection myConnection = new
OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
Server.MapPath("Northwind.mdb"));

   const string productSQL = "SELECT ProductName, UnitPrice FROM
Products WHERE CategoryID = 1";

   OleDbCommand myCommand = new OleDbCommand(productSQL, myConnection);

   myConnection.Open();
   dgProducts.DataSource = myCommand.ExecuteReader();
   dgProducts.DataBind();
   myConnection.Close();
}

                  

ユーザー コントロールのロードのたびに実行される Page_Load イベント ハンドラは、最初のページのロード時にのみ BindData() メソッドを呼び出し、以降の postback では呼び出しません。 BindData() は、Northwinds Access データベースへの接続を作成し、SQL ステートメントを発行して CategoryID が 1 に等しいすべての製品 (Beverages の場合は CategoryID) を取得してから、その結果を、ユーザー コントロール (dgProducts) に追加された DataGrid にバインドします。

注意 : このユーザー コントロールと、この記事全体を通して検証しているすべてのユーザー コントロールの完全な作業サンプルは、この記事のダウンロード用コード内に用意されています。

ユーザー コントロールの配置

ユーザー コントロールを作成し終わったら、次のステップでは、ユーザー コントロールを ASP.NET Web ページに配置します。 その一環として、2 つのステップを行います。まず、ASP.NET Web ページに @Register ディレクティブを追加し、次に、宣言 Web コントロール構文を使用して、ユーザー コントロールを Web ページの HTML コンテンツに挿入します。

注意 残念ながら、Visual Studio .NET には、このプロセスに対応する機能はありません。 ユーザー コントロールをツールボックスに追加できないので、ドラッグして Web ページにドロップすることはできません。 手動で @Register ディレクティブを追加し、手動でユーザー コントロール用の構文を挿入する必要があります。  さらに、デザイナの Visual Studio .NET では、ユーザー コントロールをレンダリングした実際のコンテンツではなく、ユーザー コントロールの名前の付いたグレー化されたボックスしか表示されません。
更新 私の ASP.NET クラスの熱心な学生ならびにこの記事の多数の読者の方々から、Visual Studio .NET を使用して ASP.NET Web ページにユーザー コントロールを追加するもっと簡単な方法があることを指摘いただきました。 ユーザー コントロールを作成した後、それをソリューション エクスプローラからドラッグし、ASP.NET ページのデザイン ビューにドロップします。 すると、<%@ Register %> ディレクティブとユーザー コントロールの宣言構文が ASP.NET Web ページの HTML 部分に自動的に追加されます。
ただし、このページの分離コードクラス内のユーザー コントロールをプログラマチックに参照するには、やはり、ユーザー コントロールと同名のメンバ変数を分離コードクラスに手動で追加する必要があることに注意してください。 (詳細は『ユーザー コントロールへのプロパティおよびメソッドの追加』の項を参照してください。)

@Register ディレクティブは、ASP.NET Web ページの HTML 部分の最上部に表示される必要があり、次のような構文でなければなりません。

<%@ Register TagPrefix="prefix" TagName="name"
   Src="path to .ascx file" %>

                  

TagPrefix 値と TagName 値は、Web ページの HTML 部分でユーザー コントロールがどのように宣言として参照されるかを指定します。 Src には、ユーザー コントロールの .ascx ファイルへのパスが入っていなければなりません。 ASP.NET Web ページと同じディレクトリ内にユーザー コントロールを入れてあれば、Src には .ascx ファイルのファイル名を入れるだけで済みます。 Src には、~ を入れることもできます。これは、Web アプリケーションの基本ディレクトリまでたどって解決されます。 この波形記号構文は便利です。これを使用すると、すべてのユーザー コントロールを /Controls ディレクトリ内に入れることができます。 それにより、ASP.NET Web ページがディレクトリ構造内のどこに置かれていても、Src を ~/Controls/UserControlFileName.ascx と指定することができます。

以下は、ASP.NET Web ページ Beverages.aspx で使用される @Register ディレクティブを示しています。 Src 属性を見れば、DisplayBeverages.ascx ユーザー コントロールは、Web アプリケーションのルート ディレクトリに置かれていることが分かります。

<%@ Register TagPrefix="skm" TagName="ShowBeverages"
Src="~/DisplayBeverages.ascx" %>

                  

次に、Web ページにユーザー コントロールを追加するために、以下の宣言構文を Web ページの HTML 部分に単に追加します。

<skm:ShowBeverages runat="server"></skm:ShowBeverages>

                  

プレフィックス (skm) およびタグ名 (ShowBeverages) が、@Register ディレクティブ内の TagPrefix 属性値と TagName 属性値に一致していることに注意してください。 また、runat="server" にも注意してください。この属性は、他の Web コントロールの場合と同じように必要です。

このように追加を完了したら、Web アプリケーションを作成し、Beverages.aspx にアクセスしてユーザー コントロールをテストする準備が整います。 図 1 は、赤枠で囲まれたユーザー コントロールの出力が置かれたブラウザを通して見た Beverages.aspx のスクリーン ショットを示しています。

ms972975.usercontrols_fig01(ja-jp,MSDN.10).gif

図 1. Beverages ユーザー コントロール


ユーザー コントロールの利点は、ユーザー インターフェイスを再使用できることにあります。 つまり、品揃えされた飲料製品のリストを他の Web ページで作成する必要が生じた場合、単に @Register ディレクティブをそのページに追加し、該当する宣言構文をそのページの HTML 部分に置くだけで済みます。 もし、ユーザー コントロールを使用しないで、飲料を Beverages.aspx に表示するコードを代わりに作成することにした場合、他のページにこの機能を複製するときは、DataGrid とそれに関連したソース コードを Beverages.aspx から切り取って、飲料製品を一覧で表示する必要のある他のページに貼り付ける必要があります。

ユーザー コントロールの処理方法

ASP.NET Web ページの HTML 部分には、Web コントロール、ユーザー コントロール、および HTML 構文を混合して入れることができます。 ASP.NET Web ページに初めてアクセスした場合や、Web ページの HTML 部分に変更を加えた後、初めてアクセスした場合、HTML 部分からクラスが自動的に作成されます。 このクラスは、Web ページの分離コードクラスから派生されますが、HTML 部分の宣言構文をベースにして Web ページのコントロール階層を構成します。

たとえば、Web ページの HTML 部分は、次のようなマークアップからなると想定します。

<html>
<body>
  <form runat="server">
    Name: <asp:TextBox runat="server" id="name"></asp:TextBox>
    <br />
    <asp:Button runat="server" Text="Submit"></asp:Button>
  </form>
</body>
</html>

                  

これで、次のようなコントロール階層が生成されます。

ms972975.usercontrols_fig02(ja-jp,MSDN.10).gif

図 2. ページのコントロール階層


HTML マークアップは LiteralControl インスタンスに変換され、Web Form は HtmlForm オブジェクトに変換され、TextBox および Button Web コントロールはそれぞれのクラスに変換されたことに注意してください。

このコントロール階層を作成し終わったら、この階層内の各コントロールの RenderControl(HtmlTextWriter) メソッドを再帰的に呼び出すことで、ASP.NET Web ページをレンダリングすることができます。 コントロールの RenderControl(HtmlTextWriter) メソッドを呼び出すと、コントロールによって HTML マークアップが作成されて、HtmlTextWriter オブジェクトに付加されます。 ASP.NET Web ページを要求するたびに、そのページに対応するクラスが呼び出されることによって、コントロール階層が作成されてレンダリングされることに注意してください。

ユーザー コントロールは、ASP.NET Web ページに似たやり方で動作します。 ユーザー コントロールに初めてアクセスした場合や、HTML 部分に変更を加えた後に初めてアクセスした場合、ユーザー コントロールは、ユーザー コントロールの分離コードクラスから派生されたクラスにコンパイルされます。 ASP.NET Web ページの自動生成クラスと同様に、ユーザー コントロールの自動生成クラスも、ユーザー コントロールの HTML 部分をベースにしてコントロール階層を構成します。 本質的に、ユーザー コントロールには独自のコントロール階層があるということです。

次に、次のような HTML マークアップをもったユーザー コントロールがあると想定します。

<asp:Label runat="server" id="lblCategoryName"></asp:Label>
<br />
<asp:Label runat="server" id="Description"></asp:Label>

                  

この場合のユーザー コントロールのコントロール階層は、次のようになります。

ms972975.usercontrols_fig03(ja-jp,MSDN.10).gif

図 3. ユーザー コントロールのコントロール階層


ASP.NET Web ページの HTML 部分には、Web コントロールや HTML 構文だけでなく、ユーザー コントロールも収容できることを思い出してください。 ユーザー コントロールは、ユーザー コントロールの自動生成クラスのインスタンスとしてコントロール階層に組み入れられます。 したがって、Button の後は Web コントロールが、上記で説明した宣言構文を持つユーザー コントロールのインスタンスになるように、上記の ASP.NET Web ページ サンプルを拡大すると想定します。 それによって、Web ページのコントロール階層は次のようになります。

ここをクリックすると、拡大表示になります

図 4. ページ内で更新されたコントロール階層


ユーザー コントロール クラスをコントロール階層に追加した後、そのクラスの InitializeAsUserControl(Page) メソッドが呼び出されます。 InitializeAsUserControl(Page) は、System.Web.UI.UserControl クラスに定義されるメソッドであって、自動生成のユーザー コントロール クラスの間接的な派生元です。 (自動生成のユーザー コントロール クラスは、ユーザー コントロールの分離コードクラスから派生されることを思い出してください。ASP.NET Web ページの分離コードクラスが System.Web.UI.Page から派生されるのと同様の方法で、この分離コードクラスも、System.Web.UI.UserControl から派生されます。) InitializeAsUserControl(Page) メソッドは、ユーザー コントロールのクラス階層を生成し、そのユーザー コントロールの Page プロパティを、引き渡された Page インスタンスに割り当てます。それによってユーザー コントロールは、所属する Page をプログラマチックに参照できるようになります。 InitializeAsUserControl(Page) の実行後、Web ページのコントロール階層は次のようになります。

ここをクリックすると、拡大表示になります

図 5. ユーザー コントロールの初期化後の更新済みページ階層


ユーザー コントロールをコントロール階層に付加し、それ独自のコントロール階層を構築し終わると、そのページのコントロール階層は、そのユーザー コントロールが未使用であった場合と同じように見えますが、実際は、その HTML 部分はページの HTML 部分に直接挿入されていることに注意してください。 このようにして階層が完成したら、階層内の各コントロールの RenderControl (HtmlTextWriter) メソッドを再帰的に呼び出すことで、Web ページをレンダリングすることができます。

これまで、ユーザー コントロールが背後でどのように処理されるかを見てきたので、ユーザー コントロール内の Web コントロールによって引き起こされたイベントへの応答方法から始めて、ユーザー コントロールの一歩進んだ概念をいくつか見てみましょう。

ユーザー コントロールの Web コントロールから起動されたイベントへの応答

上記で見たとおり、ユーザー コントロールには、HTML 構文と Web コントロールを混合してその HTML 部分内に収容することができます。 たとえば、ここではすでに DisplayBeverages.ascx ユーザー コントロールを作成しましたが、これは、飲料製品を DataGrid に表示します。  多くの場合、ユーザー コントロールを作成する開発者は、ユーザー コントロール内の Web コントロールで起動されたサーバー側イベントに対してプログラマチックに応答できる必要があります。 列を並べ替えできるようにこの DataGrid を構成したと想定します。 それが原因で、DataGrid の列のヘッダーは、LinkButtons にレンダリングされます。 そのようなリンクのうちの 1 つをクリックすると、Web ページは postback されて、DataGridSortCommand が起動されます。 このような場合、ユーザー コントロール内で発生した Web コントロール イベントに対して、どのように応答できるのであろうかという疑問がわきます。

さいわい、その答えは簡単です。すなわち、イベントを起動する Web コントロールが ASP.NET Web ページ内にある場合と同じ技法をユーザー コントロールでも使用します。 つまり、ユーザー コントロールで、Web コントロール イベント用のイベント ハンドラを作成し、そのイベント ハンドラ内に必要なコードを入れます。 これを解説するため、新規ユーザー コントロール DisplayBeveragesSorting.ascx を作成してみましょう。これは、DataGrid を並び替え可能にすることで、先に検証した DisplayBeverages.ascx ユーザー コントロールを拡張します。

最初のステップでは、DataGrid を拡張し、並び替えがサポートされるようにします。 その一環として、AllowSorting="True" を DataGrid の宣言に追加し、DataGridBoundColumnsSortExpression プロパティを構成します。 次に、DataGridSortCommand イベント用のイベント ハンドラを、ユーザー コントロールの分離コードクラス内に作成します。 (並び替え可能な DataGrid の作成についての解説は、この記事の範囲を超えています。 並び替え機能を DataGrid に追加する方法の詳細は、「An Extensive Examination of the DataGrid Web Control」 (英語) を参照してください。)

並べ替え機能を追加すると、ユーザー コントロールの分離コードクラスのコードが若干変更されます。 主な変更点として、今後 BindData() メソッドは、結果の並べ替えに使用する列名を指定する文字列入力をとるようになります。 さらに、分離コードクラスは、DataGridSortCommand イベント用のイベント ハンドラを収容するようになります。 このイベント ハンドラは、指定された SortExpression を使用して、単に DataGrid にデータを再バインドするだけです。

private void Page_Load(object sender, System.EventArgs e)
{
   if (!Page.IsPostBack)
      BindData("UnitPrice DESC");
}

private void BindData(string orderBy)
{
   OleDbConnection myConnection = new
OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
Server.MapPath("Northwind.mdb"));

   string productSQL =
    "SELECT ProductName, UnitPrice FROM Products " +
      "WHERE CategoryID = 1 ORDER BY " + orderBy;

   OleDbCommand myCommand = new OleDbCommand(productSQL, myConnection);

   myConnection.Open();
   OleDbDataReader reader = myCommand.ExecuteReader();
   dgProducts.DataSource = reader;
   dgProducts.DataBind();
   reader.Close();
   myConnection.Close();
}

private void dgProducts_SortCommand(object source,
  System.Web.UI.WebControls.DataGridSortCommandEventArgs e)
{
   BindData(e.SortExpression);
}

                  

以下のスクリーン ショットは、SortableBeverages.aspx Web ページを示しています。これには、ユーザー コントロール DisplayBeveragesSorting.ascx が組み込まれています。 DataGrid の列ヘッダは、リンクにレンダリングされることに注意してください。 リンクをクリックすると Web Form が postback され、DataGridSortCommand イベント ハンドラが起動されます。 ユーザー コントロール内のイベント ハンドラは、このイベントをキャッチして、正しく並び替えられた順序でデータベース データを DataGrid に再バインドして処理します。 最初のスクリーン ショットは、最初にアクセスされたときのページを示し、2 番目のスクリーン ショットは、[Product Name] リンクがクリックされた後のページを示しています。

ms972975.usercontrols_fig06(ja-jp,MSDN.10).gif

図 6. SortableBeverages ユーザー コントロール


ms972975.usercontrols_fig07(ja-jp,MSDN.10).gif

図 7. 並べ替え後の SortableBeverages ユーザー コントロール


このサンプルから取り除くべき重要点は、Web コントロール イベントの起動時に何らかの処理を行う必要があって、その Web コントロールがユーザー コントロール内にある場合、そのイベントのイベント ハンドラをユーザー コントロールの分離コードクラス内に入れることです。

ユーザー コントロールへのプロパティおよびメソッドの追加

DataGrid 内に飲料製品を表示するこのユーザー コントロールの実行サンプルをさらに便利にすることができます。それには、ユーザー コントロールが置かれている Web ページで、どのようなカテゴリの製品を DataGrid に表示するかを指定できるようにします。 そうすれば、Beverage (飲料)、Condiment (調味料)、Dairy Product (乳製品)、などを表示するのにも、同じユーザー コントロールを使用することができます。 そのためには、ユーザー コントロールの分離コードクラスにプロパティを追加する必要があります。それを使用して、表示する製品の CategoryID を指定することができます。

ユーザー コントロールの分離コードクラス内のプロパティは、パブリック メンバ変数として作成できますが、プロパティ構文を使用して作成することもできます。 つまり、ユーザー コントロール 分離コードクラスへの追加を行う以下のいずれかの方法によって、CategoryIDプロパティをユーザー コントロールに追加することができます。

public class DisplayProductsByCategory : System.Web.UI.UserControl
{
   public int CategoryID;  // パブリック メンバ変数

   private void Page_Load(object sender, System.EventArgs e)
   {
      ...
   }
}
                  

別の方法は次のとおりです。

public class DisplayProductsByCategory : System.Web.UI.UserControl
{
   // ******** プロパティ構文 ************
   private int categoryID;
   public int CategoryID
   {
      get
      {
         return categoryID;
      }
      set
      {
         categoryID = value;
      }
   }
   // *************************************

   private void Page_Load(object sender, System.EventArgs e)
   {
      ...
   }
}

                  

プロパティ構文の利点は、プロパティ値が正しい範囲内にあるかどうかを確認するための検査を実行できることにあります。 さらに、このすぐ後のサンプルに見られるとおり、プロパティ値を ViewState に保存する必要が生じることがあります。そのような場合に、プロパティ構文を使用することができます。

特定のカテゴリに属する製品を表示するには、ハードコーディングした値を設定するのとは対照的に、BindData() メソッドを更新して、CategoryID を指定するパラメータを SQL ステートメントで使用するようにする必要があります。 以下のコードは、更新後のユーザー コントロール DisplayProductsByCategory.ascx 用の BindData() メソッドを示しています。

private void BindData(string orderBy)
{
   OleDbConnection myConnection = new
OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
Server.MapPath("Northwind.mdb"));

   string productSQL = "SELECT ProductName, UnitPrice FROM " +
     "Products WHERE CategoryID = @CatID ORDER BY " + orderBy;

   OleDbCommand myCommand = new OleDbCommand(productSQL, myConnection);
   myCommand.Parameters.Add(new OleDbParameter("@CatID", CategoryID));

   myConnection.Open();
   dgProducts.DataSource = myCommand.ExecuteReader();
   dgProducts.DataBind();
   myConnection.Close();
}

                  

@CatID パラメータには、CategoryID プロパティの値が割り当てられていることに注意してください。 これで、CategoryID プロパティを使用して指定したカテゴリに属する製品だけが確実に DataGrid に表示されるようになります。

ここまでで、製品のどのサブセットを DataGrid に表示するかを指示する値をもつ CategoryID プロパティを指定したユーザー コントロールを作成しました。 残る作業は、そのプロパティを ASP.NET Web ページ内にプログラマチックに設定するだけです。 その解説として、新規の Web ページ Condiments.aspx を作成してみます。これは、DisplayProductsByCategory.ascx ユーザー コントロールを使用して、調味料製品 (CategoryID が 2 のもの) だけを表示します。

ユーザー コントロールを Condiments.aspx の HTML 部分に追加する場合、ユーザー コントロールの ID 属性を記入することが重要です。 この ID は、分離コードクラス内のユーザー コントロールを参照するのに使用します。 つまり、HTML 部分にユーザー コントロールを追加するときは、次のような宣言構文を使用します。

<skm:ShowProducts runat="server" id="beverages"></skm:ShowProducts>

                  

ASP.NET Web ページの分離コードクラスでは、ユーザー コントロールをプログラマチックに参照し、その CategoryID プロパティを値 2 に設定しなければなりません。そのためには、以下の 2 つの方法のいずれかを行います。

  • FindControl(ID) メソッドを使用して、Web ページのコントロール階層内のユーザー コントロールを見つけ出します。
  • Web ページの分離コードクラス内で、ユーザー コントロールと同じタイプをもち、ユーザー コントロールの ID と同じ名前の付いた、保護付きのメンバ変数を作成します。

この 2 つのアプローチをそれぞれ考察してみます。 最初のアプローチは、以下のコードを使用して完遂することができます。

private void Page_Load(object sender, System.EventArgs e)
{
   if (!Page.IsPostBack)
   {
      DisplayProductsByCategory myUC =
        (DisplayProductsByCategory) FindControl("beverages");
      myUC.CategoryID = 2;
   }
}

                  

FindControl(ID) メソッドは、単一の文字列入力を取り込んで、入力パラメータに等しい ID プロパティをもつコントロールをページのコントロール階層内で探します。 そのようなコントロールが見つかった場合、それが戻されます。見つからなかった場合、ヌル (ただし、Microsoft Visual Basic .NET では Nothing) が戻されます。 FindControl(ID) は、タイプ Control のオブジェクトを戻すので、戻り値を Control からユーザー コントロールのタイプ DisplayProductsByCategory にキャストする必要があります。

2 番目の方式では、適切なタイプと名前を指定した保護付きメンバ変数を、Web ページの分離コードクラスに追加します。 それが完了したら、以下のサンプルに示されているとおり、そのメンバ変数からユーザー コントロールを直接参照できるようになります。

public class Condiments : System.Web.UI.Page
{
   protected DisplayProductsByCategory beverages;

   private void Page_Load(object sender, System.EventArgs e)
   {
      if (!Page.IsPostBack)
         beverages.CategoryID = 2;
   }
}

                  

保護付きメンバ変数は、正しいタイプ (DisplayProductsByCategory) になっていて、Web ページの HTML 部分内のユーザー コントロールの ID プロパティと同じ名前が付いていることに注意してください。 この変数の宣言が完了したら、Page_Load イベント ハンドラに示されているとおり、直接参照できるようになります。 (メンバ変数の名前と、ユーザー コントロールの ID プロパティの値が一致しないと、NullReferenceException 例外が発生することに気を付けてください。)

注意 : Visual Studio .NET を使用してユーザー コントロールを作成する場合、ユーザー コントロールのクラス名は、そのユーザー コントロールの分離コードクラスの名前 (ここの場合は DisplayProductsByCategory) になります。 ユーザー コントロールで分離コードクラスを使用しないで、代わりにサーバー側の <script> ブロック内にユーザー コントロールのソース コードを配備する場合、@Control ディレクティブの ClassName 属性内にユーザー コントロールのクラス名を指定することができます。 (@Control ディレクティブの詳細は、「技術文書」 (英語) を参照してください。)

以下のスクリーン ショットは、ブラウザからアクセスした場合の Condiments.aspx ページを示しています。 表示される製品は、調味料製品ラインに属し、この製品サブセットが表示される理由は、ASP.NET Web ページにおいてユーザー コントロールの CategoryID プロパティが 2 (調味料の CategoryID プロパティ) に設定されているからであることに注意してください。

ms972975.usercontrols_fig08(ja-jp,MSDN.10).gif

図 8. DisplayProductsByCategory コントロールを示した調味料ページ


postback の処理

前の項で考察した DisplayBeveragesSorting.ascx ユーザー コントロールと同様に、DisplayProductsByCategory.ascx ユーザー コントロールも、並び替え可能な DataGrid を使用します。 ユーザーが DataGrid の並び替え可能な列ヘッダのうちの 1 つをクリックすると、Web ページは postback されて、ユーザー コントロールのイベント ハンドラが起動されるので、BindData(string) が再呼び出しされて、クリックされた列の SortExpression が引き渡されます。

理想どおりなら、適切に並べ替えられた調味料の正しいサブセットがページに表示されるはずです。 しかし、DataGrid ヘッダ リンクをクリックしても、どのレコードも表示されません (図 9 を参照)。

ms972975.usercontrols_fig09(ja-jp,MSDN.10).gif

図 9. ヘッダをクリックしたときの動作


こうなってしまう原因は、ASP.NET Web ページが postback されても、それが ASP.NET Web ページの Page_Load イベント ハンドラ内であると、ユーザー コントロールの CategoryID プロパティが割り当てられるのは、Page.IsPostBack が false の場合のみだからです。 したがって、ユーザー コントロールの CategoryID のデフォルトの int 値は 0 になっています。しかし、0 の CategoryID の製品はないため、DataGrid の一覧には何も製品が表示されないことになります。

簡単な対策としては、ユーザー コントロールの CategoryID プロパティが、postback であってもなくても、このプロパティをページ上で設定します。 それに勝る技法としては、複数の postback にまたがってプロパティ値を維持する責任をユーザー コントロールに負わせます。 これを実現するには、プライベート メンバ変数とは対照的に、ユーザー コントロールの CategoryID のプロパティ構文を変更して、ViewState に対して値の読み取りおよび書き込みを行います。 それに必要な変更は次のとおりです。

public int CategoryID
{
   get
   {
      object o = ViewState["CategoryID"];
      if (o == null)
         return 0;   // デフォルト値を戻します。
      else
         return (int) o;
   }
   set
   {
      ViewState["CategoryID"] = value;
   }
}

                  

コンパイル済みのカスタム コントロールをこれまでに作成した経験がある方には、このアプローチのほうが馴染み深いはずです。 ViewState とは、レンダリングされる Web ページの <form> 内の非表示の <input> フィールド内で、レンダリング中に自動的にシリアライズ化される辞書のことです。 postback と同時にこの非表示の <input> フィールドはシリアライズ化を解除されて、元の ViewState オブジェクトに戻ります。 こうして、複数の postback にまたがって値を維持するために、ViewState オブジェクトを使用できるというわけです。 上記のような変更を行えば、ユーザー コントロールの CategoryID プロパティ値は ViewState 内で維持されるので、postback を複数回行っても失われることはありません。 ユーザー コントロールをこのように拡張すれば、DisplayProductsByCategory.ascxDataGrid の並べ替えを正しく処理できるようになります。

ユーザー コントロール イベント

ASP.NET Web ページに表示される TextBoxDropDownListDataGrid をはじめとする Web コントロールは、サーバー側イベントを起動することができます。そのようなイベントを検出してプログラマチックに処理するには、Web ページの分離コードクラスに定義したイベント ハンドラを使用します。 Web コントロールと同様に、ユーザー コントロールも、そのユーザー コントロールを収容する Web ページで処理できるイベントを提供する (起動時に) ことができます。 ユーザー コントロールを収容するページでユーザー コントロール イベントをキャッチするには、まずユーザー コントロール内でイベントをあらかじめ作成してから、適切な時点でそのイベントを起動する必要があります。

ユーザー コントロール内でのイベントの作成、イベントの起動、およびユーザー コントロールを収容する Web ページ内での起動後のイベントの処理といった概念を解明するために、カテゴリ別に製品を一覧表示する別のサンプルを見てみましょう。 今度は、CategoryID をユーザー コントロールに入れるのではなく、すべての指定可能なカテゴリのドロップダウン リストを追加してみます。 そうすると、ユーザーは、どのカテゴリの製品を表示するかを選択することができます。 選択を行うとただちに Web Form は postback され、DataGrid は再びバインドされて、選択したカテゴリに一致する製品が表示されます。

これらすべてをユーザー コントロール内で遂行することができます。 DropDownList および DataGrid の Web コントロールを備えた DisplayProductSelection.ascx という新規のユーザー コントロールの作成から始めます。 ユーザー コントロールの分離コードクラスで、DropDownList にカテゴリを取り込む必要がありますが、それは最初のロード時のみでその後の postback では必要ありません。 すると、BindData() メソッドは、ドロップダウン リスト内で選択されているカテゴリに一致するカテゴリをもった製品を表示します。このメソッドが呼び出されるのは、最初のページ ロード時と、DropDownList Web コントロールの SelectedIndexChanged イベントの起動時のみです。 このユーザー コントロールの分離コードクラスのコードを、以下に示してあります。

private void Page_Load(object sender, System.EventArgs e)
{
   if (!Page.IsPostBack)
   {
      BindCategoriesDDL();
      BindData("UnitPrice DESC");
   }
}

private void BindCategoriesDDL()
{
  // レコードを Categories テーブルからカテゴリ
  // の DropDownList にバインドします (簡略化のためにコードを示していません)
}

private void BindData(string orderBy)
{
   OleDbConnection myConnection = new
OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
Server.MapPath("Northwind.mdb"));

   string productSQL = "SELECT ProductName, UnitPrice FROM " +
     "Products WHERE CategoryID = @CatID ORDER BY " + orderBy;

   OleDbCommand myCommand = new OleDbCommand(productSQL, myConnection);
   myCommand.Parameters.Add(new OleDbParameter("@CatID",
     categories.SelectedValue));

   myConnection.Open();
   dgProducts.DataSource = myCommand.ExecuteReader();
   dgProducts.DataBind();
   myConnection.Close();
}


private void categories_SelectedIndexChanged(object sender,
  System.EventArgs e)
{
   BindData("UnitPrice DESC");
}

                  

このコードを使用すれば、DisplayProductSelection.ascx ユーザー コントロールでユーザーは、ドロップダウン リストからカテゴリを選択してから、そのカテゴリに一致する製品を DataGrid に表示することができます。 図 10 のスクリーン ショットは、ASP.NET Web ページでこのユーザー コントロールを使用した図を示しています。

ms972975.usercontrols_fig10(ja-jp,MSDN.10).gif

図 10. DisplayProductSelection コントロール


これで、ユーザー コントロールは、収容している Web コントロールのイベントを完璧に処理できるようになります。 つまり、ユーザー コントロールの分離コードクラスには、DropDownListSelectedIndexChanged イベント ハンドラが入っています。 上述のとおり、これが理想的なアプローチです。つまり、Web コントロールで起動されたイベントをユーザー コントロールで処理します。 ただし、ユーザーが別のカテゴリの製品を表示しようとしたときに、ASP.NET Web ページでそれを察知したい場合はどうなるでしょうか。

そのサポートとして、ユーザー コントロールの分離コードクラスに対してカスタム イベントを追加し、そのカスタム イベントを DropDownListSelectedIndexChanged イベント ハンドラで起動することができます。 それから、ユーザー コントロールを収容する ASP.NET Web ページは、そのユーザー コントロールのカスタム イベント用のイベント ハンドラを作成することができます。 まず、ユーザー コントロールに対するカスタム イベントの追加と起動について考察してから、ASP.NET Web ページ内にユーザー コントロール イベント用のイベント ハンドラを作成する方法を検討してみます。

カスタム イベントの作成と起動

イベントは、何らかの関心事項が起きたことをクラスから一連のサブスクライバに通知するための手段になります。 サブスクライバとは、イベントが起きたときにアラートを受け取る必要のあるクラスのことです。 ここのサンプルでは、ユーザー コントロール クラス (イベント ソース) は、ユーザーが別の製品カテゴリをドロップダウン リスト (イベント) から選択したことを ASP.NET Web ページ (サブスクライバ) に通知します。

クラス内にイベントを作成するときは、そのイベントのデリゲートを指定する必要があります。 デリゲートは、イベント ハンドラの署名がどのようなものでなければならないかを指示します。つまり、イベント ハンドラがどのような戻り値および入力パラメータを受諾する必要があるかを指示します。 .NET Framework 内のイベントの場合、イベントのデリゲートはいずれも何も戻してはならず、2 つの入力パラメータを受諾する必要があります。そのようなパラメータの一方は、タイプ Object であり、もう一方 (派生されたもの) は System.EventArgs です。

.NET Framework には、いくつかのイベント デリゲートが標準装備されています。 その最も単純なものは EventHandler デリゲートです。これは、イベント用のイベント ハンドラには次のような署名が付くことを指示します。

void eventHandlerName(object sender, EventArgs e)

                  

Web アプリケーションのよく使用される別のデリゲートに CommandEventHandler があります。これは、イベント用のイベント ハンドラには次のような署名が付くことを指示します。

void eventHandlerName(object sender, CommandEventArgs e)

                  

CommandEventArgs クラスは、EventArgs クラスから派生され、CommandArgument および CommandName という 2 つの追加プロパティを内包しています。 CommandEventHandler デリゲートは、ButtonCommand イベント、および DataGridDataList、および RepeaterItemCommand イベントなどの多数の ASP.NET Web コントロール イベントで使用されます。

.NET Framework に組み込まれているイベント デリゲートのうちの 1 つの使用以外に、独自のイベント デリゲートや、EventArgs を拡張した独自のクラスを作成することもできます。 イベントおよびイベント デリゲートの詳細について、「イベントとデリゲート」、「Event Delegates in Simple English」 (英語) 、「イベントの定義」 に必ず目を通してください。

ここでの解説用として、ProductSelectionChanged というイベントをユーザー コントロール内に作成します。これは、CommandEventHandler のタイプのイベント デリゲートをもつことになります。 ユーザー コントロール クラス内にイベントを作成するには、次のような構文を使用します。

// C#
public event CommandEventHandler ProductSelectionChanged;

' VB.NET
Public Event ProductSelectionChanged(ByVal sender as Object,
  ByVal e as CommandEventArgs)

                  

このイベント宣言は、ユーザー コントロールの分離コードクラス内に置かれていなければなりません。

ユーザー コントロール レベルで残っている作業は、ユーザーがカテゴリ ドロップダウン リストを通して別のカテゴリを選択したときにイベントを起動することだけです。

private void categories_SelectedIndexChanged(object sender,
  System.EventArgs e)
{
   BindData("UnitPrice DESC");

   // ProductSelectionChanged イベントを起動します...
   if (ProductSelectionChanged != null)
   {
      CommandEventArgs e = new
        CommandEventArgs(categories.SelectedValue, null);
      ProductSelectionChanged(this, e);
   }
}

                  

イベントを起動するために、新規の CommandEventArgs インスタンスを作成し、メソッドの呼び出しに似た構文を使用してイベントを呼び出しただけであることに注意してください。 ProductSelectionChanged イベントで、EventHandler イベント デリゲートではなく CommandEventHandler イベント デリゲートを使用することにした理由は、ユーザーがドロップダウン リストから選択した CategoryID を一緒に引き渡したかったからです。 単純に、選択された CategoryID を伝送するためのコンジットとして、CommandEventArgs クラスの CommandName プロパティを使用しました。 (これに勝るアプローチとして、特定のデリゲートの作成を作業に組み入れることもできますが、そうすると、サンプルがさらに複雑化されてしまうと考えました。)

注意 : イベント起動のための Visual Basic .NET 構文は、C# とは若干異なることに注意してください。 Visual Basic .NET では、次のように RaiseEvent ステートメントを使用します。
RaiseEvent ProductSelectionChanged(Me, e)

                  

イベントの処理

ここまでで、ユーザー コントロールの分離コードクラスにカスタム イベントを追加し、ユーザーが新規のカテゴリをドロップダウン リストから選択したときにこのイベントが起動されるようにしました。 作業の最終段階では、そのユーザー コントロールを使用するイベント ハンドラを ASP.NET Web ページ内に作成してから、ProductSelectionChanged イベントをイベント ハンドラに結び付けます。

その最初のステップでは、当然、新規の ASP.NET Web ページを作成します。 ここのサンプルでは、AnyProduct.aspx というページを作成し、DisplayProductSelection.ascx ユーザー コントロールを追加しました。 このユーザー コントロールを分離コードクラスで参照する必要があるので、必ずそれに ID 属性を設定するとともに、正しいタイプと、HTML 部分内のユーザー コントロールの ID と同じ名前を使用して、分離コードクラス内に保護付きのメンバ変数を作成します。 (AnyProduct.aspx では、ID 値とメンバ変数は products です。)

注意 : Visual Basic .NET をご使用の場合、必ず WithEvents キーワードを使用して保護付きメンバ変数を宣言してください。 WithEvents キーワードは、処理しようとしているイベントがメンバ変数によって起動される可能性のあることを示します。 その構文は次のようになります。
Protected WithEvents products As DisplayProductSelection.

                  

HTML 部分にユーザー コントロールを追加し、それに対応する保護付きメンバ変数を追加し終わったら、次のステップでは、イベント ハンドラを作成します。 ProductSelectionChanged イベントは、CommandEventHandler イベント デリゲートを使用すると定義されているので、イベント ハンドラは、オブジェクトと CommandEventArgs インスタンスの 2 つの入力パラメータを受け入れる必要があります。 C# においてそれに適したイベント ハンドラはどのようなものかを以下に示してあります。

private void products_ProductSelectionChanged(object sender,
CommandEventArgs e)
{
   Response.Write("The user selected CategoryID " + e.CommandName);
}

                  

イベント ハンドラ名 products_ProductSelectionChanged は、適切な任意のメソッド名でかまわないことに注意してください。 また、イベント ハンドラ内で、CommandEventArgs パラメータの CommandName プロパティを使用することによって、選択された CategoryID を取得できることにも注意してください。

最後のステップでは、ProductSelectionChanged イベントを products_ProductSelectionChanged イベント ハンドラに結びつけます。 C# では、イベント ハンドラは通常は Page クラスの Init イベント中に結びつけられます。 Visual Studio .NET をご使用の場合、分離コードクラスには、OnInit() メソッド内で呼び出される InitializeComponent() メソッドが入っています。 (この 2 つのメソッドは、Web フォーム デザイナで生成されたコードの領域にしまい込まれています。) イベントを結びつけるには、以下のコード行を InitializeComponent() メソッドに追加します。

products.ProductSelectionChanged += new
  CommandEventHandler(this.products_ProductSelectionChanged);

                  

これで products は、分離コードクラス内のユーザー コントロールを表わすメンバ変数になります。 この構文は、イベント デリゲートをイベントに追加します。 CommandEventHandler デリゲートに渡されるパラメータは、ProductSelectionChanged イベントの起動時に呼び出される必要のあるメソッド名です。

注意 : Visual Basic .NET では、次のように Handles キーワードを使用して、イベントをイベント ハンドラに結びつけることができます。
Private Sub products_ProductSelectionChanged(ByVal sender As Object, _
  ByVal e As CommandEventArgs) _
  Handles products.ProductSelectionChanged.

                  

図 11 は、ユーザーがユーザー コントロール内のドロップダウン リストで新規のカテゴリを選択した後の AnyProduct.aspx のスクリーン ショットを示しています。 ユーザーが新規のドロップダウン リスト項目を選択すると、Web Form が postback し、DropDownListSelectedIndexChanged イベントを起動するので、それによってユーザー コントロールのイベント ハンドラが呼び出されます。 このイベント ハンドラは、データを DataGrid に再バインドしてから、その ProductSelectionChanged イベントを起動します。 その結果、ASP.NET Web ページの AnyProduct.aspx 内の products_ProductSelectionChanged イベント ハンドラが実行されて、Response.Write() ステートメントが、ユーザーによって選択された CategoryID の値を送信します。

ms972975.usercontrols_fig11(ja-jp,MSDN.10).gif

図 11. DisplayProductSelection コントロールを示した AnyProduct ページ


ユーザー コントロールの動的追加

Web 開発での私の経歴は、1998 年にクラシック ASP を使用して、コンサルティング企業向けにイントラネット アプリケーションを構築したときから始まります。 そのときの Web アプリケーションでの要件の 1 つは、ユーザーごとに異なる機能を提供することでした。 たとえば、コンサルタントによっては、現在作業中のプロジェクトに関する情報しか調べることはできませんでした。 他方、部長は、そのチームに割り当てられた任意のプロジェクトに関する情報を編集することができました。 このような要件があったため、ユーザーが Web ページにアクセスしたときには、そのロールを判別したうえで、正しいユーザー インターフェイス要素を表示する必要がありました。

このような、ユーザーのロールまたは許可セットに基づいてユーザー インターフェイス要素を生成するというモデルは、今日の Web アプリケーションでも変わらず一般的に使用されています。 というわけで、ユーザーのロールに基づいて、ユーザーを Web ページに動的に追加する必要が生じることがあります。 たとえば、ASP.NET Web ページにアクセスしたユーザーが部長であった場合、ListManagerProjects.ascx ユーザー コントロールをロードする必要がありますが、コンサルタントが同じページにアクセスした場合は、このユーザー コントロールがロードされることはありません。

ユーザー コントロールを動的に表示および非表示にする 1 つのアプローチとして、ユーザーのロールに基づいて、表示する必要があると思われるすべてのユーザー コントロールを追加してから、すべてのユーザー コントロールに Visible プロパティを設定します。 また別のアプローチとしては、実行時にユーザー コントロールをページに動的にロードします。

ASP.NET Page クラスとすべての Web コントロールの直接または間接の派生元である System.Web.UI.Control クラスには、やはり Control から派生するオブジェクトの集合である Controls プロパティが入っています。 Page クラスには Controls の集合が入り、その集合内の各コントロールには Controls の集合が入り、と以後同様に続いていって、コントロール階層が形成されます。 (コントロール階層の構成図は、上述の図 2 を参照してください。)

Web コントロールまたはユーザー コントロールは、別のコントロールの Controls 集合にプログラマチックに追加できるので、Web コントロールまたはユーザー コントロールを実行時に Page コントロールに動的に挿入することができます。 Web コントロールを動的に追加することは簡単です。次のように、単に、Controls プロパティの Add() メソッドを使用して、Web コントロールを挿入するだけです。

// Button を作成します。
Button b = new Button();
b.Text = "Click Me!";

// Button を Page の Controls 集合に追加します。
Page.Controls.Add(b);

                  

Web ページに対する Web コントロールの動的追加時に、PageViewState に関連して生じる可能性のある多少の問題があります。 それに関する詳細な解説は、この記事で取り上げる範囲を超えています。 しかし、手短に言うと、postback のたびにコントロールを再ロードする必要があり、Web コントロールを動的に追加するのに最も安全な場所は Init イベントの中です。 Web コントロールを動的に追加する方法の詳細は、「[HOWTO] Visual Basic .NET を使用してASP.NET で動的にコントロールを作成する方法」および「プログラムによる Web フォーム ページへのコントロールの追加」 を参照してください。

ユーザー コントロールの動的なロードと、Web コントロールの動的なロードは若干異なります。 ユーザー コントロールを Controls 集合にロードするには、まず LoadControl(string) メソッドを使用して、このコントロールをあらかじめロードしておかなければなりません。 LoadControl(string) は、ロードするユーザー コントロールへの仮想パスを文字列入力としてとります (上記で説明した ~ 構文を使用することができます)。 それによって、ユーザー コントロールの Control インスタンスが戻されます。 次に、次のようにして、この Control インスタンスを PageControls 集合に追加することができます。

// ユーザー コントロールをロードします。
Control uc = LoadControl("~/MyUserControl.ascx");

// ユーザー コントロールを Controls 集合に追加します。
Page.Controls.Add(uc);

                  

Web コントロールの動的ロードの場合と同様に、ユーザー コントロールを動的にロードする場合も、ページへのアクセス (postback も含む) のたびにこれを行う必要があります。 ここでも、その理想的なロード先は、Page の Init イベントです。

注意 : Page クラスだけでなく、どのコントロールにも、Controls 集合があることに注意してください。 したがって、ユーザー コントロールを Web ページ上に正確に配置したい場合は、そのユーザー コントロールを置きたい場所に PlaceHolder Web コントロールをドロップし、ユーザー コントロールをプログラマチックにロードするときに、そのコントロールを PlaceHolderControls 集合に追加します。

この記事のダウンロード資料には、DynamicUserControls.ascx という ASP.NET Web ページが入っています。これは、querystring 値に基づいてユーザー コントロールを動的にロードする方法を説明しています。 その解説では、各 Page のロードごとにユーザー コントロールを Init イベント内で再作成する方法が示されています。

まとめ

この記事では、ユーザー コントロールを詳細に考察しました。 ユーザー コントロールの簡単な作成と配置の方法を手短に考察した後、ユーザー コントロールを Page のコントロール階層に注入する方法と、その出力をレンダリングする方法を検討しました。 その後、いくつかのさらに進んだユーザー コントロールのトピックを見ました。すなわち、Web コントロールのうちの 1 つに対するユーザー コントロールからの応答、ユーザー コントロールへのプロパティの追加、ユーザー コントロールを収容する ASP.NET Web ページ内でのユーザー コントロールへのイベントの追加とイベント ハンドラの作成、およびユーザー コントロールの動的ロードの方法のトピックを取り上げました。

参考資料

ASP.NET: Tips, Tutorials and Code』 (英語)

ASP.NET Data Web Controls Kick Start』 (英語)

Developing Microsoft ASP.NET Server Controls and Components』 (英語)

著者紹介

Scott Mitchell は、5 冊の著書の著者であるとともに、4GuysFromRolla.com の創設者でもあります。Microsoft Web テクノロジの作業に 5 年前から携わっています。 Mitchell 氏は、独立したコンサルタント、講習者、および著者として活躍しています。 同氏の連絡先は、mitchell@4guysfromrolla.com であり、同氏のブログは http://www.scottonwriting.net/sowBlog/ (英語) に掲載されています。


© 2009 Microsoft Corporation. All rights reserved. 使用条件  |  商標  |  プライバシー
Page view tracker