Denominazione degli ID di controllo nelle pagine di contenuto (VB)

di Scott Mitchell

Scarica il PDF

Illustra come i controlli ContentPlaceHolder fungono da contenitore di denominazione e quindi rendono difficile l'uso di un controllo a livello di codice (tramite FindControl). Esamina questo problema e le soluzioni alternative. Viene inoltre illustrato come accedere a livello di codice al valore ClientID risultante.

Introduzione

Tutti i controlli server ASP.NET includono una ID proprietà che identifica in modo univoco il controllo ed è il mezzo a cui il controllo è accessibile a livello di codice nella classe code-behind. Analogamente, gli elementi di un documento HTML possono includere un id attributo che identifica in modo univoco l'elemento. Questi id valori vengono spesso usati nello script lato client per fare riferimento a un particolare elemento HTML a livello di codice. Dato questo, è possibile presupporre che quando viene eseguito il rendering di un controllo server ASP.NET in HTML, il relativo ID valore viene usato come id valore dell'elemento HTML di cui è stato eseguito il rendering. Questo non è necessariamente il caso perché in determinate circostanze un singolo controllo con un singolo ID valore può essere visualizzato più volte nel markup sottoposto a rendering. Si consideri un controllo GridView che include un oggetto TemplateField con un controllo Web Label con un ID valore .ProductName Quando GridView è associato all'origine dati in fase di esecuzione, questa etichetta viene ripetuta una volta per ogni riga gridView. Ogni etichetta sottoposta a rendering richiede un valore univoco id .

Per gestire questi scenari, ASP.NET consente di denominare determinati controlli come contenitori di denominazione. Un contenitore di denominazione funge da nuovo ID spazio dei nomi. Tutti i controlli server visualizzati all'interno del contenitore di denominazione hanno un valore di cui è stato eseguito il rendering id con ID il prefisso del controllo contenitore di denominazione. Ad esempio, le GridView classi e GridViewRow sono entrambi contenitori di denominazione. Di conseguenza, a un controllo Label definito in un oggetto GridView TemplateField con IDProductName viene assegnato un valore di cui è stato id eseguito il rendering.GridViewID_GridViewRowID_ProductName Poiché GridViewRowID è univoco per ogni riga gridView, i valori risultanti id sono univoci.

Nota

L'interfacciaINamingContainer viene usata per indicare che un determinato controllo server ASP.NET deve funzionare come contenitore di denominazione. L'interfaccia INamingContainer non specifica alcun metodo che il controllo server deve implementare, ma viene usato come marcatore. Nella generazione del markup sottoposto a rendering, se un controllo implementa questa interfaccia, il motore di ASP.NET antepone ID automaticamente il relativo valore ai valori dell'attributo sottoposto a rendering id dei discendenti. Questo processo viene descritto in modo più dettagliato nel passaggio 2.

La denominazione dei contenitori non solo modifica il valore dell'attributo di cui è stato id eseguito il rendering, ma influisce anche sulla modalità di riferimento del controllo a livello di codice dalla classe code-behind della pagina ASP.NET. Il FindControl("controlID") metodo viene comunemente usato per fare riferimento a un controllo Web a livello di codice. Tuttavia, FindControl non penetra attraverso la denominazione dei contenitori. Di conseguenza, non è possibile usare direttamente il Page.FindControl metodo per fare riferimento ai controlli all'interno di un controllo GridView o di un altro contenitore di denominazione.

Come si può supporre, le pagine master e ContentPlaceHolders vengono entrambe implementate come contenitori di denominazione. In questa esercitazione viene illustrato come le pagine master influiscono sui valori degli elementi id HTML e sui modi per fare riferimento a livello di codice ai controlli Web all'interno di una pagina di contenuto usando FindControl.

Passaggio 1: Aggiunta di una nuova pagina ASP.NET

Per illustrare i concetti illustrati in questa esercitazione, aggiungere una nuova pagina ASP.NET al sito Web. Creare una nuova pagina di contenuto denominata IDIssues.aspx nella cartella radice, associandola alla Site.master pagina master.

Aggiungere la pagina contenuto IDIssues.aspx alla cartella radice

Figura 01: Aggiungere la pagina IDIssues.aspx contenuto alla cartella radice

Visual Studio crea automaticamente un controllo Contenuto per ognuno dei quattro ContentPlaceHolders della pagina master. Come indicato nell'esercitazione Su più contentPlaceHolders e contenuto predefinito , se un controllo Contenuto non è presente, viene generato il contenuto ContentPlaceHolder predefinito della pagina master. Poiché i QuickLoginUI e LeftColumnContent ContentPlaceHolders contengono markup predefiniti adatti per questa pagina, procedere e rimuovere i controlli Contenuto corrispondenti da IDIssues.aspx. A questo punto, il markup dichiarativo della pagina del contenuto dovrebbe essere simile al seguente:

<%@ 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>

Nell'esercitazione Specifica di titolo, meta tag e altre intestazioni HTML nell'esercitazione pagina master è stata creata una classe di pagina di base personalizzata (BasePage) che configura automaticamente il titolo della pagina se non è impostata in modo esplicito. Affinché la IDIssues.aspx pagina usi questa funzionalità, la classe code-behind della pagina deve derivare dalla BasePage classe ( anziché System.Web.UI.Page). Modificare la definizione della classe code-behind in modo che abbia un aspetto simile al seguente:

Partial Class IDIssues
 Inherits BasePage

End Class

Aggiornare infine il Web.sitemap file in modo da includere una voce per questa nuova lezione. Aggiungere un <siteMapNode> elemento e impostarne title gli attributi e url rispettivamente su "Problemi di denominazione dell'ID di controllo" e ~/IDIssues.aspx. Dopo aver apportato questa aggiunta, il Web.sitemap markup del file dovrebbe essere simile al seguente:

<?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>

Come illustrato nella figura 2, la nuova voce della mappa del sito in Web.sitemap viene immediatamente riflessa nella sezione Lezioni nella colonna a sinistra.

La sezione Lezioni include ora un collegamento a

Figura 02: La sezione Lezioni include ora un collegamento a "Problemi di denominazione degli ID di controllo"

Passaggio 2: Esame delle modifiche sottoposte a renderingID

Per comprendere meglio le modifiche apportate dal motore di ASP.NET ai valori visualizzati id dei controlli server, aggiungere alcuni controlli Web alla IDIssues.aspx pagina e quindi visualizzare il markup sottoposto a rendering inviato al browser. In particolare, digitare il testo "Please enter your age:" seguito da un controllo Web TextBox. Più in basso nella pagina aggiungere un controllo Web Button e un controllo Web Label. Impostare le proprietà e Columns di ID TextBox rispettivamente su Age e 3. Impostare le proprietà e ID del Text pulsante su "Invia" e SubmitButton. Cancellare la proprietà dell'etichetta Text e impostarla ID su Results.

A questo punto il markup dichiarativo del controllo contenuto dovrebbe essere simile al seguente:

<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>

La figura 3 mostra la pagina visualizzata tramite la finestra di progettazione di Visual Studio.

La pagina include tre controlli Web: un controllo TextBox, un pulsante e un'etichetta

Figura 03: La pagina include tre controlli Web: un controllo TextBox, un pulsante e un'etichetta (fare clic per visualizzare l'immagine a dimensione intera)

Visitare la pagina tramite un browser e quindi visualizzare l'origine HTML. Come illustrato nel markup seguente, i id valori degli elementi HTML per i controlli Web TextBox, Button e Label sono una combinazione dei valori dei ID controlli Web e dei ID valori dei contenitori di denominazione nella pagina.

<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>

Come indicato in precedenza in questa esercitazione, sia la pagina master che il relativo ContentPlaceHolders fungono da contenitori di denominazione. Di conseguenza, entrambi contribuiscono ai valori sottoposti a ID rendering dei relativi controlli annidati. Prendere l'attributo di id TextBox, ad esempio : ctl00_MainContent_Age. Tenere presente che il valore del ID controllo TextBox è .Age È preceduto dal valore del ID controllo ContentPlaceHolder, MainContent. Inoltre, questo valore è preceduto dal valore della ID pagina master, ctl00. L'effetto netto è un id valore di ID attributo costituito dai valori della pagina master, del controllo ContentPlaceHolder e del controllo TextBox stesso.

La figura 4 illustra questo comportamento. Per determinare il rendering id di Age TextBox, iniziare con il ID valore del controllo TextBox, Age. Successivamente, è possibile modificare la gerarchia dei controlli. In ogni contenitore di denominazione (questi nodi con un colore pesca), anteporre al rendering corrente il rendering id con il contenitore di iddenominazione .

Gli attributi ID di cui è stato eseguito il rendering sono basati sui valori ID dei contenitori di denominazione

Figura 04: Gli attributi sottoposti id a rendering sono basati sui ID valori dei contenitori di denominazione

Nota

Come illustrato, la ctl00 parte dell'attributo di cui è stato id eseguito il rendering costituisce il ID valore della pagina master, ma potrebbe essere necessario chiedersi come è venuto questo ID valore. Non è stato specificato in nessun punto della pagina master o contenuto. La maggior parte dei controlli server in una pagina ASP.NET viene aggiunta in modo esplicito tramite il markup dichiarativo della pagina. Il MainContent controllo ContentPlaceHolder è stato specificato in modo esplicito nel markup di Site.master. Il Age markup di TextBox è stato definito IDIssues.aspx. È possibile specificare i ID valori per questi tipi di controlli tramite il Finestra Proprietà o dalla sintassi dichiarativa. Altri controlli, come la pagina master stessa, non sono definiti nel markup dichiarativo. Di conseguenza, i valori ID devono essere generati automaticamente. Il motore ASP.NET imposta i ID valori in fase di esecuzione per i controlli i cui ID non sono stati impostati in modo esplicito. Usa il modello ctlXXdi denominazione , dove XX è un valore intero che aumenta in sequenza.

Poiché la pagina master stessa funge da contenitore di denominazione, anche i controlli Web definiti nella pagina master hanno modificato i valori degli attributi di cui è stato id eseguito il rendering. Ad esempio, l'etichetta DisplayDate aggiunta alla pagina master nell'esercitazione Creazione di un layout Site-Wide con pagine master include il markup sottoposto a rendering seguente:

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

Si noti che l'attributo id include sia il valore della ID pagina master (ctl00) che il ID valore del controllo Web Etichetta (DateDisplay).

Passaggio 3: Fare riferimento a controlli Web a livello di codice tramiteFindControl

Ogni controllo server ASP.NET include un FindControl("controlID") metodo che cerca nei discendenti del controllo un controllo denominato controlID. Se viene trovato un controllo di questo tipo, viene restituito; se non viene trovato alcun controllo corrispondente, FindControl restituisce Nothing.

FindControl è utile negli scenari in cui è necessario accedere a un controllo, ma non si dispone di un riferimento diretto. Quando si usano controlli Web dati come GridView, ad esempio, i controlli all'interno dei campi di GridView vengono definiti una volta nella sintassi dichiarativa, ma in fase di esecuzione viene creata un'istanza del controllo per ogni riga gridView. Di conseguenza, i controlli generati in fase di esecuzione esistono, ma non è disponibile un riferimento diretto dalla classe code-behind. Di conseguenza, è necessario usare per lavorare FindControl a livello di codice con un controllo specifico all'interno dei campi di GridView. Per altre informazioni sull'uso FindControl di per accedere ai controlli all'interno dei modelli di un controllo Web dati, vedere Formattazione personalizzata basata su dati. Questo stesso scenario si verifica quando si aggiungono dinamicamente controlli Web a un Web Form, un argomento descritto in Creazione di interfacce utente di immissione dati dinamica.

Per illustrare l'uso del FindControl metodo per cercare i controlli all'interno di una pagina del contenuto, creare un gestore eventi per l'evento dell'oggetto SubmitButtonClick . Nel gestore eventi aggiungere il codice seguente, che fa riferimento a Age textBox e Results Label a livello di codice usando il FindControl metodo e quindi visualizza un messaggio in in Results base all'input dell'utente.

Nota

Naturalmente, non è necessario usare FindControl per fare riferimento ai controlli Label e TextBox per questo esempio. È possibile farvi riferimento direttamente tramite i valori ID delle proprietà. FindControl Uso qui per illustrare cosa accade quando si usa FindControl da una pagina di contenuto.

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

Mentre la sintassi usata per chiamare il FindControl metodo è leggermente diversa nelle prime due righe di SubmitButton_Click, sono semanticamente equivalenti. Tenere presente che tutti i controlli server ASP.NET includono un FindControl metodo . Ciò include la Page classe da cui tutte le classi code-behind ASP.NET devono derivare. Pertanto, la chiamata equivale a chiamare FindControl("controlID")Page.FindControl("controlID"), presupponendo che il metodo non sia stato sottoposto a override FindControl nella classe code-behind o in una classe di base personalizzata.

Dopo aver immesso questo codice, visitare la IDIssues.aspx pagina tramite un browser, immettere l'età e fare clic sul pulsante "Invia". Quando si fa clic sul pulsante "Invia" viene generato un NullReferenceException oggetto (vedere la figura 5).

Viene generata un'eccezione NullReferenceException

Figura 05: Viene generato un oggetto NullReferenceException (fare clic per visualizzare l'immagine a dimensione intera)

Se si imposta un punto di interruzione nel SubmitButton_Click gestore eventi, si noterà che entrambe le chiamate per FindControl restituire Nothing. Viene NullReferenceException generato quando si tenta di accedere alla Age proprietà di Text TextBox.

Il problema è che Control.FindControl cerca solo i discendenti di Control che si trovano nello stesso contenitore di denominazione. Poiché la pagina master costituisce un nuovo contenitore di denominazione, una chiamata a Page.FindControl("controlID") non permea mai l'oggetto ctl00pagina master . Fare riferimento alla figura 4 per visualizzare la gerarchia dei controlli, che mostra l'oggetto Page come elemento padre dell'oggetto ctl00pagina master. Di conseguenza, Label Results e Age TextBox non vengono trovati e ResultsLabelAgeTextBox vengono assegnati valori di Nothing.

Esistono due soluzioni alternative per questa sfida: è possibile eseguire il drill-down, un contenitore di denominazione alla volta, al controllo appropriato; oppure è possibile creare un metodo personalizzato FindControl che permea i contenitori di denominazione. Esaminiamo ognuna di queste opzioni.

Drill-in del contenitore di denominazione appropriato

Per fare FindControl riferimento Results a Label o Age TextBox, è necessario chiamare FindControl da un controllo predecessore nello stesso contenitore di denominazione. Come illustrato nella figura 4, il MainContent controllo ContentPlaceHolder è l'unico predecessore di Results o Age che si trova all'interno dello stesso contenitore di denominazione. In altre parole, la chiamata del FindControlMainContent metodo dal controllo , come illustrato nel frammento di codice seguente, restituisce correttamente un riferimento ai Results controlli o Age .

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

Tuttavia, non è possibile usare contentPlaceHolder MainContent dalla classe code-behind della pagina contenuto usando la sintassi precedente perché ContentPlaceHolder è definito nella pagina master. È invece necessario usare FindControl per ottenere un riferimento a MainContent. Sostituire il codice nel SubmitButton_Click gestore eventi con le modifiche seguenti:

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

Se si visita la pagina tramite un browser, immettere l'età e fare clic sul pulsante "Invia", viene generato un oggetto NullReferenceException . Se si imposta un punto di interruzione nel SubmitButton_Click gestore eventi, si noterà che questa eccezione si verifica quando si tenta di chiamare il MainContent metodo dell'oggetto FindControl . L'oggetto MainContent è uguale a Nothing perché il FindControl metodo non è in grado di individuare un oggetto denominato "MainContent". Il motivo sottostante è lo stesso dei Results controlli Label e Age TextBox: FindControl avvia la ricerca dall'inizio della gerarchia dei controlli e non penetra nei contenitori di denominazione, ma MainContent ContentPlaceHolder si trova all'interno della pagina master, ovvero un contenitore di denominazione.

Prima di poter usare FindControl per ottenere un riferimento a MainContent, è necessario prima di tutto un riferimento al controllo pagina master. Dopo aver ottenuto un riferimento alla pagina master, è possibile ottenere un riferimento a MainContent ContentPlaceHolder tramite FindControl e, da qui, i riferimenti a Results Label e Age TextBox (anche in questo FindControlcaso tramite ). Ma come si ottiene un riferimento alla pagina master? Esaminando gli id attributi nel markup di cui è stato eseguito il rendering, è evidente che il valore della ID pagina master è ctl00. È quindi possibile usare Page.FindControl("ctl00") per ottenere un riferimento alla pagina master, quindi usare tale oggetto per ottenere un riferimento a MainContente così via. Il frammento di codice seguente illustra questa logica:

'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)

Anche se questo codice funzionerà certamente, presuppone che la pagina master generata ID automaticamente sia ctl00sempre . Non è mai consigliabile fare ipotesi sui valori generati automaticamente.

Fortunatamente, un riferimento alla pagina master è accessibile tramite la Page proprietà della Master classe. Pertanto, invece di dover usare FindControl("ctl00") per ottenere un riferimento della pagina master per accedere MainContent a ContentPlaceHolder, è invece possibile usare Page.Master.FindControl("MainContent"). Aggiornare il SubmitButton_Click gestore eventi con il codice seguente:

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

Questa volta, visitando la pagina tramite un browser, immettendo l'età e facendo clic sul pulsante "Invia" viene visualizzato il messaggio nell'etichetta Results , come previsto.

L'età dell'utente viene visualizzata nell'etichetta

Figura 06: l'età dell'utente viene visualizzata nell'etichetta (fare clic per visualizzare l'immagine a dimensione intera)

Ricerca ricorsiva nei contenitori

Il motivo per cui l'esempio di codice precedente ha fatto riferimento al MainContent controllo ContentPlaceHolder dalla pagina master e quindi ai Results controlli Label e Age TextBox da MainContentè dovuto al fatto che il Control.FindControl metodo esegue la ricerca solo all'interno del contenitore di denominazione di Control. La presenza FindControl di un contenitore di denominazione ha senso nella maggior parte degli scenari perché due controlli in due contenitori di denominazione diversi possono avere gli stessi ID valori. Si consideri il caso di un controllo GridView che definisce un controllo Web Etichetta denominato ProductName all'interno di uno dei relativi Campi Template. Quando i dati sono associati a GridView in fase di esecuzione, viene creata un'etichetta ProductName per ogni riga gridView. Se FindControl è stata eseguita una ricerca in tutti i contenitori di denominazione e si è chiamato Page.FindControl("ProductName"), quale istanza di Label deve FindControl restituire? Etichetta ProductName nella prima riga gridView? Quello nell'ultima riga?

Pertanto, la Control.FindControl ricerca del contenitore di denominazione di Control ha senso nella maggior parte dei casi. Esistono tuttavia altri casi, ad esempio quelli che ci si trovano di fronte, in cui è presente un oggetto univoco ID in tutti i contenitori di denominazione e si vuole evitare di dover fare riferimento meticolosamente a ogni contenitore di denominazione nella gerarchia di controllo per accedere a un controllo. Anche la presenza di una FindControl variante che esegue ricerche ricorsive in tutti i contenitori di denominazione ha senso. Sfortunatamente, .NET Framework non include tale metodo.

La buona notizia è che è possibile creare un metodo personalizzato FindControl che esegue ricerche ricorsive in tutti i contenitori di denominazione. In realtà, usando i metodi di estensione è possibile aggiungere FindControlRecursive un metodo alla Control classe per accompagnare il metodo esistente FindControl .

Nota

I metodi di estensione sono una funzionalità nuova di C# 3.0 e Visual Basic 9, che sono i linguaggi forniti con .NET Framework versione 3.5 e Visual Studio 2008. In breve, i metodi di estensione consentono a uno sviluppatore di creare un nuovo metodo per un tipo di classe esistente tramite una sintassi speciale. Per altre informazioni su questa funzionalità utile, vedere l'articolo Estendere la funzionalità del tipo di base con i metodi di estensione.

Per creare il metodo di estensione, aggiungere un nuovo file alla App_Code cartella denominata PageExtensionMethods.vb. Aggiungere un metodo di estensione denominato FindControlRecursive che accetta come parametro di input denominato StringcontrolID. Affinché i metodi di estensione funzionino correttamente, è fondamentale che la classe sia contrassegnata come e Module che i metodi di estensione siano preceduti dall'attributo <Extension()> . Inoltre, tutti i metodi di estensione devono accettare come primo parametro un oggetto del tipo a cui si applica il metodo di estensione.

Aggiungere il codice seguente al PageExtensionMethods.vb file per definire questo Module e il FindControlRecursive metodo di estensione:

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

Con questo codice sul posto, tornare alla IDIssues.aspx classe code-behind della pagina e impostare come commento le chiamate al metodo corrente FindControl . Sostituirli con le chiamate a Page.FindControlRecursive("controlID"). I metodi di estensione vengono visualizzati direttamente negli elenchi a discesa IntelliSense. Come illustrato nella figura 7, quando si digita Page e si raggiunge il punto, il FindControlRecursive metodo viene incluso nell'elenco a discesa IntelliSense insieme agli altri Control metodi di classe.

I metodi di estensione sono inclusi negli elenchi a discesa IntelliSense

Figura 07: I metodi di estensione sono inclusi nella Drop-Downs in IntelliSense (fare clic per visualizzare l'immagine a dimensione intera)

Immettere il codice seguente nel SubmitButton_Click gestore eventi e quindi testarlo visitando la pagina, immettendo l'età e facendo clic sul pulsante "Invia". Come illustrato di nuovo nella figura 6, l'output risultante sarà il messaggio "Hai anni!"

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

Nota

Poiché i metodi di estensione non hanno familiarità con C# 3.0 e Visual Basic 9, se si usa Visual Studio 2005 non è possibile usare metodi di estensione. Sarà invece necessario implementare il FindControlRecursive metodo in una classe helper. Rick Strahl ha un esempio del suo post di blog , ASP.NET Maser Pages e FindControl.

Passaggio 4: Uso del valore dell'attributo correttoidnello script Client-Side

Come indicato nell'introduzione di questa esercitazione, l'attributo sottoposto a rendering id di un controllo Web viene spesso usato nello script sul lato client per fare riferimento a un particolare elemento HTML a livello di codice. Ad esempio, il codice JavaScript seguente fa riferimento a un elemento HTML in id base al relativo e quindi visualizza il relativo valore in una finestra di messaggio modale:

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

Tenere presente che nelle pagine ASP.NET che non includono un contenitore di denominazione, l'attributo dell'elemento id HTML di cui è stato eseguito il rendering è identico al valore della proprietà del ID controllo Web. Per questo motivo, è possibile impostare come hardcoded nei valori degli attributi nel id codice JavaScript. Ovvero, se si sa di voler accedere al Age controllo Web TextBox tramite script sul lato client, eseguire questa operazione tramite una chiamata a document.getElementById("Age").

Il problema con questo approccio è che quando si usano pagine master (o altri controlli contenitore di denominazione), il codice HTML id sottoposto a rendering non è sinonimo della proprietà del ID controllo Web. La prima inclinazione può essere quella di visitare la pagina tramite un browser e visualizzare l'origine per determinare l'attributo effettivo id . Dopo aver appreso il valore di cui è stato eseguito id il rendering, è possibile incollarlo nella chiamata a per getElementById accedere all'elemento HTML che è necessario usare tramite lo script lato client. Questo approccio è inferiore all'ideale perché alcune modifiche alla gerarchia di controllo della pagina o alle modifiche alle ID proprietà dei controlli di denominazione modificheranno l'attributo risultante id , interrompendo così il codice JavaScript.

La buona notizia è che il valore dell'attributo di cui viene eseguito il id rendering è accessibile nel codice lato server tramite la proprietà del ClientIDcontrollo Web. È consigliabile usare questa proprietà per determinare il valore dell'attributo id usato nello script lato client. Ad esempio, per aggiungere una funzione JavaScript alla pagina che, quando viene chiamata, visualizza il valore di TextBox in una finestra di Age messaggio modale, aggiungere il codice seguente al Page_Load gestore eventi:

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)

Il codice precedente inserisce il valore della Age proprietà textBox ClientID nella chiamata JavaScript a getElementById. Se visiti questa pagina tramite un browser e visualizzi l'origine HTML, troverai il codice JavaScript seguente:

<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>

Si noti che il valore dell'attributo corretto id , ctl00_MainContent_Age, viene visualizzato all'interno della chiamata a getElementById. Poiché questo valore viene calcolato in fase di esecuzione, funziona indipendentemente dalle modifiche successive apportate alla gerarchia del controllo pagina.

Nota

Questo esempio JavaScript mostra semplicemente come aggiungere una funzione JavaScript che fa riferimento correttamente all'elemento HTML di cui viene eseguito il rendering da un controllo server. Per usare questa funzione, è necessario creare codice JavaScript aggiuntivo per chiamare la funzione quando il documento viene caricato o quando viene eseguita una determinata azione utente. Per altre informazioni su questi argomenti e correlati, vedere Uso dello script di Client-Side.

Riepilogo

Alcuni controlli server ASP.NET fungono da contenitori di denominazione, che influiscono sui valori degli attributi di rendering id dei relativi controlli discendenti e sull'ambito FindControl dei controlli canvassed dal metodo . Per quanto riguarda le pagine master, sia la pagina master stessa che i relativi controlli ContentPlaceHolder sono contenitori di denominazione. Di conseguenza, è necessario inserire un po'più lavoro per fare riferimento ai controlli a livello di codice all'interno della pagina del contenuto usando FindControl. In questa esercitazione sono state esaminate due tecniche: l'analisi del controllo ContentPlaceHolder e la chiamata FindControl al relativo metodo e il rolling dell'implementazione che FindControl esegue ricerche ricorsive in tutti i contenitori di denominazione.

Oltre ai problemi sul lato server, i contenitori di denominazione introducono per quanto riguarda i controlli Web di riferimento, sono presenti anche problemi sul lato client. In assenza di contenitori di denominazione, il valore della proprietà del controllo Web e il valore dell'attributo ID di cui è stato eseguito id il rendering sono uno nello stesso. Ma con l'aggiunta del contenitore di denominazione, l'attributo sottoposto a rendering id include sia i ID valori del controllo Web che i contenitori di denominazione nella gerarchia di controllo. Questi problemi di denominazione sono un problema diverso da quando si usa la proprietà del controllo Web per determinare il valore dell'attributo ClientID di cui è stato eseguito il rendering id nello script lato client.

Programmazione felice!

Altre informazioni

Per altre informazioni sugli argomenti illustrati in questa esercitazione, vedere le risorse seguenti:

Informazioni sull'autore

Scott Mitchell, autore di più libri ASP/ASP.NET e fondatore di 4GuysFromRolla.com, ha lavorato con le tecnologie Web Microsoft dal 1998. Scott lavora come consulente indipendente, allenatore e scrittore. Il suo ultimo libro è Sams Teach Yourself ASP.NET 3,5 in 24 ore. Scott può essere raggiunto all'indirizzo mitchell@4GuysFromRolla.com o tramite il suo blog all'indirizzo http://ScottOnWriting.NET.

Grazie speciali

Questa serie di esercitazioni è stata esaminata da molti revisori utili. I revisori lead per questa esercitazione erano Zack Jones e Suchi Barnerjee. Interessati a esaminare i prossimi articoli MSDN? In tal caso, lasciami una riga in mitchell@4GuysFromRolla.com.