Benennung von Steuerelement-IDs auf Inhaltsseiten (VB)
von Scott Mitchell
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 ID
ProductName
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 FindControl
verweisen 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.
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.
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.
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
.
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.master
angegeben. 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 ctlXX
verwendet, 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_Click
unterscheidet, 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).
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 FindControl
Nothing
werden. Wird NullReferenceException
ausgelöst, wenn wir versuchen, auf die Eigenschaft von Age
Text
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 ctl00
nie durchlässig. (Siehe Abbildung 4, um die Steuerelementhierarchie anzuzeigen, die das Page
Objekt als übergeordnetes Element des master Seitenobjekts ctl00
darstellt.) Daher werden die Results
Label- und Age
TextBox-Werte nicht gefunden und ResultsLabel
AgeTextBox
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 MainContent
FindControl
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 ctl00
wird. 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.
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 MainContent
verwiesen 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.vb
hinzu. Fügen Sie eine Erweiterungsmethode mit dem Namen FindControlRecursive
hinzu, die als Eingabe einen String
Parameter mit dem Namen controlID
akzeptiert. 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.
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 richtigenid
Attributwerts 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 ClientID
zugegriffen 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 getElementById
angezeigt 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 FindControl
ausfü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:
- ASP.NET Gestaltungsvorlagen und
FindControl
- Erstellen von Benutzeroberflächen für dynamische Dateneingaben
- Vorgehensweise: Verweisen auf ASP.NET Gestaltungsvorlageninhalt
- Mater Pages: Tipps, Tricks und Fallen
- Arbeiten mit Client-Side Skript
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.
Feedback
https://aka.ms/ContentUserFeedback.
Bald verfügbar: Im Laufe des Jahres 2024 werden wir GitHub-Issues stufenweise als Feedbackmechanismus für Inhalte abbauen und durch ein neues Feedbacksystem ersetzen. Weitere Informationen finden Sie unterFeedback senden und anzeigen für