Benennung von Steuerelement-IDs auf Inhaltsseiten (VB)
von Scott Mitchell
Veranschaulicht, wie ContentPlaceHolder-Steuerelemente als Benennungscontainer dienen und daher die programmgesteuerte Arbeit mit einem Steuerelement erschweren (über FindControl). Untersucht dieses Problem und 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 die Mittel ist, mit der programmgesteuert auf das Steuerelement in der CodeBehind-Klasse 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 clientseitigem Skript verwendet, um programmgesteuert auf ein bestimmtes HTML-Element zu verweisen. In diesem Fall können Sie davon ausgehen, dass beim Rendern eines ASP.NET Serversteuerelements in HTML der 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 wird. Betrachten Sie ein GridView-Steuerelement, das ein TemplateField mit einem Label-Websteuerelement mit einem ID
Wert von ProductName
. Wenn die 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 behandeln, ermöglicht ASP.NET, dass bestimmte Steuerelemente als Benennungscontainer bezeichnet werden. Ein Benennungscontainer dient als neuer ID
Namespace. Alle Serversteuerelemente, die im Benennungscontainer angezeigt werden, haben den ID
gerenderten id
Wert mit dem Präfix des Benennungscontainersteuerelements. Beispielsweise sind die Klassen und GridViewRow
die GridView
Beiden Benennungscontainer. Folglich erhält ein in einem GridView TemplateField ID
ProductName
definiertes Label-Steuerelement einen gerenderten id
Wert von 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 Benennungscontainer funktionieren soll. Die INamingContainer
Schnittstelle enthält keine Methoden, die vom Serversteuerelement implementiert werden müssen. Stattdessen wird sie als Markierung verwendet. Wenn ein Steuerelement diese Schnittstelle implementiert, wird beim Generieren des gerenderten Markups automatisch der Wert des ASP.NET Moduls den ID
gerenderten Attributwerten der untergeordneten id
Elemente vorangestellt. Dieser Prozess wird in Schritt 2 ausführlicher erläutert.
Das Benennen von Containern ändert nicht nur den gerenderten id
Attributwert, sondern wirkt sich auch darauf aus, wie programmgesteuert über die CodeBehind-Klasse der ASP.NET Seite verwiesen wird. Die FindControl("controlID")
Methode wird häufig verwendet, um programmgesteuert auf ein Websteuerelement zu verweisen. FindControl
dringt jedoch nicht durch Benennungscontainer durch. Daher können Sie die Page.FindControl
Methode nicht direkt verwenden, um auf Steuerelemente in einem GridView- oder anderen Benennungscontainer zu verweisen.
Wie Sie vielleicht bereits vermutet haben, werden Gestaltungsvorlagen und ContentPlaceHolders beide als Benennungscontainer implementiert. In diesem Lernprogramm untersuchen wir, wie Gestaltungsvorlagen sich auf HTML-Elementwerte id
und Methoden auswirken, um mithilfe einer Inhaltsseite FindControl
programmgesteuert auf Websteuerelemente zu verweisen.
Schritt 1: Hinzufügen einer neuen ASP.NET Seite
Um die in diesem Lernprogramm erläuterten Konzepte zu veranschaulichen, fügen wir unserer Website eine neue ASP.NET Seite hinzu. Erstellen Sie eine neue Inhaltsseite, die im Stammordner benannt ist IDIssues.aspx
, und binden Sie sie an die Site.master
Gestaltungsvorlage.
Abbildung 01: Hinzufügen der Inhaltsseite IDIssues.aspx
zum Stammordner
Visual Studio erstellt automatisch ein Inhaltssteuerelement für die vier ContentPlaceHolders der Gestaltungsvorlage. Wie im Lernprogramm "Multiple ContentPlaceHolders" und "Default Content Content " erwähnt, wird stattdessen ein Inhaltssteuerelement ausgegeben, wenn kein Inhaltssteuerelement vorhanden ist, der standardmäßige ContentPlaceHolder-Inhalt der Gestaltungsvorlage angezeigt wird. Da die QuickLoginUI
Und LeftColumnContent
ContentPlaceHolder geeignetes Standardmarkup für diese Seite enthalten, fahren Sie fort, und entfernen Sie die entsprechenden Inhaltssteuerelemente 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 Lernprogramm "Titel", "Metatags" und "Andere HTML-Kopfzeilen" im Lernprogramm "Gestaltungsvorlage" haben wir eine benutzerdefinierte Basisseitenklasse (BasePage
) erstellt, die den Titel der Seite automatisch konfiguriert, wenn sie nicht explizit festgelegt ist. Damit die IDIssues.aspx
Seite diese Funktionalität verwendet, muss die CodeBehind-Klasse der Seite von der BasePage
Klasse abgeleitet werden (anstelle von System.Web.UI.Page
). Ändern Sie die CodeBehind-Klasse-Definition 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 <siteMapNode>
Element hinzu, und legen Sie dessen title
Attribute url
auf "Steuerelement-ID-Benennungsprobleme" bzw ~/IDIssues.aspx
. "Steuerelement-ID-Benennungsprobleme" fest. Nachdem Sie diese Ergänzung vorgenommen haben, sollte das Markup der Web.sitemap
Datei 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 Web.sitemap
sofort im Abschnitt "Lektionen" in der linken Spalte wiedergegeben.
Abbildung 02: Der Abschnitt "Lektionen" enthält jetzt einen Link zu "Probleme bei der Benennung von Steuerelement-IDs"
Schritt 2: Untersuchen der gerendertenID
Änderungen
Um die Änderungen des moduls ASP.NET an die gerenderten id
Werte von Serversteuerelementen besser zu verstehen, fügen wir der Seite einige Websteuerelemente hinzu und zeigen dann das gerenderte Markup an IDIssues.aspx
, 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 auf der Seite ein Schaltflächenweb-Steuerelement und ein Bezeichnungswebsteuerelement hinzu. Legen Sie die Eigenschaften Age
des Columns
TextBox-Steuerelements ID
auf bzw. 3 fest. Legen Sie die Schaltflächen Text
und Eigenschaften auf "Submit" und SubmitButton
ID
. Löschen Sie die Eigenschaft der Bezeichnung Text
, und legen Sie sie ID
auf Results
.
An diesem Punkt sollte das deklarative Markup ihres Inhaltssteuerelements 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 im Visual Studio-Designer angezeigt wird.
Abbildung 03: Die Seite enthält drei Websteuerelemente: ein TextBox-, Schaltflächen- und Beschriftungssymbol (Klicken, 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 der ID
Werte der Benennungscontainer 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 Lernprogramm erwähnt, dienen sowohl die Gestaltungsvorlage 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
, z. B.: ctl00_MainContent_Age
. Erinnern Sie sich daran, dass der Wert des TextBox-Steuerelements ID
lautete Age
. Dies wird dem Wert des ContentPlaceHolder-Steuerelements ID
vorangestellt. MainContent
Darüber hinaus wird dieser Wert dem Wert der Gestaltungsvorlage ID
vorangestellt. ctl00
Der Nettoeffekt ist ein id
Attributwert, der aus den ID
Werten der Gestaltungsvorlage, dem ContentPlaceHolder-Steuerelement und dem TextBox selbst besteht.
Abbildung 4 veranschaulicht dieses Verhalten. Um das Rendern id
des Age
TextBox-Steuerelements zu bestimmen, beginnen Sie mit dem ID
Wert des TextBox-Steuerelements. Age
Arbeiten Sie als Nächstes nach oben in der Steuerelementhierarchie. Stellen Sie bei jedem Benennungscontainer (diese Knoten mit einer Pfirsichfarbe) dem aktuellen Präfix voran, der mit dem Benennungscontainer id
gerendert wirdid
.
Abbildung 04: Die gerenderten id
Attribute basieren auf den ID
Werten der Benennungscontainer
Hinweis
Wie wir bereits erläutert haben, stellt der ctl00
Teil des gerenderten id
Attributs den ID
Wert der Gestaltungsvorlage dar, aber Sie fragen sich vielleicht, wie dieser ID
Wert entstanden ist. Wir haben sie nicht an einer beliebigen Stelle auf unserer Gestaltungsvorlage oder Inhaltsseite angegeben. Die meisten Serversteuerelemente auf 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
; das Age
TextBox-Element wurde als Markup definiert IDIssues.aspx
. Wir können die ID
Werte für diese Arten von Steuerelementen über die Eigenschaftenfenster oder aus der deklarativen Syntax angeben. Andere Steuerelemente, wie die Gestaltungsvorlage selbst, werden nicht im deklarativen Markup definiert. Folglich müssen ihre ID
Werte für uns automatisch generiert werden. Das modul ASP.NET legt die ID
Werte zur Laufzeit für diese Steuerelemente fest, deren IDs nicht explizit festgelegt wurden. Es verwendet das Benennungsmuster ctlXX
, wobei XX ein sequenziell steigender ganzzahliger Wert ist.
Da die Gestaltungsvorlage selbst als Benennungscontainer dient, haben die in der Gestaltungsvorlage definierten Websteuerelemente auch gerenderte id
Attributwerte geändert. Die Bezeichnung, die wir der Gestaltungsvorlage im Lernprogramm "Erstellen eines websiteweiten Layouts mit Gestaltungsvorlagen" hinzugefügt haben, DisplayDate
weist beispielsweise das folgende gerenderte Markup auf:
<span id="ctl00_DateDisplay">current date</span>
Beachten Sie, dass das id
Attribut sowohl den Wert der Gestaltungsvorlage ID
(ctl00
) als auch den ID
Wert des Label-Websteuerelements (DateDisplay
) enthält.
Schritt 3: Programmgesteuertes Verweisen auf Websteuerelemente überFindControl
Jedes ASP.NET Serversteuerelement enthält eine FindControl("controlID")
Methode, mit der die untergeordneten Elemente des Steuerelements nach einem Steuerelement namens controlID durchsucht werden. Wenn ein solches Steuerelement gefunden wird, wird es zurückgegeben; wenn kein übereinstimmende Steuerelement gefunden wird, FindControl
wird zurückgegeben Nothing
.
FindControl
ist in Szenarien hilfreich, in denen Sie auf ein Steuerelement zugreifen müssen, aber keinen direkten Verweis darauf haben. Wenn Sie beispielsweise mit Datenwebsteuerelementen wie gridView arbeiten, werden die Steuerelemente in den Feldern von GridView einmal in der deklarativen Syntax definiert, aber zur Laufzeit wird für jede GridView-Zeile eine Instanz des Steuerelements erstellt. Folglich sind die zur Laufzeit generierten Steuerelemente vorhanden, aber wir verfügen nicht über einen direkten Verweis aus der CodeBehind-Klasse. Daher müssen wir verwenden, FindControl
um programmgesteuert mit einem bestimmten Steuerelement innerhalb der GridView-Felder zu arbeiten. (Weitere Informationen zur Verwendung FindControl
der Steuerelemente in den Vorlagen eines Datenwebsteuerelements finden Sie unter Benutzerdefinierte Formatierung basierend auf Daten.) Dieses Szenario tritt auf, wenn Websteuerelemente dynamisch zu einem Webformular hinzugefügt werden, ein Thema, das in der Erstellung dynamischer Dateneingabebenutzeroberflächen erläutert wird.
Um die Verwendung der FindControl
Methode zum Suchen nach Steuerelementen auf einer Inhaltsseite zu veranschaulichen, erstellen Sie einen Ereignishandler für das SubmitButton
Ereignis.Click
Fügen Sie im Ereignishandler den folgenden Code hinzu, der programmgesteuert mithilfe der FindControl
Methode auf das Age
TextBox- und Results
Label-Element verweist, und zeigt dann basierend auf der Eingabe des Benutzers eine Meldung Results
an.
Hinweis
Natürlich müssen FindControl
wir für dieses Beispiel nicht auf die Steuerelemente "Label" und "TextBox" verweisen. Wir konnten sie direkt über ihre ID
Eigenschaftswerte referenzieren. Ich verwende FindControl
hier, um zu veranschaulichen, was passiert, wenn sie von einer Inhaltsseite aus verwendet FindControl
wird.
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 SubmitButton_Click
geringfügig unterscheidet, sind sie semantisch gleichwertig. Denken Sie daran, dass alle ASP.NET Serversteuerelemente eine FindControl
Methode enthalten. Dies schließt die Klasse ein, von der Page
alle ASP.NET CodeBehind-Klassen abgeleitet werden müssen. Daher entspricht der Aufruf FindControl("controlID")
dem Aufrufen Page.FindControl("controlID")
, vorausgesetzt, Sie haben die FindControl
Methode in Ihrer CodeBehind-Klasse oder in einer benutzerdefinierten Basisklasse nicht außer Kraft gesetzt.
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 "Absenden". Beim Klicken auf die Schaltfläche "Absenden" wird eine NullReferenceException
ausgelöst (siehe Abbildung 5).
Abbildung 05: A NullReferenceException
wird ausgelöst (Klicken, um das Bild in voller Größe anzuzeigen)
Wenn Sie einen Haltepunkt im SubmitButton_Click
Ereignishandler festlegen, sehen Sie, dass beide Aufrufe zurückgegeben werden Nothing
FindControl
sollen. Dies NullReferenceException
wird ausgelöst, wenn versucht wird, auf die Eigenschaft des Text
Age
TextBox-Objekts zuzugreifen.
Das Problem besteht darin, dass Control.FindControl
nur die untergeordneten Steuerelements durchsucht werden, die sich im gleichen Benennungscontainer befinden. Da die Gestaltungsvorlage einen neuen Benennungscontainer darstellt, wird durch einen Aufruf Page.FindControl("controlID")
niemals das Gestaltungsvorlagenobjekt ctl00
gemessen. (Verweisen Sie zurück auf Abbildung 4, um die Steuerelementhierarchie anzuzeigen, die das Page
Objekt als übergeordnetes Objekt des Gestaltungsvorlagenobjekts ctl00
anzeigt.) Daher werden bezeichnung Results
und Age
TextBox nicht gefunden und ResultsLabel
zugewiesene AgeTextBox
Werte von Nothing
.
Es gibt zwei Problemumgehungen für diese Herausforderung: Wir können einen Drilldown ausführen, jeweils einen Benennungscontainer, auf das entsprechende Steuerelement; oder wir können unsere eigene FindControl
Methode erstellen, die die Benennung von Containern durchdringt. Lassen Sie uns jede dieser Optionen untersuchen.
Drilldown in den entsprechenden Benennungscontainer
FindControl
Um auf das BezeichnungsResults
- oder Age
TextBox-Element zu verweisen, müssen wir von einem Vorgängersteuerelement im gleichen Benennungscontainer aufrufenFindControl
. Wie in Abbildung 4 gezeigt, ist das MainContent
ContentPlaceHolder-Steuerelement das einzige Vorgängerelement oder Results
Age
innerhalb desselben Benennungscontainers. Mit anderen Worten, das Aufrufen der FindControl
Methode aus dem MainContent
Steuerelement, wie im folgenden Codeausschnitt dargestellt, gibt ordnungsgemäß einen Verweis auf die Results
Oder Age
Steuerelemente zurück.
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
CodeBehind-Klasse unserer Inhaltsseite mithilfe der obigen Syntax arbeiten, da der ContentPlaceHolder in der Gestaltungsvorlage definiert ist. Stattdessen müssen wir verwenden FindControl
, um einen Verweis auf 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, geben Sie Ihr Alter ein, und klicken Sie auf die Schaltfläche "Absenden", eine NullReferenceException
wird ausgelöst. Wenn Sie einen Haltepunkt im SubmitButton_Click
Ereignishandler festlegen, sehen Sie, dass diese Ausnahme auftritt, wenn Sie versuchen, 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 identisch mit den Results
Steuerelementen "Label" und Age
"TextBox": FindControl
Startet die Suche von oben in der Steuerelementhierarchie und dringt nicht in Benennungscontainer ein, aber der MainContent
ContentPlaceHolder befindet sich innerhalb der Gestaltungsvorlage, bei der es sich um einen Benennungscontainer handelt.
Bevor wir einen Verweis MainContent
abrufen könnenFindControl
, benötigen wir zuerst einen Verweis auf das Gestaltungsvorlagensteuerelement. Sobald wir einen Verweis auf die Gestaltungsvorlage haben, können wir einen Verweis auf den MainContent
ContentPlaceHolder über FindControl
und von dort aus Verweise auf das Results
Bezeichnungs- und Age
TextBox-Element abrufen (wieder, über die Verwendung FindControl
). Aber wie erhalten wir einen Verweis auf die Gestaltungsvorlage? Durch Die Überprüfung der id
Attribute im gerenderten Markup ist offensichtlich, dass der Wert der Gestaltungsvorlage ID
lautet ctl00
. Daher könnten Page.FindControl("ctl00")
wir einen Verweis auf die Gestaltungsvorlage abrufen und dann dieses Objekt verwenden, um einen Verweis auf MainContent
usw. abzurufen. 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)
Während dieser Code sicherlich funktioniert, wird davon ausgegangen, dass die automatisch generierte ID
Gestaltungsvorlage immer sein ctl00
wird. Es ist nie eine gute Idee, Annahmen über automatisch generierte Werte zu treffen.
Glücklicherweise kann über die Eigenschaft der Klasse auf die Page
Gestaltungsvorlage Master
verwiesen werden. FindControl("ctl00")
Daher können wir anstelle Page.Master.FindControl("MainContent")
einer Referenz der Gestaltungsvorlage einen Verweis auf die Gestaltungsvorlage abrufen, 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
Dieses Mal wird die Meldung in Results
der Bezeichnung wie erwartet angezeigt, wenn Sie die Seite über einen Browser besuchen, Ihr Alter eingeben und auf die Schaltfläche "Absenden" klicken.
Abbildung 06: Das Alter des Benutzers wird in der Beschriftung angezeigt (Zum Anzeigen des Bilds mit voller Größe klicken)
Rekursives Durchsuchen von Benennungscontainern
Der Grund, warum im vorherigen Codebeispiel auf das MainContent
ContentPlaceHolder-Steuerelement von der Gestaltungsvorlage verwiesen wurde, und dann die Results
Steuerelemente "Label" und Age
"TextBox" aus MainContent
, liegt daran, dass die Control.FindControl
Methode nur im Benennungscontainer von Steuerelement durchsucht wird. Wenn FindControl
Sie innerhalb des Benennungscontainers bleiben, ist in den meisten Szenarien sinnvoll, da zwei Steuerelemente in zwei unterschiedlichen Benennungscontainern möglicherweise dieselben ID
Werte aufweisen. Berücksichtigen Sie den Fall eines GridView-Steuerelements, das ein Label-Websteuerelement definiert, das in einem seiner TemplateFields benannt ProductName
ist. Wenn die Daten zur Laufzeit an gridView gebunden sind, wird für jede GridView-Zeile eine ProductName
Bezeichnung erstellt. Wenn FindControl
wir alle Benennungscontainer durchsucht haben und aufgerufen Page.FindControl("ProductName")
haben, welche Bezeichnungsinstanz sollte die Rückgabe ausgeführt werden FindControl
? Die ProductName
Beschriftung in der ersten GridView-Zeile? Die in der letzten Zeile?
Control.FindControl
Daher ist es sinnvoll, den Benennungscontainer von Control in den meisten Fällen zu durchsuchen. Es gibt jedoch andere Fälle, z. B. die, die uns gegenüberstehen, bei denen wir über alle Benennungscontainer verfügen ID
und vermeiden möchten, dass sie sorgfältig auf jeden Benennungscontainer in der Steuerelementhierarchie verweisen müssen, um auf ein Steuerelement zuzugreifen. FindControl
Eine Variante, die rekursiv alle Benennungscontainer durchsucht, ist auch sinnvoll. Leider enthält .NET Framework keine solche Methode.
Die gute Nachricht ist, dass wir eine eigene FindControl
Methode erstellen können, die rekursiv alle Benennungscontainer durchsucht. Tatsächlich können wir mit Erweiterungsmethoden eine FindControlRecursive
Methode für die Control
Klasse heften, um ihre vorhandene FindControl
Methode zu begleiten.
Hinweis
Erweiterungsmethoden sind ein neues Feature für C# 3.0 und Visual Basic 9. Dabei handelt es sich um die Sprachen, die mit .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 zum Erweitern der Basistypfunktionalität mit Erweiterungsmethoden.
Um die Erweiterungsmethode zu erstellen, fügen Sie dem Ordner mit dem App_Code
Namen eine neue Datei hinzu PageExtensionMethods.vb
. Fügen Sie eine Erweiterungsmethode namens FindControlRecursive
hinzu, die als Eingabe einen String
Parameter mit dem Namen controlID
verwendet. Damit Erweiterungsmethoden ordnungsgemäß funktionieren, ist es wichtig, dass die Klasse als eine Module
gekennzeichnet wird und dass die Erweiterungsmethoden dem <Extension()>
Attribut vorangestellt werden. Darüber hinaus müssen alle Erweiterungsmethoden als ersten Parameter ein Objekt des Typs akzeptieren, auf den die Erweiterungsmethode angewendet wird.
Fügen Sie der PageExtensionMethods.vb
Datei den folgenden Code hinzu, um dies 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
Kehren Sie mit diesem Code an die CodeBehind-Klasse der IDIssues.aspx
Seite zurück, und kommentieren Sie die aktuellen FindControl
Methodenaufrufe aus. Ersetzen Sie sie durch Anrufe an Page.FindControlRecursive("controlID")
. Was die Erweiterungsmethoden beachten, ist, dass sie direkt in den IntelliSense-Dropdownlisten angezeigt werden. Wie in Abbildung 7 dargestellt, wird die FindControlRecursive
Methode zusammen mit den anderen Control
Klassenmethoden in der IntelliSense-Dropdownliste enthalten, wenn Sie den Punkt eingeben Page
und dann den Punkt drücken.
Abbildung 07: Erweiterungsmethoden sind in den IntelliSense-Dropdowns enthalten (Klicken, um das Bild in voller Größe anzuzeigen)
Geben Sie den folgenden Code in den SubmitButton_Click
Ereignishandler ein, und testen Sie ihn, indem Sie die Seite besuchen, Ihr Alter eingeben und auf die Schaltfläche "Absenden" klicken. Wie in Abbildung 6 gezeigt, ist 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 clientseitigem Skript
Wie in der Einführung dieses Lernprogramms erwähnt, wird das gerenderte id
Attribut eines Websteuerelements häufig in clientseitigem Skript verwendet, um programmgesteuert auf ein bestimmtes HTML-Element zu verweisen. Beispielsweise verweist der folgende JavaScript auf ein HTML-Element anhand id
seines Elements und zeigt dann seinen Wert in einem modalen Meldungsfeld an:
var elem = document.getElementById("Age");
if (elem != null)
alert("You entered " + elem.value + " into the Age text box.");
Denken Sie daran, dass in ASP.NET Seiten, die keinen Benennungscontainer enthalten, das Attribut des id
gerenderten HTML-Elements mit dem Eigenschaftswert des Websteuerelements ID
identisch ist. Aus diesem Grund ist es verlockend, code in Attributwerten in id
JavaScript-Code hart zu codieren. Das heißt, wenn Sie wissen, dass Sie über ein clientseitiges Skript auf das Age
TextBox-Websteuerelement zugreifen möchten, führen Sie dies über einen Aufruf von document.getElementById("Age")
.
Das Problem bei diesem Ansatz besteht darin, dass der gerenderte HTML-Code id
bei Verwendung von Gestaltungsvorlagen (oder anderen Benennungscontainersteuerelementen) nicht gleichbedeutend mit der Eigenschaft des Websteuerelements ID
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 einfügen, um getElementById
auf das HTML-Element zuzugreifen, mit dem Sie über clientseitige Skripts 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, wodurch der JavaScript-Code abgebrochen wird.
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
in clientseitigem Skript verwendet wird. Wenn Sie beispielsweise der Seite eine JavaScript-Funktion hinzufügen möchten, die, wenn sie aufgerufen wird, den Wert des Age
TextBox-Steuerelements in einem modalen Meldungsfeld anzeigt, fügen Sie dem Page_Load
Ereignishandler den 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 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 innerhalb ctl00_MainContent_Age
des Aufrufs getElementById
angezeigt wird. Da dieser Wert zur Laufzeit berechnet wird, funktioniert er unabhängig von späteren Änderungen an der Seitensteuerelementhierarchie.
Hinweis
In diesem JavaScript-Beispiel wird lediglich gezeigt, wie eine JavaScript-Funktion hinzugefügt wird, die korrekt auf das HTML-Element verweist, das von einem Serversteuerelement gerendert wird. 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 transpiriert. Weitere Informationen zu diesen und verwandten Themen finden Sie unter "Arbeiten mit clientseitigem Skript".
Zusammenfassung
Bestimmte ASP.NET Serversteuerelemente fungieren als Benennungscontainer, die sich auf die gerenderten id
Attributwerte ihrer untergeordneten Steuerelemente sowie den Bereich der Steuerelemente auswirken, die von der FindControl
Methode verwendet werden. Im Hinblick auf Gestaltungsvorlagen benennen sowohl die Gestaltungsvorlage selbst als auch die zugehörigen ContentPlaceHolder-Steuerelemente Container. Daher müssen wir etwas mehr Arbeit machen, um programmgesteuert auf Steuerelemente innerhalb der Inhaltsseite FindControl
zu verweisen. In diesem Lernprogramm haben wir zwei Techniken untersucht: Drilldown in das ContentPlaceHolder-Steuerelement und Aufrufen seiner FindControl
Methode; und das Rollieren unserer eigenen FindControl
Implementierung, die rekursiv durch alle Benennungscontainer durchsucht.
Neben den serverseitigen Problemen bei der Benennung von Containern in Bezug auf das Verweisen auf Websteuerelemente gibt es auch clientseitige Probleme. Wenn keine Container benannt werden, ist der Eigenschaftswert des Websteuerelements ID
und der gerenderte id
Attributwert gleich. Mit dem Hinzufügen des Namenscontainers enthält das gerenderte id
Attribut jedoch sowohl die ID
Werte des Websteuerelements als auch die Benennungscontainer in der Herkunft der Steuerelementhierarchie. Diese Namensbedenken sind ein Nichtproblem, solange Sie die Eigenschaft des Websteuerelements ClientID
verwenden, um den gerenderten id
Attributwert in Ihrem clientseitigen Skript zu ermitteln.
Glückliche Programmierung!
Weitere nützliche Informationen
Weitere Informationen zu den in diesem Lernprogramm erläuterten Themen finden Sie in den folgenden Ressourcen:
- ASP.NET Gestaltungsvorlagen und
FindControl
- Erstellen dynamischer Dateneingabebenutzeroberflächen
- Vorgehensweise: Verweisen auf ASP.NET Gestaltungsvorlageninhalt
- Mater-Seiten: Tipps, Tricks und Fallen
- Arbeiten mit clientseitigem Skript
Zum Autor
Scott Mitchell, Autor mehrerer ASP/ASP.NET Bücher und Gründer von 4GuysFromRolla.com, arbeitet seit 1998 mit Microsoft Web Technologies zusammen. Scott arbeitet als unabhängiger Berater, Trainer und Schriftsteller. Sein neuestes Buch ist Sams Teach Yourself ASP.NET 3.5 in 24 Stunden. Scott kann über seinen Blog unter mitchell@4GuysFromRolla.com oder über seinen Blog erreicht http://ScottOnWriting.NETwerden.
Besonderer Dank an
Diese Lernprogrammreihe wurde von vielen hilfreichen Prüfern überprüft. Leitende Prüfer für dieses Lernprogramm waren Zack Jones und Suchi Barnerjee. Möchten Sie meine bevorstehenden MSDN-Artikel überprüfen? Wenn dies der Fall ist, legen Sie mir eine Zeile bei mitchell@4GuysFromRolla.com.