Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
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 valore di ProductName. Quando il GridView è associato all'origine dati durante l'esecuzione, questa Label viene ripetuta una volta per ogni riga del GridView. Ogni etichetta sottoposta a rendering richiede un valore univoco id .
Per gestire questi scenari, ASP.NET consente di designare alcuni controlli come contenitori di nomi. Un contenitore di denominazione funge da nuovo ID spazio dei nomi. Tutti i controlli server visualizzati all'interno del contenitore di denominazione hanno il loro valore reso con un prefisso del controllo del contenitore di denominazione. Ad esempio, le GridView e GridViewRow classi sono entrambe contenitori di denominazione. Di conseguenza, a un controllo Label definito in un TemplateField del GridView con IDProductName viene assegnato un valore di rendering idGridViewID_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 valore di ID ai valori dell'attributo id dei suoi discendenti visualizzati. Questo processo viene illustrato in modo più dettagliato nel passaggio 2.
La denominazione dei contenitori non solo modifica il valore dell'attributo reso id, ma influisce anche sul modo in cui il controllo può essere fatto riferimento a livello di codice nella 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 forse avrai intuito, le pagine master e i 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.
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 Più ContentPlaceHolders e Contenuto predefinito, se un controllo Contenuto non è presente, viene generato il contenuto predefinito del ContentPlaceHolder della pagina master. Poiché i QuickLoginUI e LeftColumnContent ContentPlaceHolders contengono markup predefiniti adatti per questa pagina, rimuovi i corrispondenti controlli "Contenuto" 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>
Nella lezione Specifica del titolo, dei meta tag e di altre intestazioni HTML nella Pagina Master è stata creata una classe personalizzata di pagina di base (BasePage) che configura automaticamente il titolo della pagina se non è impostato 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
Infine, aggiornare il file Web.sitemap per includere un'entrata per questa nuova lezione. Aggiungere un elemento <siteMapNode> e impostare rispettivamente i suoi attributi title e url 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 è immediatamente riflessa nella sezione Lezioni nella colonna sinistra.
Figura 02: La sezione Lezioni include ora un collegamento a "Problemi di denominazione degli ID di controllo"
Passaggio 2: Esaminare le modifiche reseID
Per comprendere meglio le modifiche che il motore ASP.NET apporta ai valori dei controlli server durante il rendering, aggiungiamo alcuni controlli Web alla IDIssues.aspx pagina e quindi visualizziamo il markup renderizzato inviato al browser. In particolare, digitare il testo "Please enter your age:" seguito da un controllo di tipo TextBox Web. Più in basso nella pagina, aggiungere un controllo Web Button e un controllo Web Label. Impostare le proprietà di TextBox, rispettivamente ID e Columns, su Age e 3. Impostare le proprietà Text e ID del pulsante su "Invia" e SubmitButton. Cancella la proprietà Text dell'etichetta e imposta la sua 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.
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 . Prendi, per esempio, l'attributo id di TextBox: ctl00_MainContent_Age. Tenere presente che il valore del ID controllo TextBox era Age. È prefissato con il valore del suo controllo ContentPlaceHolder ID, MainContent. Inoltre, a questo valore viene anteposto il valore della pagina master ID, ctl00. L'effetto netto è un id valore di attributo costituito dai ID valori della pagina principale, del controllo ContentPlaceHolder e del controllo TextBox stesso.
La figura 4 illustra questo comportamento. Per determinare il rendering id del controllo Age TextBox, iniziare con il valore ID del controllo TextBox, Age. Successivamente, procedi verso l'alto nella gerarchia dei controlli. In ogni contenitore di denominazione (quei nodi con un colore pesca), anteponi il prefisso id al id reso correntemente.
Figura 04: Gli attributi resi si basano 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 abbiamo specificato alcunché nella nostra pagina principale 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 controllo MainContent ContentPlaceHolder è stato specificato in modo esplicito nel markup di Site.master; il Age TextBox è stato definito nel markup di 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 che abbiamo aggiunto alla pagina master nell'esercitazione Creazione di un layout a livello di sito con pagine master include il seguente markup sottoposto a rendering:
<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 controllo server ASP.NET include un metodo FindControl("controlID") che cerca un controllo denominato controlID tra i discendenti del controllo. 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 FindControl a livello di codice per lavorare 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 metodo FindControl per cercare i controlli all'interno di una pagina di contenuti, creare un gestore eventi per l'evento Click di SubmitButton. Nel gestore eventi, aggiungere il codice seguente, che fa riferimento al Age TextBox e al Results Label in modo programmato usando il metodo FindControl 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 fare riferimento a essi direttamente tramite i valori delle loro proprietà ID. 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 . Questo include la Page classe, da cui tutte le classi code-behind ASP.NET devono derivare. Pertanto, chiamare FindControl("controlID") è equivalente a chiamare Page.FindControl("controlID"), presupponendo che non sia stato sovrascritto il metodo FindControl nella classe del codice sottostante o in una classe 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 (vedere la figura 5).
Figura 05: Viene sollevato un oggetto (NullReferenceExceptioncliccare per visualizzare l'immagine a grandezza intera)
Se si imposta un punto di interruzione nel SubmitButton_Click gestore eventi, si noterà che entrambe le chiamate a FindControl restituiscono Nothing. Viene NullReferenceException generato quando si tenta di accedere alla proprietà Text di Age 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 ctl00 pagina master. Pertanto, l'etichetta Results e il controllo TextBox Age non vengono trovati e a ResultsLabel e AgeTextBox vengono assegnati valori di Nothing.
Esistono due soluzioni alternative a questa sfida: possiamo approfondire, un contenitore di nomi alla volta, fino al controllo appropriato. Oppure, possiamo creare il nostro metodo personalizzato FindControl che attraversa i contenitori di nomi. Esaminiamo ognuna di queste opzioni.
Approfondimento nel contenitore di denominazione appropriato
Per fare riferimento alla Results Label o alla Age TextBox, è necessario chiamare FindControl da un controllo antenato nello stesso contenitore di denominazione. Come illustrato nella figura 4, il MainContent controllo ContentPlaceHolder è l'unico antenato di Results o Age che si trova nello stesso contenitore di denominazione. In altre parole, il metodo FindControl chiamato dal controllo MainContent, come illustrato nel frammento di codice seguente, restituisce correttamente un riferimento ai controlli Results o Age.
Dim ResultsLabel As Label = CType(MainContent.FindControl("Results"), Label)
Dim AgeTextBox As TextBox = CType(MainContent.FindControl("Age"), TextBox)
Tuttavia, non è possibile usare il ContentPlaceHolder MainContent dalla classe di code-behind della pagina dei contenuti usando la sintassi precedente, perché il 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 al MainContent ContentPlaceHolder tramite FindControl e, da qui, i riferimenti all'etichetta Results e al controllo TextBox Age (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 principale è 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 sarà sempre ctl00. 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.
Figura 06: l'età dell'utente viene visualizzata nell'etichetta (fare clic per visualizzare l'immagine a dimensione intera)
Ricerca ricorsiva attraverso 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 ha cercato in tutti i contenitori di denominazione e abbiamo chiamato Page.FindControl("ProductName"), quale istanza di Label dovrebbe restituire FindControl? L'etichetta ProductName nella riga della prima GridView? Quello nell'ultima riga?
Pertanto, avere la ricerca limitata al Control.FindControl 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, possiamo aggiungere un metodo FindControlRecursive alla classe Control per accompagnare il suo metodo FindControl esistente.
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 chiamato FindControlRecursive che prende in input un parametro String chiamato 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 incorporato, ritornare alla classe code-behind della pagina IDIssues.aspx e commentare le chiamate ai metodi correnti FindControl. Sostituirli con le chiamate a Page.FindControlRecursive("controlID"). Ciò che è interessante dei metodi di estensione è che vengono visualizzati direttamente nei menu a tendina di IntelliSense. Come illustrato nella figura 7, quando si digita Page e quindi si preme il tasto punto, il metodo FindControlRecursive viene incluso nell'elenco a discesa IntelliSense insieme agli altri metodi della classe Control.
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 "Hai [età] 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 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, è allettante inserire i valori degli attributi direttamente nel 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 conosciuto il valore di id a cui è stato eseguito il rendering, è possibile incollarlo nella chiamata a getElementById per accedere all'elemento HTML necessario tramite 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 rendering id è accessibile nel codice lato server tramite la proprietà ClientID del 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 ulteriori informazioni su questi e argomenti correlati, leggere "Lavorare con gli script lato client".
Riepilogo
Alcuni controlli server ASP.NET fungono da contenitori di denominazione, influenzando i valori degli attributi id dei loro controlli discendenti, nonché l'ambito dei controlli coperti dal metodo FindControl. 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: esaminare a fondo il controllo ContentPlaceHolder e chiamare il relativo metodo FindControl; e sviluppare un'implementazione personalizzata FindControl che ricerca in modo ricorsivo in tutti i contenitori di nomi.
Oltre ai problemi sul lato server che i contenitori di denominazione introducono riguardo al riferimento ai controlli Web, ci sono anche questioni sul lato client. In assenza di denominazione dei contenitori, il valore della proprietà del ID controllo Web e il valore dell'attributo reso id sono identici. Tuttavia, con l'aggiunta del contenitore di denominazione, l'attributo renderizzato id include sia i ID valori del controllo Web che i contenitori di denominazione nella genealogia della gerarchia di controllo. Questi problemi di denominazione non sono un problema fintanto che si usa la proprietà del ClientID controllo Web per determinare il valore dell'attributo id nel rendering nello script sul lato client.
Buon programmatori!
Altre informazioni
Per altre informazioni sugli argomenti illustrati in questa esercitazione, vedere le risorse seguenti:
-
Pagine master di ASP.NET e
FindControl - Creazione di interfacce utente di immissione dati dinamica
- Procedura: Fare riferimento al contenuto della pagina master ASP.NET
- Mater Pages: suggerimenti, trucchi e trappole
- Gestione degli script client-side
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 diversi revisori validi. I principali revisori per questo tutorial erano Zack Jones e Suchi Barnerjee. Si è interessati a esaminare i prossimi articoli MSDN? Se sì, scrivimi a mitchell@4GuysFromRolla.com.