Freigeben über


Benennung von Steuerelement-IDs auf Inhaltsseiten (VB)

von Scott Mitchell

PDF herunterladen

Veranschaulicht, wie ContentPlaceHolder-Steuerelemente als Namenscontainer dienen und daher das programmgesteuerte Arbeiten mit einem Steuerelement (über FindControl) erschweren. Untersucht dieses Problem und die Problemumgehungen. Erläutert außerdem, wie programmgesteuert auf den resultierenden ClientID-Wert zugegriffen wird.

Einführung

Alle ASP.NET-Serversteuerelemente enthalten eine ID -Eigenschaft, die das Steuerelement eindeutig identifiziert und das Mittel ist, mit dem programmgesteuert in der CodeBehind-Klasse auf das Steuerelement zugegriffen wird. Ebenso können die Elemente in einem HTML-Dokument ein id Attribut enthalten, das das Element eindeutig identifiziert. Diese id Werte werden häufig in clientseitigen Skripts verwendet, um programmgesteuert auf ein bestimmtes HTML-Element zu verweisen. Unter diesem Aspekt können Sie davon ausgehen, dass beim Rendern eines ASP.NET-Serversteuerelements in HTML dessen ID Wert als id Wert des gerenderten HTML-Elements verwendet wird. Dies ist nicht unbedingt der Fall, da unter bestimmten Umständen ein einzelnes Steuerelement mit einem einzelnen ID Wert mehrmals im gerenderten Markup angezeigt werden kann. Betrachten Sie ein GridView-Steuerelement, das ein TemplateField mit einem Label Web-Steuerelement mit dem ID Wert enthält ProductName. Wenn gridView zur Laufzeit an die Datenquelle gebunden ist, wird diese Bezeichnung einmal für jede GridView-Zeile wiederholt. Jede gerenderte Bezeichnung benötigt einen eindeutigen id Wert.

Um solche Szenarien zu verarbeiten, ermöglicht ASP.NET, dass bestimmte Steuerelemente als Benennungscontainer bezeichnet werden. Ein Namenscontainer dient als neuer ID Namespace. Alle Serversteuerelemente, die innerhalb des Namenscontainers angezeigt werden, haben ihren gerenderten id Wert mit dem Präfix des ID Namenscontainersteuerelements. Beispielsweise sind die GridView Klassen und GridViewRow beide Namenscontainer. Folglich erhält ein label-Steuerelement, das in einem GridView TemplateField mit IDProductName definiert ist, den gerenderten id Wert GridViewID_GridViewRowID_ProductName. Da GridViewRowID für jede GridView-Zeile eindeutig ist, sind die resultierenden id Werte eindeutig.

Hinweis

Die INamingContainer -Schnittstelle wird verwendet, um anzugeben, dass ein bestimmtes ASP.NET-Serversteuerelement als Namenscontainer fungieren soll. Die INamingContainer -Schnittstelle enthält keine Methoden, die das Serversteuerelement implementieren muss, sondern wird als Marker verwendet. Wenn beim Generieren des gerenderten Markups ein Steuerelement diese Schnittstelle implementiert, stellt die ASP.NET-Engine ihren ID Wert automatisch den gerenderten id Attributwerten ihrer Nachfolger voran. Dieser Prozess wird in Schritt 2 ausführlicher erläutert.

Benennungscontainer ändern nicht nur den gerenderten id Attributwert, sondern beeinflussen auch, wie programmgesteuert über die CodeBehind-Klasse der ASP.NET Seite auf das Steuerelement verwiesen wird. Die FindControl("controlID") -Methode wird häufig verwendet, um programmgesteuert auf ein Websteuerelement zu verweisen. FindControl dringt jedoch nicht durch Benennungscontainer ein. Daher können Sie die Page.FindControl -Methode nicht direkt verwenden, um auf Steuerelemente in einem GridView-Container oder einem anderen Namenscontainer zu verweisen.

Wie Sie vielleicht vermutet haben, werden master Seiten und ContentPlaceHolders beide als Namenscontainer implementiert. In diesem Tutorial wird untersucht, wie sich master Seiten auf HTML-Elementwerte id auswirken und wie Sie mithilfe von programmgesteuert auf Websteuerelemente innerhalb einer Inhaltsseite FindControlverweisen können.

Schritt 1: Hinzufügen einer neuen seite ASP.NET

Um die in diesem Tutorial erläuterten Konzepte zu veranschaulichen, fügen wir unserer Website eine neue ASP.NET-Seite hinzu. Erstellen Sie eine neue Inhaltsseite namens IDIssues.aspx im Stammordner, und binden Sie sie an die Site.master seite master.

Hinzufügen der Inhaltsseite IDIssues.aspx zum Stammordner

Abbildung 01: Hinzufügen der Inhaltsseite IDIssues.aspx zum Stammordner

Visual Studio erstellt automatisch ein Content-Steuerelement für jeden der vier ContentPlaceHolder der master Seite. Wie im Tutorial Multiple ContentPlaceHolders and Default Content erwähnt, wird stattdessen der master standardmäßige ContentPlaceHolder-Inhalt der Seite ausgegeben, wenn kein Inhaltssteuerelement vorhanden ist. Da contentPlaceHolders QuickLoginUI und LeftColumnContent geeignetes Standardmarkup für diese Seite enthalten, entfernen Sie die entsprechenden Content-Steuerelemente aus IDIssues.aspx. An diesem Punkt sollte das deklarative Markup der Inhaltsseite wie folgt aussehen:

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

Im Tutorial Angeben von Titel, Metatags und anderen HTML-Headern im Gestaltungsvorlagen-Tutorial haben wir eine benutzerdefinierte Basisseitenklasse (BasePage) erstellt, die den Titel der Seite automatisch konfiguriert, wenn er nicht explizit festgelegt ist. Damit die IDIssues.aspx Seite diese Funktionalität verwenden kann, muss die CodeBehind-Klasse der Seite von der BasePage -Klasse (anstelle von System.Web.UI.Page) abgeleitet werden. Ändern Sie die Definition der CodeBehind-Klasse so, dass sie wie folgt aussieht:

Partial Class IDIssues
 Inherits BasePage

End Class

Aktualisieren Sie schließlich die Web.sitemap Datei so, dass sie einen Eintrag für diese neue Lektion enthält. Fügen Sie ein Element hinzu <siteMapNode> , und legen Sie dessen title Attribute und url auf "Steuerungs-ID-Benennungsprobleme" bzw ~/IDIssues.aspx. fest. Nachdem Sie diese Ergänzung vornehmen, sollte das Markup Ihrer Web.sitemap Datei in etwa wie folgt aussehen:

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

Wie in Abbildung 2 dargestellt, wird der neue Websiteübersichtseintrag in Web.sitemap sofort im Abschnitt Lektionen in der linken Spalte angezeigt.

Der Abschnitt

Abbildung 02: Der Abschnitt "Lektionen" enthält jetzt einen Link zu "Probleme mit der Benennung von Steuer-ID".

Schritt 2: Untersuchen der gerendertenIDÄnderungen

Um besser zu verstehen, welche Änderungen die ASP.NET Engine an den gerenderten id Werten von Serversteuerelementen vornimmt, fügen wir der IDIssues.aspx Seite einige Websteuerelemente hinzu und zeigen dann das gerenderte Markup an, das an den Browser gesendet wird. Geben Sie insbesondere den Text "Bitte geben Sie Ihr Alter ein:" gefolgt von einem TextBox-Websteuerelement ein. Fügen Sie weiter unten auf der Seite ein Button-Web-Steuerelement und ein Label Web-Steuerelement hinzu. Legen Sie die Eigenschaften und Columns von ID TextBox auf Age bzw. 3 fest. Legen Sie die Eigenschaften der Schaltfläche Text und ID auf "Submit" und fest SubmitButton. Löschen Sie die Eigenschaft von Label, Text und legen Sie sie ID auf fest Results.

An diesem Punkt sollte das deklarative Markup Ihres Inhaltssteuerelements in etwa wie folgt aussehen:

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

Abbildung 3 zeigt die Seite, wenn sie über den Visual Studio-Designer angezeigt wird.

Die Seite enthält drei Websteuerelemente: ein Textfeld, eine Schaltfläche und eine Bezeichnung.

Abbildung 03: Die Seite enthält drei Websteuerelemente: textBox, Button und Label (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Besuchen Sie die Seite über einen Browser, und zeigen Sie dann die HTML-Quelle an. Wie das folgende Markup zeigt, sind die id Werte der HTML-Elemente für die Steuerelemente TextBox, Button und Label Web eine Kombination aus den ID Werten der Websteuerelemente und den ID Werten der Namenscontainer auf der Seite.

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

Wie weiter oben in diesem Tutorial erwähnt, dienen sowohl die seite master als auch die zugehörigen ContentPlaceHolders als Benennungscontainer. Folglich tragen beide die gerenderten ID Werte ihrer geschachtelten Steuerelemente bei. Nehmen Sie das TextBox-Attribut id für instance: ctl00_MainContent_Age. Denken Sie daran, dass der Wert des ID TextBox-Steuerelements lautet Age. Diesem wird der Wert des ContentPlaceHolder-Steuerelements ID vorangestellt. MainContent Darüber hinaus wird diesem Wert der wert der master Seite ID vorangestellt. ctl00 Der Nettoeffekt ist ein id Attributwert, der aus den ID Werten der master Seite, des ContentPlaceHolder-Steuerelements und des TextBox-Steuerelements selbst besteht.

Abbildung 4 veranschaulicht dieses Verhalten. Um das Gerenderte id des Age TextBox-Steuerelements zu bestimmen, beginnen Sie mit dem ID Wert des TextBox-Steuerelements Age. Arbeiten Sie sich als Nächstes in der Steuerungshierarchie nach oben. Präfixieren Sie bei jedem Namenscontainer (Knoten mit pfirsichfarbener Farbe) dem aktuellen Gerenderten id den Namen des Namenscontainers id.

Die Attribute der gerenderten ID basieren auf den ID-Werten der Benennungscontainer.

Abbildung 04: Die gerenderten id Attribute basieren auf den ID Werten der Benennungscontainer.

Hinweis

Wie bereits erwähnt, stellt der ctl00 Teil des gerenderten id Attributs den ID Wert der master Seite dar, aber Sie fragen sich vielleicht, wie dieser ID Wert entstanden ist. Wir haben sie an keiner Stelle auf unserer master- oder Inhaltsseite angegeben. Die meisten Serversteuerelemente in einer ASP.NET Seite werden explizit über das deklarative Markup der Seite hinzugefügt. Das MainContent ContentPlaceHolder-Steuerelement wurde explizit im Markup von Site.masterangegeben. Das Age TextBox-Markup wurde definiert IDIssues.aspx. Wir können die ID Werte für diese Typen von Steuerelementen über die Eigenschaftenfenster oder über die deklarative Syntax angeben. Andere Steuerelemente, z. B. die master Seite selbst, sind im deklarativen Markup nicht definiert. Folglich müssen ihre ID Werte automatisch für uns generiert werden. Die ASP.NET-Engine legt die ID Werte zur Laufzeit für die Steuerelemente fest, deren IDs nicht explizit festgelegt wurden. Es wird das Namensmuster ctlXXverwendet, wobei XX ein sequenziell steigender ganzzahliger Wert ist.

Da die seite master selbst als Namenscontainer fungiert, weisen die websteuerelemente, die auf der seite master definiert sind, auch geänderte gerenderte id Attributwerte auf. Die Bezeichnung, die DisplayDate wir der Seite master im Tutorial Erstellen eines Site-Wide Layouts mit Gestaltungsvorlagen hinzugefügt haben, weist beispielsweise das folgende gerenderte Markup auf:

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

Beachten Sie, dass das id Attribut sowohl den Wert der master Seite (ctl00) als auch den ID Wert des Label Web-Steuerelements ID (DateDisplay) enthält.

Schritt 3: Programmgesteuertes Verweisen auf Websteuerelemente überFindControl

Jedes ASP.NET Serversteuerelements enthält eine FindControl("controlID") Methode, die die Nachfolger des Steuerelements nach einem Steuerelement namens controlID durchsucht. Wenn ein solches Steuerelement gefunden wird, wird es zurückgegeben. Wenn kein übereinstimmende Steuerelement gefunden wird, FindControl wird zurückgegeben Nothing.

FindControl ist nützlich in Szenarien, in denen Sie auf ein Steuerelement zugreifen müssen, aber keinen direkten Verweis darauf haben. Wenn Sie mit Datenwebsteuerelementen wie GridView arbeiten, werden die Steuerelemente in den GridView-Feldern einmal in der deklarativen Syntax definiert, aber zur Laufzeit wird für jede GridView-Zeile ein instance des Steuerelements erstellt. Folglich sind die zur Laufzeit generierten Steuerelemente vorhanden, aber wir haben keinen direkten Verweis aus der CodeBehind-Klasse. Daher müssen wir verwenden FindControl , um programmgesteuert mit einem bestimmten Steuerelement in den GridView-Feldern zu arbeiten. (Weitere Informationen zur Verwendung FindControl von für den Zugriff auf die Steuerelemente in den Vorlagen eines Datenwebsteuerelements finden Sie unter Benutzerdefinierte Formatierung basierend auf Daten.) Dieses Szenario tritt auch beim dynamischen Hinzufügen von Websteuerelementen zu einem Webformular auf, ein Thema, das unter Erstellen dynamischer Benutzeroberflächen für die Dateneingabe erläutert wird.

Um die Verwendung der FindControl -Methode zum Suchen nach Steuerelementen innerhalb einer Inhaltsseite zu veranschaulichen, erstellen Sie einen Ereignishandler für das SubmitButton-Ereignis des -Ereignisses Click . Fügen Sie im Ereignishandler den folgenden Code hinzu, der programmgesteuert mithilfe der FindControl -Methode auf textBox Age und Results Label verweist und dann basierend auf der Eingabe des Benutzers eine Nachricht anzeigtResults.

Hinweis

Natürlich müssen wir nicht verwenden FindControl , um auf die Steuerelemente Label und TextBox für dieses Beispiel zu verweisen. Wir könnten direkt über ihre ID Eigenschaftswerte auf sie verweisen. Ich verwende FindControl hier, um zu veranschaulichen, was bei der Verwendung FindControl von einer Inhaltsseite geschieht.

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

Während sich die zum Aufrufen der FindControl Methode verwendete Syntax in den ersten beiden Zeilen geringfügig SubmitButton_Clickunterscheidet, sind sie semantisch gleichwertig. Denken Sie daran, dass alle ASP.NET Serversteuerelemente eine FindControl -Methode enthalten. Dies schließt die Page -Klasse ein, von der alle ASP.NET CodeBehind-Klassen abgeleitet werden müssen. Daher entspricht das Aufrufen FindControl("controlID") dem Aufrufen Page.FindControl("controlID")von , vorausgesetzt, Sie haben die FindControl Methode nicht in Ihrer CodeBehind-Klasse oder in einer benutzerdefinierten Basisklasse überschrieben.

Nachdem Sie diesen Code eingegeben haben, besuchen Sie die IDIssues.aspx Seite über einen Browser, geben Sie Ihr Alter ein, und klicken Sie auf die Schaltfläche "Übermitteln". Beim Klicken auf die Schaltfläche "Übermitteln" wird ein NullReferenceException ausgelöst (siehe Abbildung 5).

NullReferenceException wird ausgelöst.

Abbildung 05: A NullReferenceException wird ausgelöst (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Wenn Sie einen Haltepunkt im SubmitButton_Click Ereignishandler festlegen, wird angezeigt, dass beide Aufrufe zurückgegeben FindControlNothingwerden. Wird NullReferenceException ausgelöst, wenn wir versuchen, auf die Eigenschaft von AgeText TextBox zuzugreifen.

Das Problem besteht darin, dass Control.FindControl nur die Absteigenden von Control durchsucht werden, die sich im gleichen Namenscontainer befinden. Da die master Seite einen neuen Namenscontainer darstellt, Page.FindControl("controlID") wird das master-Seitenobjekt ctl00nie durchlässig. (Siehe Abbildung 4, um die Steuerelementhierarchie anzuzeigen, die das Page Objekt als übergeordnetes Element des master Seitenobjekts ctl00darstellt.) Daher werden die Results Label- und Age TextBox-Werte nicht gefunden und ResultsLabelAgeTextBox und sind zugewiesene Werte von Nothing.

Es gibt zwei Problemumgehungen für diese Herausforderung: Wir können einen Drilldown ausführen, jeweils einen Namenscontainer, zum entsprechenden Steuerelement. oder wir können eine eigene FindControl Methode erstellen, die die Benennung von Containern durchdringt. Lassen Sie uns jede dieser Optionen untersuchen.

Bohren in den entsprechenden Benennungscontainer

Wenn Sie verwenden FindControl möchten, um auf das Results Label oder Age TextBox zu verweisen, müssen Sie von einem Vorgängersteuerelement aus im gleichen Benennungscontainer aufrufen FindControl . Wie Abbildung 4 gezeigt hat, ist das MainContent ContentPlaceHolder-Steuerelement der einzige Vorgänger von Results oder Age , das sich innerhalb desselben Namenscontainers befindet. Anders ausgedrückt: Beim Aufrufen der FindControl -Methode aus dem MainContent -Steuerelement, wie im codeausschnitt unten gezeigt, wird ordnungsgemäß ein Verweis auf das Results -Steuerelement oder Age zurückgegeben.

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

Wir können jedoch nicht mit dem ContentPlaceHolder aus der MainContent Code-Behind-Klasse unserer Inhaltsseite mithilfe der obigen Syntax arbeiten, da der ContentPlaceHolder auf der master Seite definiert ist. Stattdessen müssen wir verwenden FindControl , um einen Verweis auf abzurufen MainContent. Ersetzen Sie den Code im SubmitButton_Click Ereignishandler durch die folgenden Änderungen:

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

Wenn Sie die Seite über einen Browser besuchen, Ihr Alter eingeben und auf die Schaltfläche "Übermitteln" klicken, wird ein NullReferenceException ausgelöst. Wenn Sie einen Haltepunkt im SubmitButton_Click Ereignishandler festlegen, sehen Sie, dass diese Ausnahme beim Versuch auftritt, die Methode des MainContentFindControl Objekts aufzurufen. Das MainContent -Objekt ist gleich, Nothing da die FindControl -Methode kein Objekt mit dem Namen "MainContent" finden kann. Der zugrunde liegende Grund ist der gleiche wie bei den Results Steuerelementen Label und Age TextBox: FindControl Startet die Suche von oben in der Steuerelementhierarchie und durchdringt keine Benennungscontainer, aber der MainContent ContentPlaceHolder befindet sich innerhalb der master Seite, bei der es sich um einen Benennungscontainer handelt.

Bevor wir verwenden FindControl können, um einen Verweis auf abzurufenMainContent, benötigen wir zunächst einen Verweis auf das master-Seitensteuerelements. Sobald wir einen Verweis auf die master Seite haben, können wir über FindControl einen Verweis auf den MainContent ContentPlaceHolder und von dort aus Verweise auf die Bezeichnung und Age das Results Textfeld abrufen (wiederum mithilfe von FindControl). Aber wie erhalten wir einen Verweis auf die master Seite? Wenn Sie die id Attribute im gerenderten Markup überprüfen, wird offensichtlich, dass der Wert der ID master Seite istctl00. Daher könnten wir verwendenPage.FindControl("ctl00"), um einen Verweis auf die master-Seite abzurufen, und dann dieses Objekt verwenden, um einen Verweis auf usw. abzurufenMainContent. Der folgende Codeausschnitt veranschaulicht diese Logik:

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

Obwohl dieser Code sicherlich funktioniert, wird davon ausgegangen, dass die automatisch generierte ID master Seite immer sein ctl00wird. Es ist nie eine gute Idee, Annahmen über automatisch generierte Werte zu treffen.

Glücklicherweise ist ein Verweis auf die seite master über die -Eigenschaft der Page -Klasse Master zugänglich. Daher können wir stattdessen FindControl("ctl00")Page.Master.FindControl("MainContent")verwenden, um einen Verweis auf die master-Seite abzurufen, um auf den MainContent ContentPlaceHolder zuzugreifen. Aktualisieren Sie den SubmitButton_Click Ereignishandler mit dem folgenden Code:

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

Wenn Sie die Seite dieses Mal über einen Browser besuchen, Ihr Alter eingeben und auf die Schaltfläche "Übermitteln" klicken, wird die Meldung wie erwartet in der Results Bezeichnung angezeigt.

Das Alter des Benutzers wird in der Bezeichnung angezeigt.

Abbildung 06: Das Alter des Benutzers wird in der Bezeichnung angezeigt (Klicken Sie, um das vollständige Bild anzuzeigen)

Rekursive Suche nach Benennungscontainern

Der Grund, warum im vorherigen Codebeispiel auf das MainContent ContentPlaceHolder-Steuerelement von der seite master und dann auf das Results Label- und Age TextBox-Steuerelement von MainContentverwiesen wurde, liegt daran, dass die Control.FindControl -Methode nur im Namenscontainer von Control sucht. Der FindControl Verbleib im Benennungscontainer ist in den meisten Szenarien sinnvoll, da zwei Steuerelemente in zwei unterschiedlichen Benennungscontainern möglicherweise dieselben ID Werte aufweisen. Betrachten Sie den Fall eines GridView-Steuerelements, das ein Label-Websteuerelement mit dem Namen ProductName in einem seiner TemplateFields definiert. Wenn die Daten zur Laufzeit an die GridView gebunden sind, wird für jede GridView-Zeile eine ProductName Bezeichnung erstellt. Wenn FindControl alle Benennungscontainer durchsucht wurden und wir aufgerufen habenPage.FindControl("ProductName"), welche Bezeichnung instance sollte zurückgegeben FindControl werden? Die ProductName Bezeichnung in der ersten GridView-Zeile? Die in der letzten Zeile?

Daher ist es in den meisten Fällen sinnvoll, Control.FindControl nur den Namenscontainer von Control zu suchen. Es gibt jedoch andere Fälle, z. B. den, in dem wir für alle Benennungscontainer eine eindeutige ID haben und vermeiden möchten, dass wir für den Zugriff auf ein Steuerelement akribisch auf jeden Benennungscontainer in der Steuerungshierarchie verweisen müssen. FindControl Eine Variante zu haben, die alle Namenscontainer rekursiv durchsucht, ist ebenfalls sinnvoll. Leider enthält die .NET Framework eine solche Methode nicht.

Die gute Nachricht ist, dass wir eine eigene FindControl Methode erstellen können, die alle Benennungscontainer rekursiv durchsucht. Tatsächlich können wir mit Erweiterungsmethoden eine FindControlRecursive Methode an die -Klasse anheften, um die Control vorhandene FindControl Methode zu begleiten.

Hinweis

Erweiterungsmethoden sind ein neues Feature in C# 3.0 und Visual Basic 9. Dies sind die Sprachen, die mit den .NET Framework Version 3.5 und Visual Studio 2008 ausgeliefert werden. Kurz gesagt, erweiterungsmethoden ermöglichen es einem Entwickler, eine neue Methode für einen vorhandenen Klassentyp über eine spezielle Syntax zu erstellen. Weitere Informationen zu diesem hilfreichen Feature finden Sie in meinem Artikel Erweitern der Basistypfunktionalität mit Erweiterungsmethoden.

Um die Erweiterungsmethode zu erstellen, fügen Sie dem Ordner eine neue Datei mit dem App_Code Namen PageExtensionMethods.vbhinzu. Fügen Sie eine Erweiterungsmethode mit dem Namen FindControlRecursive hinzu, die als Eingabe einen String Parameter mit dem Namen controlIDakzeptiert. Damit Erweiterungsmethoden ordnungsgemäß funktionieren, ist es wichtig, dass die Klasse als eine Module gekennzeichnet wird und dass den Erweiterungsmethoden das <Extension()> -Attribut vorangestellt wird. Darüber hinaus müssen alle Erweiterungsmethoden als ersten Parameter ein Objekt des Typs akzeptieren, für den die Erweiterungsmethode gilt.

Fügen Sie der Datei den PageExtensionMethods.vb folgenden Code hinzu, um diese Module und die FindControlRecursive Erweiterungsmethode zu definieren:

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

Wenn dieser Code vorhanden ist, kehren Sie zur CodeBehind-Klasse der IDIssues.aspx Seite zurück, und kommentieren Sie die aktuellen FindControl Methodenaufrufe aus. Ersetzen Sie sie durch Aufrufe von Page.FindControlRecursive("controlID"). Das Besondere an Erweiterungsmethoden ist, dass sie direkt in den IntelliSense-Dropdownlisten angezeigt werden. Wie Abbildung 7 zeigt, ist die FindControlRecursive Methode beim Eingeben Page und Anschließenden Trefferpunkt zusammen mit den anderen Control Klassenmethoden in der IntelliSense-Dropdownliste enthalten.

Erweiterungsmethoden sind in den IntelliSense-Dropdownlisten enthalten.

Abbildung 07: Erweiterungsmethoden sind im IntelliSense-Drop-Downs enthalten (Klicken Sie hier, um das bild in voller Größe anzuzeigen)

Geben Sie den folgenden Code in den SubmitButton_Click Ereignishandler ein, und testen Sie ihn dann, indem Sie die Seite besuchen, Ihr Alter eingeben und auf die Schaltfläche "Übermitteln" klicken. Wie in Abbildung 6 dargestellt, wird die resultierende Ausgabe die Meldung "Sie sind alt!"

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

Hinweis

Da Erweiterungsmethoden in C# 3.0 und Visual Basic 9 neu sind, können Sie bei Verwendung von Visual Studio 2005 keine Erweiterungsmethoden verwenden. Stattdessen müssen Sie die FindControlRecursive -Methode in einer Hilfsklasse implementieren. Rick Strahl hat ein solches Beispiel in seinem Blogbeitrag, ASP.NET Maser Pages und FindControl.

Schritt 4: Verwenden des richtigenidAttributwerts in Client-Side Skript

Wie in der Einführung in diesem Tutorial erwähnt, wird das gerenderte id Attribut eines Websteuerelements häufig in clientseitigen Skripts verwendet, um programmgesteuert auf ein bestimmtes HTML-Element zu verweisen. Das folgende JavaScript verweist beispielsweise auf ein HTML-Element nach und id zeigt seinen Wert dann in einem modalem Meldungsfeld an:

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

Denken Sie daran, dass das Attribut des id gerenderten HTML-Elements in ASP.NET Seiten, die keinen Benennungscontainer enthalten, mit dem Eigenschaftswert des Websteuerelements ID identisch ist. Aus diesem Grund ist es verlockend, in Attributwerten hart in id JavaScript-Code zu programmieren. Das heißt, wenn Sie wissen, dass Sie über clientseitiges Skript auf das TextBox-Websteuerelement zugreifen möchten, tun Sie dies Age über einen Aufruf von document.getElementById("Age").

Das Problem bei diesem Ansatz besteht darin, dass der gerenderte HTML-Code id bei Verwendung von master Seiten (oder anderen Namenscontainersteuerelementen) nicht mit der Eigenschaft des Websteuerelements ID gleichbedeutend ist. Ihre erste Neigung kann sein, die Seite über einen Browser zu besuchen und die Quelle anzuzeigen, um das tatsächliche id Attribut zu bestimmen. Sobald Sie den gerenderten id Wert kennen, können Sie ihn in den Aufruf von einfügen, um getElementById auf das HTML-Element zuzugreifen, mit dem Sie über clientseitiges Skript arbeiten müssen. Dieser Ansatz ist weniger als ideal, da bestimmte Änderungen an der Steuerelementhierarchie der Seite oder Änderungen an den ID Eigenschaften der Benennungssteuerelemente das resultierende id Attribut ändern und dadurch Ihren JavaScript-Code unterbrechen.

Die gute Nachricht ist, dass auf den id gerenderten Attributwert im serverseitigen Code über die Eigenschaft des Websteuerelements ClientIDzugegriffen werden kann. Sie sollten diese Eigenschaft verwenden, um den Attributwert zu bestimmen, der id im clientseitigen Skript verwendet wird. Um z. B. eine JavaScript-Funktion zur Seite hinzuzufügen, die beim Aufruf den Wert des Age TextBox in einem modalen Meldungsfeld anzeigt, fügen Sie dem Ereignishandler den Page_Load folgenden Code hinzu:

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)

Der obige Code fügt den Wert der Age TextBox-Eigenschaft ClientID in den JavaScript-Aufruf von ein getElementById. Wenn Sie diese Seite über einen Browser besuchen und die HTML-Quelle anzeigen, finden Sie den folgenden JavaScript-Code:

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

Beachten Sie, wie der richtige id Attributwert ( ctl00_MainContent_Age) innerhalb des Aufrufs von getElementByIdangezeigt wird. Da dieser Wert zur Laufzeit berechnet wird, funktioniert er unabhängig von späteren Änderungen an der Seitensteuerelementhierarchie.

Hinweis

Dieses JavaScript-Beispiel zeigt lediglich, wie eine JavaScript-Funktion hinzugefügt wird, die ordnungsgemäß auf das von einem Serversteuerelement gerenderte HTML-Element verweist. Um diese Funktion zu verwenden, müssen Sie zusätzliches JavaScript erstellen, um die Funktion aufzurufen, wenn das Dokument geladen wird oder wenn eine bestimmte Benutzeraktion ausgeführt wird. Weitere Informationen zu diesen und verwandten Themen finden Sie unter Arbeiten mit Client-Side Skript.

Zusammenfassung

Bestimmte ASP.NET Serversteuerelemente fungieren als Benennungscontainer, was sich auf die gerenderten id Attributwerte ihrer untergeordneten Steuerelemente sowie auf den Bereich der von der FindControl -Methode canvassierten Steuerelemente auswirkt. In Bezug auf master Seiten benennen sowohl die master Seite selbst als auch die zugehörigen ContentPlaceHolder-Steuerelemente Container. Daher müssen wir etwas mehr Arbeit auf die Programmsteuerung innerhalb der Inhaltsseite mit FindControlausführen. In diesem Tutorial haben wir zwei Techniken untersucht: Bohren in das ContentPlaceHolder-Steuerelement und aufrufen deren FindControl Methode; und Rollieren unserer eigenen FindControl Implementierung, die alle Benennungscontainer rekursiv durchsucht.

Neben den serverseitigen Problemen bei der Benennung von Containern in Bezug auf den Verweis auf Websteuerelemente gibt es auch clientseitige Probleme. Ohne Benennung von Containern sind der Eigenschaftswert und der gerenderte id Attributwert des Websteuerelements ID identisch. Mit dem Hinzufügen von Benennungscontainern enthält das gerenderte id Attribut jedoch sowohl die ID Werte des Websteuerelements als auch die Benennungscontainer in der Herkunft der Steuerungshierarchie. Diese Benennungsprobleme sind kein Problem, solange Sie die Eigenschaft des Websteuerelements ClientID verwenden, um den gerenderten id Attributwert in Ihrem clientseitigen Skript zu bestimmen.

Viel Spaß beim Programmieren!

Weitere Informationen

Weitere Informationen zu den in diesem Tutorial erläuterten Themen finden Sie in den folgenden Ressourcen:

Zum Autor

Scott Mitchell, Autor mehrerer ASP/ASP.NET-Bücher und Gründer von 4GuysFromRolla.com, arbeitet seit 1998 mit Microsoft-Webtechnologien. Scott arbeitet als unabhängiger Berater, Trainer und Autor. Sein neuestes Buch ist Sams Teach Yourself ASP.NET 3.5 in 24 Stunden. Scott kann unter mitchell@4GuysFromRolla.com oder über seinen Blog unter http://ScottOnWriting.NETerreicht werden.

Besonderen Dank an

Diese Tutorialreihe wurde von vielen hilfreichen Prüfern überprüft. Hauptprüfer für dieses Tutorial waren Zack Jones und Suchi Barnerjee. Möchten Sie meine anstehenden MSDN-Artikel lesen? Wenn dies der Fall ist, legen Sie eine Zeile unter ab mitchell@4GuysFromRolla.com.