コンテンツ ページのコントロール ID の名前付け (VB)
ContentPlaceHolder コントロールが名前付けコンテナーとして機能し、プログラムによって (FindControl 経由で) コントロールを操作するのが困難になる方法を示します。 この問題と回避策を確認します。 また、結果の ClientID 値にプログラムでアクセスする方法についても説明します。
はじめに
すべての ASP.NET サーバー コントロールには、コントロールを ID
一意に識別するプロパティが含まれており、コントロールが分離コード クラスでプログラムによってアクセスされる手段です。 同様に、HTML ドキュメント内の要素には、要素を id
一意に識別する属性を含めることができます。これらの id
値は、プログラムによって特定の HTML 要素を参照するためにクライアント側スクリプトでよく使用されます。 これを考えると、ASP.NET サーバー コントロールが HTML にレンダリングされるときに、その ID
値がレンダリングされた HTML 要素の値として id
使用されると想定できます。 特定の状況では、1 つの値を持つ 1 ID
つのコントロールがレンダリングされたマークアップに複数回出現する可能性があるため、これは必ずしも当てはまるわけではありません。 値が の Label Web コントロールを持つ TemplateField を含む GridView コントロールをID
ProductName
考えてみましょう。 実行時に GridView がデータ ソースにバインドされると、GridView 行ごとにこの Label が 1 回繰り返されます。 レンダリングされる各ラベルには、一意 id
の値が必要です。
このようなシナリオを処理するために、ASP.NET では、特定のコントロールを名前付けコンテナーとして示すことができます。 名前付けコンテナーは、新 ID
しい名前空間として機能します。 名前付けコンテナー内に表示されるすべてのサーバー コントロールのレンダリング id
値には、名前付けコンテナー コントロールの がプレフィックスとして付 ID
けられます。 たとえば、 GridView
クラスと GridViewRow
クラスはどちらも名前付けコンテナーです。 したがって、 を使用して GridView TemplateField ID
ProductName
で定義された Label コントロールには、 のGridViewID_GridViewRowID_ProductName
レンダリング値が指定されますid
。 GridViewRowID は 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
バインドします。
図 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
の プロパティをクリアし、 を に設定しますResults
ID
。
この時点で、コンテンツ コントロールの宣言型マークアップは次のようになります。
<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 のデザイナーで表示されるページを示しています。
図 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 コントロールの ID
値 MainContent
が付きます。 さらに、この値の先頭にはマスター ページの ID
値 ctl00
である が付いています。 正味効果は、 id
マスター ページ、 ID
ContentPlaceHolder コントロール、および TextBox 自体の値で構成される属性値です。
図 4 は、この動作を示しています。 TextBox のレンダリングid
Age
を決定するには、 TextBox コントロールAge
の値からID
始めます。 次に、制御階層を上に進みます。 各名前付けコンテナー (ピーチ色のノード) で、現在のレンダリングに id
名前付けコンテナーの id
のプレフィックスを付けます。
図 04: レンダリングされた id
属性は、名前付けコンテナーの ID
値に基づいています
注意
説明したように、 ctl00
レンダリングされた属性の部分は id
マスター ページの値を構成 ID
しますが、この ID
値がどのように発生したか疑問に思うかもしれません。 マスター ページまたはコンテンツ ページのどこにも指定しませんでした。 ASP.NET ページのほとんどのサーバー コントロールは、ページの宣言型マークアップを通じて明示的に追加されます。 ContentPlaceHolder コントロールはMainContent
、 のSite.master
Age
マークアップで明示的に指定されました。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
使用してコンテンツ ページ内のコントロールを検索する方法を説明するには、 の イベントのイベント ハンドラーをClick
SubmitButton
作成します。 イベント ハンドラーに次のコードを追加します。このコードは、 メソッドを使用して FindControl
TextBox と Results
Label をプログラムによって参照Age
し、ユーザーの入力に基づいて にResults
メッセージを表示します。
注意
もちろん、この例の Label コントロールと TextBox コントロールを参照するために を使用 FindControl
する必要はありません。 プロパティ値を使用して ID
直接参照できます。 ここでは、コンテンツ ページから を使用する場合の動作を示すために使用FindControl
FindControl
します。
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_Click
2 行で若干異なりますが、意味的には同等です。 すべての ASP.NET サーバー コントロールに メソッドが含まれることを思い FindControl
出してください。 これには、 クラスが Page
含まれます。このクラスには、すべての ASP.NET 分離コード クラスの派生元である必要があります。 したがって、 の呼び出し FindControl("controlID")
は、 を呼び出すこと Page.FindControl("controlID")
と同じです。これは、分離コード クラスまたはカスタム基底クラスで メソッドをオーバーライド FindControl
していないと仮定します。
このコードを入力したら、ブラウザーからページに IDIssues.aspx
アクセスし、年齢を入力して、[送信] ボタンをクリックします。 [送信] ボタンをクリックすると、 NullReferenceException
が発生します (図 5 を参照)。
図 05: A NullReferenceException
が発生しました (クリックするとフルサイズの画像が表示されます)
イベント ハンドラーにブレークポイントを SubmitButton_Click
設定すると、両方が を呼び出して を FindControl
返 Nothing
すことがわかります。 NullReferenceException
TextBox Text
の プロパティにアクセスしようとすると、 Age
が発生します。
問題は、 Control.FindControl
同じ名前付けコンテナー内にある コントロールの子孫のみを検索することです。 マスター ページは新しい名前付けコンテナーを構成するため、 を Page.FindControl("controlID")
呼び出すとマスター ページ オブジェクト ctl00
に浸透することはありません。 (マスター ページ オブジェクトctl00
の親としてオブジェクトを示すPage
コントロール階層を表示するには、図 4 を参照してください)。したがって、Results
Label と Age
TextBox は見つかAgeTextBox
ResultsLabel
らず、 のNothing
値が割り当てられます。
この課題には 2 つの回避策があります。適切なコントロールに対して、一度に 1 つの名前付けコンテナーをドリルダウンできます。または、名前付けコンテナーに浸透する独自 FindControl
のメソッドを作成することもできます。 これらの各オプションを調べてみましょう。
適切な名前付けコンテナーへのドリルイン
を使用FindControl
して Label または Age
TextBox をResults
参照するには、同じ名前付けコンテナー内の先祖コントロールから を呼び出すFindControl
必要があります。 図 4 に示すように、MainContent
ContentPlaceHolder コントロールは、 または 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 コントロールFindControl
とAge
Results
同じです。コントロール階層の上部から検索を開始し、名前付けコンテナーに侵入しませんが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
取り付けて、既存FindControl
のControl
メソッドに付随させることができます。
注意
拡張メソッドは、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 ドロップダウンに含まれます。
図 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
属性値 ( ) がどのように表示されるかに注目してくださいgetElementById
。 ctl00_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.NET マスター ページと
FindControl
- 動的データ 入力ユーザー インターフェイスの作成
- 方法: マスター ページコンテンツ ASP.NET 参照する
- Mater ページ: ヒント、テクニック、トラップ
- Client-Side スクリプトの操作
著者について
複数の 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行をドロップしてください。
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示