Condividi tramite


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

di Scott Mitchell

Scarica 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 con 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 sottoposto a rendering. Questo non è necessariamente il caso perché in determinate circostanze un singolo controllo con un singolo ID valore può apparire 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 innominare 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 il relativo valore di cui è stato ID eseguito il rendering id con 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 ID ProductName viene assegnato un valore di cui è stato id eseguito il rendering.GridViewID_GridViewRowID_ProductName Poiché GridViewRowID è univoco per ogni riga gridView, i valori risultanti sono univoci id .

Nota

L'interfaccia INamingContainer viene usata per indicare che un particolare 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 automaticamente il ID valore ai valori degli attributi visualizzati id dai discendenti. Questo processo viene illustrato in modo più dettagliato nel passaggio 2.

I contenitori di denominazione non solo modificano il valore dell'attributo sottoposto a rendering id , ma influiscono 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 i contenitori di denominazione. Di conseguenza, non è possibile usare direttamente il Page.FindControl metodo per fare riferimento ai controlli all'interno di un controllo GridView o in 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 esaminato il modo in cui 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 del 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, il contenuto ContentPlaceHolder predefinito della pagina master viene generato. 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 del titolo, dei meta tag e di 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 "Control ID Naming Issues" 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 aIDrendering

Per comprendere meglio le modifiche apportate dal motore di ASP.NET ai valori di cui è stato eseguito id il rendering 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 Etichetta. Impostare rispettivamente le proprietà Age e e Columns di ID TextBox su 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 quando viene 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 ID valori dei 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 i relativi ContentPlaceHolders fungono da contenitori di denominazione. Di conseguenza, entrambi contribuiscono ai valori visualizzati dei relativi controlli annidati ID . Prendere l'attributo di id TextBox, ad esempio : ctl00_MainContent_Age. Tenere presente che il valore del ID controllo TextBox era 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 attributo costituito dai ID valori della pagina master, del controllo ContentPlaceHolder e del controllo TextBox stesso.

La figura 4 illustra questo comportamento. Per determinare il rendering id del Age controllo 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 id al rendering corrente il rendering con il contenitore di denominazione .id

Gli attributi ID sottoposti a rendering sono basati sui valori ID dei contenitori di denominazione

Figura 04: Gli attributi sottoposti a id 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 ci si potrebbe chiedere come è venuto questo ID valore. Non è stato specificato in nessun punto della pagina master o del 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 relativi ID valori 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 a livello di sito 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: Riferimento a controlli Web a livello di codice tramiteFindControl

Ogni ASP.NET controllo server 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 Age a textBox e Results Label a livello di codice usando il FindControl metodo e quindi visualizza un messaggio in Results in 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à. Uso FindControl 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 . 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 non sia stato eseguito l'override del FindControl metodo 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 (NullReferenceExceptionfare 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 padre dell'oggetto ctl00pagina master. Pertanto, l'etichetta e il Results controllo TextBox non vengono trovati e ResultsLabel AgeTextBox vengono assegnati valori di .NothingAge

Esistono due soluzioni alternative a 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 o Age che si trova nello stesso contenitore di Results denominazione. In altre parole, la chiamata del FindControl MainContent 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 del 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 innanzitutto 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 all'etichetta Results e Age al controllo TextBox (anche in questo caso tramite ).FindControl Ma come si ottiene un riferimento alla pagina master? Esaminando gli id attributi nel markup sottoposto a rendering, è evidente che il valore della ID pagina master è ctl00. Pertanto, è 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 automaticamente ID 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 alla 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 tramite contenitori di denominazione

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 cerca solo all'interno del contenitore di denominazione di Control. La FindControl presenza all'interno del 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 Label 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 viene eseguita la ricerca in tutti i contenitori di denominazione e viene chiamato Page.FindControl("ProductName"), quale istanza di Label deve restituire FindControl ? 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 quello che ci si trova 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. Infatti, usando i metodi di estensione è possibile modificare un FindControlRecursive metodo per la classe per accompagnarne il Control metodo esistenteFindControl.

Nota

I metodi di estensione sono una novità di C# 3.0 e Visual Basic 9, ovvero 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 Estensione della 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 String input denominato controlID. 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"). Ciò che è chiaro sui metodi di estensione è che vengono visualizzati direttamente negli elenchi a discesa IntelliSense. Come illustrato nella figura 7, quando si digita Page e quindi 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 negli elenchi a discesa 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 nella figura 6, l'output risultante sarà il messaggio "You are age years!"

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 nel suo post di blog, ASP.NET Maser Pages e FindControl.

Passaggio 4: Uso del valore dell'attributo correttoidnello script lato client

Come indicato nell'introduzione di questa esercitazione, l'attributo sottoposto a rendering id di un controllo Web viene spesso usato nello script 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 base al relativo id 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 sottoposto a rendering è identico al valore della proprietà del ID controllo Web. Per questo motivo, si tenta di impostare come hardcoded nei valori degli attributi nel id codice JavaScript. Ciò significa che, 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 accedere all'elemento getElementById HTML che è necessario usare tramite lo script sul 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 ClientID controllo Web. È consigliabile usare questa proprietà per determinare il valore dell'attributo id usato nello script sul lato client. Ad esempio, per aggiungere una funzione JavaScript alla pagina che, quando viene chiamata, visualizza il valore di Age TextBox in una finestra di 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à di ClientID TextBox nella chiamata JavaScript a getElementById. Se si visita questa pagina tramite un browser e si visualizza l'origine HTML, si troverà 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 alla gerarchia dei controlli pagina.

Nota

Questo esempio JavaScript mostra semplicemente come aggiungere una funzione JavaScript che fa riferimento correttamente all'elemento HTML sottoposto a 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 un'azione utente specifica. Per altre informazioni su questi argomenti e correlati, vedere Uso dello script sul lato client.

Riepilogo

Alcuni controlli server ASP.NET fungono da contenitori di denominazione, che influiscono sui valori degli attributi sottoposti a rendering id dei relativi controlli discendenti, nonché sull'ambito dei controlli di cui è stato eseguito il FindControl canvas 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 eseguire un po' di lavoro per fare riferimento a controlli a livello di codice all'interno della pagina del contenuto usando FindControl. In questa esercitazione sono state esaminate due tecniche: drill-in the ContentPlaceHolder controllo e chiamata del relativo FindControl metodo e rollover dell'implementazione personalizzata FindControl che esegue ricerche ricorsive in tutti i contenitori di denominazione.

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

Buon programmatori!

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, formatore 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 speciale a

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