コンテンツ ページのコントロール ID の名前付け (VB)

作成者: Scott Mitchell

PDF のダウンロード

ContentPlaceHolder コントロールが名前付けコンテナーとして機能し、プログラムによって (FindControl 経由で) コントロールを操作するのが困難になる方法を示します。 この問題と回避策を確認します。 また、結果の ClientID 値にプログラムでアクセスする方法についても説明します。

はじめに

すべての ASP.NET サーバー コントロールには、コントロールを ID 一意に識別するプロパティが含まれており、コントロールが分離コード クラスでプログラムによってアクセスされる手段です。 同様に、HTML ドキュメント内の要素には、要素を id 一意に識別する属性を含めることができます。これらの id 値は、プログラムによって特定の HTML 要素を参照するためにクライアント側スクリプトでよく使用されます。 これを考えると、ASP.NET サーバー コントロールが HTML にレンダリングされるときに、その ID 値がレンダリングされた HTML 要素の値として id 使用されると想定できます。 特定の状況では、1 つの値を持つ 1 ID つのコントロールがレンダリングされたマークアップに複数回出現する可能性があるため、これは必ずしも当てはまるわけではありません。 値が の Label Web コントロールを持つ TemplateField を含む GridView コントロールをIDProductName考えてみましょう。 実行時に GridView がデータ ソースにバインドされると、GridView 行ごとにこの Label が 1 回繰り返されます。 レンダリングされる各ラベルには、一意 id の値が必要です。

このようなシナリオを処理するために、ASP.NET では、特定のコントロールを名前付けコンテナーとして示すことができます。 名前付けコンテナーは、新 ID しい名前空間として機能します。 名前付けコンテナー内に表示されるすべてのサーバー コントロールのレンダリング id 値には、名前付けコンテナー コントロールの がプレフィックスとして付 ID けられます。 たとえば、 GridView クラスと GridViewRow クラスはどちらも名前付けコンテナーです。 したがって、 を使用して GridView TemplateField IDProductName で定義された Label コントロールには、 のGridViewID_GridViewRowID_ProductNameレンダリング値が指定されますidGridViewRowID は GridView 行ごとに一意であるため、結果idの値は一意です。

注意

インターフェイスはINamingContainer、特定の ASP.NET サーバー コントロールが名前付けコンテナーとして機能することを示すために使用されます。 インターフェイスは INamingContainer 、サーバー コントロールが実装する必要があるメソッドを示すものではなく、マーカーとして使用されます。 レンダリングされたマークアップを生成する際に、コントロールがこのインターフェイスを実装している場合、ASP.NET エンジンは、その ID 値のプレフィックスを、その子孫のレンダリングされた属性値に自動的に id 付加します。 このプロセスについては、手順 2 で詳しく説明します。

コンテナーに名前を付けると、レンダリングされる属性値が変更される id だけでなく、ASP.NET ページの分離コード クラスからコントロールをプログラムで参照する方法にも影響します。 メソッドは FindControl("controlID") 、プログラムで Web コントロールを参照するために一般的に使用されます。 ただし、 FindControl は名前付けコンテナーを通過しません。 そのため、 メソッドを Page.FindControl 直接使用して GridView またはその他の名前付けコンテナー内のコントロールを参照することはできません。

マスター ページと ContentPlaceHolders はどちらも名前付けコンテナーとして実装されています。 このチュートリアルでは、マスター ページが HTML 要素 id の値にどのように影響するか、および を使用して FindControlコンテンツ ページ内の Web コントロールをプログラムで参照する方法について説明します。

手順 1: 新しい ASP.NET ページを追加する

このチュートリアルで説明する概念を示すために、新しい ASP.NET ページを Web サイトに追加しましょう。 ルート フォルダーに という名前 IDIssues.aspx の新しいコンテンツ ページを作成し、マスター ページに Site.master バインドします。

コンテンツ ページ IDIssues.aspxをルート フォルダーに追加する

図 01: ルート フォルダーにコンテンツ ページ IDIssues.aspx を追加する

Visual Studio では、マスター ページの 4 つの ContentPlaceHolders ごとに Content コントロールが自動的に作成されます。 「複数の ContentPlaceHolders と既定のコンテンツ」チュートリアルで説明したように、コンテンツ コントロールが表示されない場合は、マスター ページの既定の ContentPlaceHolder コンテンツが代わりに出力されます。 QuickLoginUIおよび LeftColumnContent ContentPlaceHolders には、このページに適した既定のマークアップが含まれているため、対応する Content コントロールを からIDIssues.aspx削除してください。 この時点で、コンテンツ ページの宣言型マークアップは次のようになります。

<%@ Page Language="VB" MasterPageFile="~/Site.master" AutoEventWireup="false" CodeFile="IDIssues.aspx.vb" Inherits="IDIssues" Title="Untitled Page" %>

<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" Runat="Server">
</asp:Content>

マスター ページのチュートリアルのタイトル、メタ タグ、およびその他の HTML ヘッダーの指定に関するチュートリアルでは、ページのタイトルが明示的に設定されていない場合に自動的に構成するカスタム 基本ページ クラス (BasePage) を作成しました。 ページでIDIssues.aspxこの機能を使用するには、ページの分離コード クラスを ( ではなくSystem.Web.UI.Page) クラスからBasePage派生させる必要があります。 分離コード クラスの定義を次のように変更します。

Partial Class IDIssues
 Inherits BasePage

End Class

最後に、この新しいレッスンの Web.sitemap エントリを含むようにファイルを更新します。 要素を <siteMapNode> 追加し、その title 属性と url 属性をそれぞれ "Control ID Naming Issues" と ~/IDIssues.aspxに設定します。 この追加を行った後、 Web.sitemap ファイルのマークアップは次のようになります。

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
 <siteMapNode url="~/Default.aspx" title="Home">
 <siteMapNode url="~/About.aspx" title="About the Author" />
 <siteMapNode url="~/MultipleContentPlaceHolders.aspx" title="Using Multiple ContentPlaceHolder Controls" />
 <siteMapNode url="~/Admin/Default.aspx" title="Rebasing URLs" />
 <siteMapNode url="~/IDIssues.aspx" title="Control ID Naming Issues" />
 </siteMapNode>
</siteMap>

図 2 に示すように、 の新しいサイト マップ エントリ Web.sitemap は、左側の列の [レッスン] セクションにすぐに反映されます。

レッスン セクションに、

図 02: レッスン セクションに、"コントロール ID の名前付けの問題" へのリンクが含まれるようになりました

手順 2: レンダリングされたID変更を調べる

ASP.NET エンジンがサーバー コントロールのレンダリングされた id 値に加える変更について理解を深めるために、いくつかの Web コントロールをページに IDIssues.aspx 追加し、ブラウザーに送信されたレンダリングされたマークアップを表示してみましょう。 具体的には、"年齢を入力してください:" というテキストの後に TextBox Web コントロールを入力します。 ページの下にボタン Web コントロールとラベル Web コントロールを追加します。 TextBox ID の プロパティと Columns プロパティをそれぞれ 3 に Age 設定します。 Button Text の プロパティと ID プロパティを "Submit" および SubmitButtonに設定します。 Label Text の プロパティをクリアし、 を に設定しますResultsID

この時点で、コンテンツ コントロールの宣言型マークアップは次のようになります。

<p>
 Please enter your age:
 <asp:TextBox ID="Age" Columns="3" runat="server"></asp:TextBox>
</p>
<p>
 <asp:Button ID="SubmitButton" runat="server" Text="Submit" />
</p>
<p>
 <asp:Label ID="Results" runat="server"></asp:Label>
</p>

図 3 は、Visual Studio のデザイナーで表示されるページを示しています。

ページには、テキスト ボックス、ボタン、ラベルの 3 つの Web コントロールが含まれています

図 03: ページには、テキスト ボックス、ボタン、ラベルの 3 つの Web コントロールが含まれています (フルサイズの画像を表示する をクリックします)。

ブラウザーからページにアクセスし、HTML ソースを表示します。 次のマークアップが示すように、TextBox、 id Button、および Label Web コントロールの HTML 要素の値は、Web コントロールの ID 値と ID 、ページ内の名前付けコンテナーの値の組み合わせです。

<p>
 Please enter your age:
 <input name="ctl00$MainContent$Age" type="text" size="3" id="ctl00_MainContent_Age" />
</p>
<p>

 <input type="submit" name="ctl00$MainContent$SubmitButton" value="Submit" id="ctl00_MainContent_SubmitButton" />
</p>
<p>
 <span id="ctl00_MainContent_Results"></span>
</p>

このチュートリアルで前述したように、マスター ページとその ContentPlaceHolders の両方が名前付けコンテナーとして機能します。 したがって、両方とも、入れ子になったコントロールのレンダリングされた ID 値を提供します。 たとえば、 TextBox の id 属性を取得します ctl00_MainContent_Age。 TextBox コントロールの ID 値が であることを思い出してください Age。 このプレフィックスには、ContentPlaceHolder コントロールの IDMainContentが付きます。 さらに、この値の先頭にはマスター ページの IDctl00である が付いています。 正味効果は、 id マスター ページ、 ID ContentPlaceHolder コントロール、および TextBox 自体の値で構成される属性値です。

図 4 は、この動作を示しています。 TextBox のレンダリングidAgeを決定するには、 TextBox コントロールAgeの値からID始めます。 次に、制御階層を上に進みます。 各名前付けコンテナー (ピーチ色のノード) で、現在のレンダリングに id 名前付けコンテナーの idのプレフィックスを付けます。

Rendered id 属性は、名前付けコンテナーの ID 値に基づいています

図 04: レンダリングされた id 属性は、名前付けコンテナーの ID 値に基づいています

注意

説明したように、 ctl00 レンダリングされた属性の部分は id マスター ページの値を構成 ID しますが、この ID 値がどのように発生したか疑問に思うかもしれません。 マスター ページまたはコンテンツ ページのどこにも指定しませんでした。 ASP.NET ページのほとんどのサーバー コントロールは、ページの宣言型マークアップを通じて明示的に追加されます。 ContentPlaceHolder コントロールはMainContent、 のSite.masterAgeマークアップで明示的に指定されました。TextBox は' のマークアップを定義IDIssues.aspxしました。 これらの種類のコントロールのID値は、プロパティ ウィンドウまたは宣言構文を使用して指定できます。 マスター ページ自体などの他のコントロールは、宣言型マークアップでは定義されません。 したがって、それらの ID 値は自動的に生成される必要があります。 ASP.NET エンジンは、ID が ID 明示的に設定されていないコントロールの実行時に値を設定します。 名前付けパターン ctlXXを使用します。 ここで、XX は連続して増加する整数値です。

マスター ページ自体は名前付けコンテナーとして機能するため、マスター ページで定義されている Web コントロールには、レンダリングされた属性値も変更されています id 。 たとえば、マスター ページを DisplayDate 使用した Site-Wide レイアウトの作成 チュートリアルでマスター ページに追加したラベルには、次のレンダリングされたマークアップがあります。

<span id="ctl00_DateDisplay">current date</span>

属性には、idマスター ページIDの値 () と ID Label Web コントロールDateDisplay (ctl00) の値の両方が含まれていることに注意してください。

手順 3: プログラムを使用して Web コントロールを参照するFindControl

すべての ASP.NET サーバー コントロールには、 FindControl("controlID") コントロールの子孫で controlID という名前のコントロールを検索するメソッドが含まれています。 このようなコントロールが見つかった場合は、 が返されます。一致するコントロールが見つからない場合は を FindControl 返します Nothing

FindControl は、コントロールにアクセスする必要があるが、コントロールへの直接参照がないシナリオで役立ちます。 たとえば、GridView などのデータ Web コントロールを操作する場合、GridView のフィールド内のコントロールは宣言構文で 1 回定義されますが、実行時には GridView 行ごとにコントロールのインスタンスが作成されます。 そのため、実行時に生成されるコントロールは存在しますが、分離コード クラスから直接参照を使用することはできません。 その結果、GridView のフィールド内の特定のコントロールをプログラムで操作するために を使用 FindControl する必要があります。 (を使用して FindControl データ Web コントロールのテンプレート内のコントロールにアクセスする方法の詳細については、「データ に基づくカスタム書式設定」を参照してください)。この同じシナリオは、「 動的データ 入力ユーザー インターフェイスの作成」で説明されているトピックである Web フォームに Web コントロールを動的に追加する場合にも発生します。

メソッドをFindControl使用してコンテンツ ページ内のコントロールを検索する方法を説明するには、 の イベントのイベント ハンドラーをClickSubmitButton作成します。 イベント ハンドラーに次のコードを追加します。このコードは、 メソッドを使用して FindControl TextBox と Results Label をプログラムによって参照Ageし、ユーザーの入力に基づいて にResultsメッセージを表示します。

注意

もちろん、この例の Label コントロールと TextBox コントロールを参照するために を使用 FindControl する必要はありません。 プロパティ値を使用して ID 直接参照できます。 ここでは、コンテンツ ページから を使用する場合の動作を示すために使用FindControlFindControlします。

Protected Sub SubmitButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SubmitButton.Click
 Dim ResultsLabel As Label = CType(FindControl("Results"), Label)
 Dim AgeTextBox As TextBox = CType(Page.FindControl("Age"), TextBox)

 ResultsLabel.Text = String.Format("You are {0} years old!", AgeTextBox.Text)
End Sub

メソッドの呼び出し FindControl に使用される構文は の最初の SubmitButton_Click2 行で若干異なりますが、意味的には同等です。 すべての ASP.NET サーバー コントロールに メソッドが含まれることを思い FindControl 出してください。 これには、 クラスが Page 含まれます。このクラスには、すべての ASP.NET 分離コード クラスの派生元である必要があります。 したがって、 の呼び出し FindControl("controlID") は、 を呼び出すこと Page.FindControl("controlID")と同じです。これは、分離コード クラスまたはカスタム基底クラスで メソッドをオーバーライド FindControl していないと仮定します。

このコードを入力したら、ブラウザーからページに IDIssues.aspx アクセスし、年齢を入力して、[送信] ボタンをクリックします。 [送信] ボタンをクリックすると、 NullReferenceException が発生します (図 5 を参照)。

NullReferenceException が発生する

図 05: A NullReferenceException が発生しました (クリックするとフルサイズの画像が表示されます)

イベント ハンドラーにブレークポイントを SubmitButton_Click 設定すると、両方が を呼び出して を FindControlNothingすことがわかります。 NullReferenceException TextBox Text の プロパティにアクセスしようとすると、 Age が発生します。

問題は、 Control.FindControl 同じ名前付けコンテナー内にある コントロールの子孫のみを検索することです。 マスター ページは新しい名前付けコンテナーを構成するため、 を Page.FindControl("controlID") 呼び出すとマスター ページ オブジェクト ctl00に浸透することはありません。 (マスター ページ オブジェクトctl00の親としてオブジェクトを示すPageコントロール階層を表示するには、図 4 を参照してください)。したがって、ResultsLabel と Age TextBox は見つかAgeTextBoxResultsLabelらず、 のNothing値が割り当てられます。

この課題には 2 つの回避策があります。適切なコントロールに対して、一度に 1 つの名前付けコンテナーをドリルダウンできます。または、名前付けコンテナーに浸透する独自 FindControl のメソッドを作成することもできます。 これらの各オプションを調べてみましょう。

適切な名前付けコンテナーへのドリルイン

を使用FindControlして Label または Age TextBox をResults参照するには、同じ名前付けコンテナー内の先祖コントロールから を呼び出すFindControl必要があります。 図 4 に示すように、MainContentContentPlaceHolder コントロールは、 または Age の唯一のResults先祖であり、同じ名前付けコンテナー内にあります。 つまり、次のFindControlコード スニペットに示すように、 コントロールから メソッドをMainContent呼び出すと、 コントロールまたは Age コントロールへの参照がResults正しく返されます。

Dim ResultsLabel As Label = CType(MainContent.FindControl("Results"), Label)
Dim AgeTextBox As TextBox = CType(MainContent.FindControl("Age"), TextBox)

ただし、ContentPlaceHolder はマスター ページで MainContent 定義されているため、上記の構文を使用してコンテンツ ページの分離コード クラスから ContentPlaceHolder を操作することはできません。 代わりに、 を使用 FindControl して への参照を取得する MainContent必要があります。 イベント ハンドラーのコードを SubmitButton_Click 次の変更に置き換えます。

Protected Sub SubmitButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SubmitButton.Click
 Dim MainContent As ContentPlaceHolder = CType(FindControl("MainContent"), ContentPlaceHolder)

 Dim ResultsLabel As Label = CType(MainContent.FindControl("Results"), Label)
 Dim AgeTextBox As TextBox = CType(MainContent.FindControl("Age"), TextBox)

 ResultsLabel.Text = String.Format("You are {0} years old!", AgeTextBox.Text)
End Sub

ブラウザーからページにアクセスし、年齢を入力し、[送信] ボタンをクリックすると、 NullReferenceException が発生します。 イベント ハンドラーにブレークポイントをSubmitButton_Click設定すると、オブジェクトFindControlの メソッドを呼び出そうとしたときにこの例外がMainContent発生することがわかります。 メソッドが "MainContent" という名前のFindControlオブジェクトを見つけることができないため、 オブジェクトは MainContent と等しくなりますNothing。 基になる理由は、Label コントロールと TextBox コントロールFindControlAgeResults同じです。コントロール階層の上部から検索を開始し、名前付けコンテナーに侵入しませんがMainContent、ContentPlaceHolder は名前付けコンテナーであるマスター ページ内にあります。

を使用 FindControl して への MainContent参照を取得するには、まずマスター ページ コントロールへの参照が必要です。 マスター ページへの参照を取得したら、 を使用して ContentPlaceHolder へのMainContent参照を取得し、そこから Label と Age TextBox へのResults参照を取得できます (ここでも、 を使用してFindControl)。FindControl しかし、マスター ページへの参照を取得するにはどうすればよいですか? レンダリングされたマークアップの属性を id 調べることで、マスター ページの ID 値が であることが明らかになります ctl00。 したがって、 を使用 Page.FindControl("ctl00") してマスター ページへの参照を取得し、そのオブジェクトを使用して への参照を MainContent取得できます。 次のスニペットは、このロジックを示しています。

'Get a reference to the master page
Dim ctl00 As MasterPage = CType(FindControl("ctl00"), MasterPage)

'Get a reference to the ContentPlaceHolder
Dim MainContent As ContentPlaceHolder = CType(ctl00.FindControl("MainContent"), ContentPlaceHolder)

'Reference the Label and TextBox controls
Dim ResultsLabel As Label = CType(MainContent.FindControl("Results"), Label)
Dim AgeTextBox As TextBox = CType(MainContent.FindControl("Age"), TextBox)

このコードは確実に機能しますが、マスター ページの自動生成 ID は常に であると ctl00想定しています。 自動生成された値について仮定することは決して良い考えではありません。

さいわい、マスター ページへの参照には、 クラスの Master プロパティをPage使用してアクセスできます。 したがって、ContentPlaceHolder にアクセスMainContentするためにマスター ページの参照を取得するために を使用FindControl("ctl00")する代わりに、 を使用Page.Master.FindControl("MainContent")できます。 イベント ハンドラーを次の SubmitButton_Click コードで更新します。

Protected Sub SubmitButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SubmitButton.Click
 'Get a reference to the ContentPlaceHolder
 Dim MainContent As ContentPlaceHolder = CType(Page.Master.FindControl("MainContent"), ContentPlaceHolder)

 'Reference the Label and TextBox controls
 Dim ResultsLabel As Label = CType(MainContent.FindControl("Results"), Label)
 Dim AgeTextBox As TextBox = CType(MainContent.FindControl("Age"), TextBox)

 ResultsLabel.Text = String.Format("You are {0} years old!", AgeTextBox.Text)
End Sub

今回は、ブラウザーからページにアクセスし、年齢を入力し、[送信] ボタンをクリックすると、期待どおりにラベルにメッセージが Results 表示されます。

ユーザーの年齢がラベルに表示される

図 06: ユーザーの年齢がラベルに表示されている (フルサイズの画像を表示する をクリックします)

名前付けコンテナーを使用した再帰的な検索

前のコード例がマスター ページから ContentPlaceHolder コントロールをMainContent参照しResults、 Label コントロールと Age TextBox コントロールを からMainContent参照した理由は、 メソッドが Control の名前付けコンテナー内でのみ検索するためControl.FindControlです。 FindControl 2 つの異なる名前付けコンテナー内の 2 つのコントロールの値が同じであるID可能性があるため、ほとんどのシナリオでは、名前付けコンテナー内に留まるのが理にかなっています。 TemplateFields の 1 つ内で という名前 ProductName のラベル Web コントロールを定義する GridView のケースを考えてみましょう。 実行時にデータが GridView にバインドされると、 ProductName GridView 行ごとに Label が作成されます。 すべての名前付けコンテナーを検索し、 を呼び出Page.FindControl("ProductName")した場合FindControl、どの Label インスタンスを返すFindControl必要がありますか? 最初の ProductName GridView 行の Label? 最後の行の 1 つ?

Control.FindControlそのため、ほとんどの場合、Control の名前付けコンテナーだけを検索することは理にかなっています。 しかし、他にも、すべての名前付けコンテナーに対して一意 ID の名前付けコンテナーがあり、コントロールにアクセスするためにコントロール階層内の各名前付けコンテナーを細心の注意を払って参照する必要がないようにしたい場合があります。 すべての名前付けコンテナーを FindControl 再帰的に検索するバリアントを持つことも理にかなっています。 残念ながら、.NET Frameworkにはこのようなメソッドは含まれていません。

良いニュースは、すべての名前付けコンテナーを再帰的に検索する独自 FindControl のメソッドを作成できることです。 実際、拡張メソッドを使用すると、メソッドを クラスにFindControlRecursive取り付けて、既存FindControlControlメソッドに付随させることができます。

注意

拡張メソッドは、C# 3.0 および Visual Basic 9 の新機能です。これは、.NET Framework バージョン 3.5 および Visual Studio 2008 に付属する言語です。 つまり、拡張メソッドを使用すると、開発者は特殊な構文を使用して既存のクラス型の新しいメソッドを作成できます。 この便利な機能の詳細については、拡張 メソッドを使用した基本型機能の拡張に関する記事を参照してください。

拡張メソッドを作成するには、 という名前PageExtensionMethods.vbのフォルダーに新しいファイルをApp_Code追加します。 という名前FindControlRecursiveのパラメーターを入力として受け取る という名前controlIDの拡張メソッドをString追加します。 拡張メソッドを適切に機能させるには、クラスを として Module マークし、拡張メソッドの前に 属性を <Extension()> 付ける必要があります。 さらに、すべての拡張メソッドは、拡張メソッドが適用される型のオブジェクトを最初のパラメーターとして受け入れる必要があります。

ファイルに次のコードを PageExtensionMethods.vb 追加して、これと Module 拡張メソッドを FindControlRecursive 定義します。

Imports System.Runtime.CompilerServices

Public Module PageExtensionMethods
 <Extension()> _
  Public Function FindControlRecursive(ByVal ctrl As Control, ByVal controlID As String) As Control
 If String.Compare(ctrl.ID, controlID, True) = 0 Then
 ' We found the control!
 Return ctrl
 Else
 ' Recurse through ctrl's Controls collections
 For Each child As Control In ctrl.Controls
 Dim lookFor As Control = FindControlRecursive(child, controlID)

 If lookFor IsNot Nothing Then
 Return lookFor  ' We found the control
 End If
 Next

 ' If we reach here, control was not found
 Return Nothing
 End If
 End Function
End Module

このコードを配置したら、ページの分離コード クラスに IDIssues.aspx 戻り、現在 FindControl のメソッド呼び出しをコメントアウトします。 を への呼び出しに Page.FindControlRecursive("controlID")置き換えます。 拡張メソッドの概要は、IntelliSense ドロップダウン リスト内に直接表示される点です。 図 7 に示すように、ピリオドを入力 Page してヒットすると、 FindControlRecursive そのメソッドは他 Control のクラス メソッドと共に IntelliSense ドロップダウンに含まれます。

拡張メソッドが IntelliSense ドロップダウンに含まれる

図 07: 拡張メソッドは IntelliSense Drop-Downs に含まれています (フルサイズの画像を表示するには、ここをクリックします)

イベント ハンドラーに次のコードを SubmitButton_Click 入力し、ページにアクセスして年齢を入力し、[送信] ボタンをクリックしてテストします。 図 6 に示すように、結果の出力は"年齢が年を取っています" というメッセージになります。

Protected Sub SubmitButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SubmitButton.Click
 Dim ResultsLabel As Label = CType(Page.FindControlRecursive("Results"), Label)
 Dim AgeTextBox As TextBox = CType(Page.FindControlRecursive("Age"), TextBox)

 ResultsLabel.Text = String.Format("You are {0} years old!", AgeTextBox.Text)
End Sub

注意

拡張メソッドは C# 3.0 および Visual Basic 9 の新機能であるため、Visual Studio 2005 を使用している場合は拡張メソッドを使用できません。 代わりに、 メソッドをヘルパー クラスに実装 FindControlRecursive する必要があります。 Rick Strahl は、ブログ記事「 ASP.NET Maser Pages と 」 でこのような例を示しています FindControl

手順 4: Client-Side スクリプトで正しいid属性値を使用する

このチュートリアルの概要で説明したように、Web コントロールのレンダリング属性 id は、多くの場合、特定の HTML 要素をプログラムで参照するためにクライアント側スクリプトで使用されます。 たとえば、次の JavaScript は、 によって id HTML 要素を参照し、その値をモーダル メッセージ ボックスに表示します。

var elem = document.getElementById("Age");
if (elem != null)
    alert("You entered " + elem.value + " into the Age text box.");

名前付けコンテナーを含まない ASP.NET ページでは、レンダリングされた HTML 要素の id 属性は Web コントロールの ID プロパティ値と同じであることを思い出してください。 このため、属性値内のコードを JavaScript コードにハード コーディング id する必要があります。 つまり、クライアント側スクリプトを使用して TextBox Web コントロールにアクセス Age することがわかっている場合は、 の呼び出しを使用してアクセスします document.getElementById("Age")

この方法の問題は、マスター ページ (またはその他の名前付けコンテナー コントロール) を使用する場合、レンダリングされる HTML id は Web コントロールの ID プロパティと同義ではないということです。 最初の傾斜は、ブラウザーを使用してページにアクセスし、ソースを表示して実際 id の属性を決定することです。 レンダリングされた id 値がわかったら、 の呼び出し getElementById に貼り付けて、クライアント側スクリプトを使用して操作する必要がある HTML 要素にアクセスできます。 この方法は、ページのコントロール階層に対する特定の変更や名前付けコントロールのプロパティの変更によって結果idの属性が変更IDされ、JavaScript コードが破損するため、理想的ではありません。

良いニュースは、 id レンダリングされる属性値が Web コントロール ClientID の プロパティを介してサーバー側のコードでアクセス可能であるということです。 このプロパティを使用して、クライアント側スクリプトで使用される属性値を決定 id する必要があります。 たとえば、呼び出されたときに TextBox の値をモーダル メッセージ ボックスに表示する JavaScript 関数をページに追加するには、次の Age コードをイベント ハンドラーに Page_Load 追加します。

ClientScript.RegisterClientScriptBlock(Me.GetType(), "ShowAgeTextBoxScript", _
 "function ShowAge() " & vbCrLf & _
 "{" & vbCrLf & _
 " var elem = document.getElementById('" & AgeTextBox.ClientID & "');" & vbCrLf & _
 " if (elem != null)" & vbCrLf & _
 " alert('You entered ' + elem.value + ' into the Age text box.');" & vbCrLf & _
 "}", True)

上記のコードでは、TextBox ClientID の プロパティの値が Age への JavaScript 呼び出しにgetElementById挿入されます。 ブラウザーからこのページにアクセスし、HTML ソースを表示すると、次の JavaScript コードが表示されます。

<script type="text/javascript">
//<![CDATA[
function ShowAge()
{
 var elem = document.getElementById('ctl00_MainContent_Age');
 if (elem != null)
 alert('You entered ' + elem.value + ' into the Age text box.');
}//]]>
</script>

への呼び出しの中で、正しいid属性値 ( ) がどのように表示されるかに注目してくださいgetElementByIdctl00_MainContent_Age この値は実行時に計算されるため、ページ コントロール階層の後の変更に関係なく機能します。

注意

この JavaScript の例は、サーバー コントロールによってレンダリングされる HTML 要素を正しく参照する JavaScript 関数を追加する方法を示しています。 この関数を使用するには、ドキュメントの読み込み時または特定のユーザー アクションのトランスパイル時に関数を呼び出すために、追加の JavaScript を作成する必要があります。 これらのトピックおよび関連トピックの詳細については、「 Client-Side スクリプトの使用」を参照してください。

まとめ

特定の ASP.NET サーバー コントロールは、名前付けコンテナーとして機能します。これは、子孫コントロールのレンダリングされた id 属性値と、 メソッドによってキャンバス化されたコントロールのスコープに FindControl 影響します。 マスター ページに関しては、マスター ページ自体とその ContentPlaceHolder コントロールの両方がコンテナーに名前を付けます。 そのため、 を使用して FindControlコンテンツ ページ内のコントロールをプログラムで参照するには、もう少し作業を行う必要があります。 このチュートリアルでは、ContentPlaceHolder コントロールをドリルダウンしてそのメソッドを呼び出す FindControl という 2 つの手法を検討しました。また、すべての名前付けコンテナーを再帰的に検索する独自 FindControl の実装をローリングしました。

Web コントロールの参照に関して、サーバー側の名前付けコンテナーで発生する問題に加えて、クライアント側の問題もあります。 コンテナーに名前を付けなければ、Web コントロールの ID プロパティ値とレンダリングされた id 属性値は同じになります。 ただし、名前付けコンテナーが追加されると、レンダリングされた id 属性には、Web コントロールの値と、そのコントロール階層の ancestry 内の名前付けコンテナーの両方 ID が含まれます。 これらの名前付けの問題は、Web コントロールの ClientID プロパティを使用してクライアント側スクリプトでレンダリングされた id 属性値を決定する限り、問題になりません。

プログラミングに満足!

もっと読む

このチュートリアルで説明するトピックの詳細については、次のリソースを参照してください。

著者について

複数の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジと協力しています。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズ・ティーチ・自分自身 ASP.NET 24時間で3.5です。 Scott は、 または mitchell@4GuysFromRolla.com のブログから http://ScottOnWriting.NETアクセスできます。

特別な感謝

このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Zack Jones と Suchi Barnerjee でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、 に mitchell@4GuysFromRolla.com行をドロップしてください。