Benennung von Steuerelement-IDs auf Inhaltsseiten (C#)
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 Problemumgehungen. Erläutert auch, 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. Aus diesem Grund können Sie davon ausgehen, dass ein ASP.NET-Serversteuerelement in HTML gerendert wird, 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-Steuerelement mit einem Label-Websteuerelement mit dem ID
Wert ProductName enthält. Wenn die GridView zur Laufzeit an ihre 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 Namenscontainer bezeichnet werden. Ein Benennungscontainer dient als neuer ID
Namespace. Alle Serversteuerelemente, die im Benennungscontainer angezeigt werden, haben ihren gerenderten id
Wert mit dem ID
des Namenscontainersteuerelements vorangestellt. Die -Klasse und GridViewRow
die GridView
-Klasse sind z. B. beide Namenscontainer. Folglich erhält ein label-Steuerelement, das in einem GridView TemplateField mit ID
ProductName definiert ist, den 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 Namenscontainer fungieren soll. Die INamingContainer
Schnittstelle beschreib keine Methoden, die das Serversteuerelement implementieren muss, sondern als Marker. Wenn beim Generieren des gerenderten Markups ein Steuerelement diese Schnittstelle implementiert, stellt das ASP.NET-Modul seinen ID
Wert automatisch den gerenderten id
Attributwerten seiner Untergeordneten voran. Dieser Prozess wird in Schritt 2 ausführlicher erläutert.
Benennungscontainer ändern nicht nur den gerenderten id
Attributwert, sondern wirken sich auch darauf aus, wie programmgesteuert von der 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. Durchdringt FindControl
jedoch nicht die Benennung von Containern. Daher können Sie die Page.FindControl
-Methode nicht direkt verwenden, um auf Steuerelemente in einem GridView-Container oder einem anderen Benennungscontainer zu verweisen.
Wie Sie vielleicht vermutet haben, werden master Seiten und ContentPlaceHolders als Benennungscontainer implementiert. In diesem Tutorial untersuchen wir, wie sich master Seiten auf HTML-Elementwerte id
auswirken und wie Sie programmgesteuert auf Websteuerelemente innerhalb einer Inhaltsseite mit FindControl
verweisen.
Schritt 1: Hinzufügen einer neuen ASP.NET Seite
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 Standardinhalt der master Seite ausgegeben, wenn kein Content-Steuerelement 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="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeFile="IDIssues.aspx.cs" 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:
public partial class IDIssues : BasePage
{
}
Aktualisieren Sie abschließend 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 und url
auf "Steuerungs-ID-Benennungsprobleme" bzw ~/IDIssues.aspx
. fest. Nachdem Sie diese Ergänzung gemacht haben, sollte das Markup Ihrer 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 Abbildung 2 veranschaulicht, wird der neue Websiteübersichtseintrag in Web.sitemap
sofort im Abschnitt Lektionen in der linken Spalte widerspiegelt.
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 Sie 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-Websteuerelement und ein Label-Websteuerelement hinzu. Legen Sie die Eigenschaften und Columns
des Textfelds ID
auf Age
bzw. 3 fest. Legen Sie die Eigenschaften und der Schaltfläche Text
auf "Submit" und SubmitButton
fest.ID
Löschen Sie die Label-Eigenschaft Text
, und legen Sie sie ID
auf fest Results
.
An diesem Punkt sollte das deklarative Markup Ihres Content-Steuerelements 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: ein Textfeld, eine Schaltfläche und eine Bezeichnung (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 Websteuerelemente TextBox, Button und Label eine Kombination aus den ID
Werten der Websteuerelemente und den ID
Werten 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 Tutorial erwähnt, dienen sowohl die master Seite 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 TextBox-Steuerelements ID
war 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 Benennungscontainer (knoten mit einer Pfirsichfarbe) dem aktuellen Gerenderten id
das Präfix des Benennungscontainers 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 möglicherweise, wie dieser ID
Wert entstanden ist. Wir haben es 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, wie die master Seite selbst, sind im deklarativen Markup nicht definiert. Daher 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 verwendet das Benennungsmuster ctlXX
, wobei XX ein sequenziell steigender ganzzahliger Wert ist.
Da die master Seite selbst als Namenscontainer dient, weisen die in der master Seite definierten Websteuerelemente 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 ID
(ctl00
) als auch den ID
Wert des Label Web-Steuerelements (DateDisplay
) enthält.
Schritt 3: Programmgesteuertes Verweisen auf Websteuerelemente überFindControl
Jedes ASP.NET-Serversteuerelements enthält eine FindControl("controlID")
Methode, die die absteigenden Elemente 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 null
.
FindControl
ist nützlich in Szenarien, in denen Sie auf ein Steuerelement zugreifen müssen, aber keinen direkten Verweis darauf haben. Bei der Arbeit mit Datenwebsteuerelementen wie gridView 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 verfügen nicht über einen direkten Verweis aus der Code-Behind-Klasse. Daher müssen wir verwenden FindControl
, um programmgesteuert mit einem bestimmten Steuerelement in den GridView-Feldern zu arbeiten. (Weitere Informationen zur Verwendung FindControl
für den Zugriff auf die Steuerelemente in den Vorlagen eines Datenwebsteuerelements finden Sie unter Benutzerdefinierte Formatierung basierend auf Daten.) Das gleiche Szenario tritt 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.
Erstellen Sie einen Ereignishandler für das Ereignis des -EreignissesClick
, um die FindControl
Verwendung der SubmitButton
-Methode zum Suchen nach Steuerelementen innerhalb einer Inhaltsseite zu veranschaulichen. 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 Meldung in Results
anzeigt.
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 geschieht, wenn von FindControl
einer Inhaltsseite aus verwendet wird.
protected void SubmitButton_Click(object sender, EventArgs e)
{
Label ResultsLabel = FindControl("Results") as Label;
TextBox AgeTextBox = Page.FindControl("Age") as TextBox;
ResultsLabel.Text = string.Format("You are {0} years old!", AgeTextBox.Text);
}
Während sich die Zum Aufrufen der FindControl
-Methode verwendete Syntax in den ersten beiden Zeilen von 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 Page
-Klasse ein, von der alle ASP.NET CodeBehind-Klassen abgeleitet werden müssen. Daher entspricht das Aufrufen FindControl("controlID")
von 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 "Absenden". Beim Klicken auf die Schaltfläche "Absenden" wird ein NullReferenceException
ausgelöst (siehe Abbildung 5).
Abbildung 05: Ein NullReferenceException
wird ausgelöst (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Wenn Sie einen Haltepunkt im SubmitButton_Click
Ereignishandler festlegen, sehen Sie, dass beide Aufrufe FindControl
einen null
Wert zurückgeben. Wird NullReferenceException
ausgelöst, wenn versucht wird, auf die Age
Eigenschaft von Text
TextBox zuzugreifen.
Das Problem besteht darin, dass Control.FindControl
nur die Nachfolger von Control durchsucht werden, die sich im gleichen Namenscontainer befinden. Da die seite master einen neuen Namenscontainer darstellt, durchdringt ein Aufruf von Page.FindControl("controlID")
niemals das master page-Objektctl00
. (Siehe Abbildung 4, um die Steuerelementhierarchie anzuzeigen, die das Page
-Objekt als übergeordnetes Objekt des master-Seitenobjekts ctl00
zeigt.) Daher werden label Results
und Age
TextBox nicht gefunden, und ResultsLabel
und AgeTextBox
sind zugewiesene Werte von null
.
Es gibt zwei Problemumgehungen für diese Herausforderung: Wir können einen Drilldown ausführen, jeweils ein Benennungscontainer für das entsprechende Steuerelement; Oder wir können eine eigene FindControl
Methode erstellen, die Benennungscontainer durchdringt. Sehen wir uns die einzelnen Optionen an.
Drillen in den geeigneten Namenscontainer
Um mit auf FindControl
label Results
oder Age
TextBox zu verweisen, müssen wir von einem Vorgängersteuerelement im selben Namenscontainer aufrufen FindControl
. Wie in Abbildung 4 gezeigt, ist das MainContent
ContentPlaceHolder-Steuerelement der einzige Vorgänger von Results
oder Age
, der sich innerhalb desselben Namenscontainers befindet. Anders ausgedrückt: Der Aufruf der FindControl
-Methode aus dem MainContent
-Steuerelement, wie im codeausschnitt unten gezeigt, gibt ordnungsgemäß einen Verweis auf das - oder Age
-Results
Steuerelement zurück.
Label ResultsLabel = MainContent.FindControl("Results") as Label;
TextBox AgeTextBox = MainContent.FindControl("Age") as TextBox;
Wir können jedoch nicht mit dem ContentPlaceHolder aus der MainContent
CodeBehind-Klasse unserer Inhaltsseite mithilfe der obigen Syntax arbeiten, da der ContentPlaceHolder auf der seite master 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 void SubmitButton_Click(object sender, EventArgs e)
{
ContentPlaceHolder MainContent = FindControl("MainContent") as ContentPlaceHolder;
Label ResultsLabel = MainContent.FindControl("Results") as Label;
TextBox AgeTextBox = MainContent.FindControl("Age") as TextBox;
ResultsLabel.Text = string.Format("You are {0} years old!", AgeTextBox.Text);
}
Wenn Sie die Seite über einen Browser besuchen, Ihr Alter eingeben und auf die Schaltfläche "Absenden" klicken, wird ein NullReferenceException
ausgelöst. Wenn Sie einen Haltepunkt im SubmitButton_Click
Ereignishandler festlegen, sehen Sie, dass diese Ausnahme auftritt, wenn Sie versuchen, die MainContent
-Methode des FindControl
Objekts aufzurufen. Das MainContent
-Objekt liegt null
daran, dass die FindControl
-Methode kein Objekt namens "MainContent" finden kann. Der zugrunde liegende Grund ist derselbe wie bei den Results
Label- und Age
TextBox-Steuerelementen: 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 Namenscontainer handelt.
Bevor wir verwenden FindControl
können, um einen Verweis auf abzurufenMainContent
, benötigen wir zunächst einen Verweis auf das master-Seitensteuerelement. 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 Results
Bezeichnung und Age
TextBox (wiederum mithilfe von FindControl
) abrufen. Aber wie erhalten wir einen Verweis auf die master Seite? Wenn Sie die id
Attribute im gerenderten Markup überprüfen, wird deutlich, 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 abzurufen MainContent
usw. Der folgende Codeausschnitt veranschaulicht diese Logik:
// Get a reference to the master page
MasterPage ctl00 = FindControl("ctl00") as MasterPage;
// Get a reference to the ContentPlaceHolder
ContentPlaceHolder MainContent = ctl00.FindControl("MainContent") as ContentPlaceHolder;
// Reference the Label and TextBox controls
Label ResultsLabel = MainContent.FindControl("Results") as Label;
TextBox AgeTextBox = MainContent.FindControl("Age") as 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. Anstatt also verwenden zu müssenFindControl("ctl00")
, um einen Verweis auf die master Seite abzurufen, um auf den MainContent
ContentPlaceHolder zuzugreifen, können wir stattdessen verwendenPage.Master.FindControl("MainContent")
. Aktualisieren Sie den SubmitButton_Click
Ereignishandler mit dem folgenden Code:
protected void SubmitButton_Click(object sender, EventArgs e)
{
ContentPlaceHolder MainContent = Page.Master.FindControl("MainContent") as ContentPlaceHolder;
Label ResultsLabel = MainContent.FindControl("Results") as Label;
TextBox AgeTextBox = MainContent.FindControl("Age") as TextBox;
ResultsLabel.Text = string.Format("You are {0} years old!", AgeTextBox.Text);
}
Wenn Sie die Seite dieses Mal über einen Browser besuchen, Ihr Alter eingeben und auf die Schaltfläche "Senden" klicken, wird die Meldung wie erwartet im Results
Label angezeigt.
Abbildung 06: Das Alter des Benutzers wird in der Bezeichnung angezeigt (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Rekursives Durchsuchen von Benennungscontainern
Der Grund, warum im vorherigen Codebeispiel auf das MainContent
ContentPlaceHolder-Steuerelement von der seite master und dann auf die Results
Label- und Age
TextBox-Steuerelemente von MainContent
verwiesen wurde, liegt darin, dass die Control.FindControl
-Methode nur im Namenscontainer von Control durchsucht. In FindControl
den meisten Szenarien ist es sinnvoll, innerhalb des Namenscontainers zu bleiben, da zwei Steuerelemente in zwei unterschiedlichen Benennungscontainern möglicherweise dieselben ID
Werte haben. Betrachten Sie den Fall eines GridView-Steuerelements, das ein Label Web-Steuerelement namens ProductName
in einem seiner TemplateFields definiert. Wenn die Daten zur Laufzeit an gridView gebunden sind, wird für jede GridView-Zeile eine ProductName
Bezeichnung erstellt. Welche Bezeichnungs-instance sollte FindControl
zurückgegeben werden, wenn FindControl
alle Namenscontainer durchsucht und wir aufgerufen habenPage.FindControl("ProductName")
? 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, der uns gegenübersteht, in denen wir über einen eindeutigen ID
für alle Benennungscontainer verfügen und vermeiden möchten, dass sie sorgfältig auf jeden Benennungscontainer in der Steuerungshierarchie verweisen müssen, um auf ein Steuerelement zuzugreifen. Auch eine FindControl
Variante, die alle Namenscontainer rekursiv durchsucht, ist sinnvoll. Leider enthält die .NET Framework keine solche Methode.
Die gute Nachricht ist, dass wir eine eigene FindControl
Methode erstellen können, die rekursiv alle Namenscontainer durchsucht. Tatsächlich können wir eine Methode mithilfe von Erweiterungsmethoden an FindControlRecursive
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 im Lieferumfang der .NET Framework Version 3.5 und Visual Studio 2008 enthalten sind. 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.cs
hinzu. Fügen Sie eine Erweiterungsmethode namens FindControlRecursive
hinzu, die einen string
Parameter namens controlID
als Eingabe akzeptiert. Damit Erweiterungsmethoden ordnungsgemäß funktionieren, ist es wichtig, dass die Klasse selbst und ihre Erweiterungsmethoden gekennzeichnet static
sind. Darüber hinaus müssen alle Erweiterungsmethoden als ersten Parameter ein Objekt des Typs akzeptieren, auf den die Erweiterungsmethode angewendet wird, und diesem Eingabeparameter muss der Schlüsselwort (keyword) this
vorangestellt werden.
Fügen Sie der Klassendatei den PageExtensionMethods.cs
folgenden Code hinzu, um diese Klasse und die FindControlRecursive
Erweiterungsmethode zu definieren:
using System;
using System.Web;
using System.Web.UI;
public static class PageExtensionMethods
{
public static Control FindControlRecursive(this Control ctrl, string controlID)
{
if (string.Compare(ctrl.ID, controlID, true) == 0)
{
// We found the control!
return ctrl;
}
else
{
// Recurse through ctrl's Controls collections
foreach (Control child in ctrl.Controls)
{
Control lookFor = FindControlRecursive(child, controlID);
if (lookFor != null)
return lookFor; // We found the control
}
// If we reach here, control was not found
return null;
}
}
}
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, wird die Methode zusammen mit den FindControlRecursive
anderen Control
Klassenmethoden in der IntelliSense-Dropdownliste enthalten, wenn Sie Page eingeben und dann auf den Punkt treffen.
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 "Senden" klicken. Wie in Abbildung 6 gezeigt, ist die resultierende Ausgabe die Meldung "Sie sind älter!"
protected void SubmitButton_Click(object sender, EventArgs e)
{
Label ResultsLabel = Page.FindControlRecursive("Results") as Label;
TextBox AgeTextBox = Page.FindControlRecursive("Age") as TextBox;
ResultsLabel.Text = string.Format("You are {0} years old!", AgeTextBox.Text);
}
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 dieses Tutorials erwähnt, wird das gerenderte id
Attribut eines Websteuerelements häufig in clientseitigen Skripts verwendet, um programmgesteuert auf ein bestimmtes HTML-Element zu verweisen. Der folgende JavaScript-Code verweist beispielsweise auf ein HTML-Element anhand seiner id
und zeigt dann dessen Wert in einem modalem Meldungsfeld an:
var elem = document.getElementById("Age");
if (elem != null)
alert("You entered " + elem.value + " into the Age text box.");
Beachten Sie, dass in ASP.NET Seiten, die keinen Namenscontainer enthalten, das Attribut des id
gerenderten HTML-Elements mit dem Eigenschaftswert des Websteuerelements ID
identisch ist. Aus diesem Grund ist es verlockend, Attributwerte in id
JavaScript-Code hart zu codieren. Wenn Sie also wissen, dass Sie über ein clientseitiges Skript auf das Age
TextBox-Websteuerelement zugreifen möchten, können Sie dies über einen Aufruf von ausführen document.getElementById("Age")
.
Das Problem bei diesem Ansatz besteht darin, dass bei Verwendung master Seiten (oder anderer Namenscontainersteuerelemente) der gerenderte HTML-Code id
nicht mit der -Eigenschaft des Websteuerelements ID
synonym 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
über clientseitiges Skript auf das HTML-Element zuzugreifen, mit dem Sie arbeiten müssen. Dieser Ansatz ist nicht 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 Attributwert, der id
gerendert wird, 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. Wenn Sie z. B. eine JavaScript-Funktion zu der Seite hinzufügen möchten, die beim Aufruf den Wert des Age
TextBox-Objekts in einem modalen Meldungsfeld anzeigt, fügen Sie dem Ereignishandler den Page_Load
folgenden Code hinzu:
ClientScript.RegisterClientScriptBlock(this.GetType(), "ShowAgeTextBoxScript",
string.Format(@"function ShowAge()
{{
var elem = document.getElementById('{0}');
if (elem != null)
alert('You entered ' + elem.value + ' into the Age text box.');
}}", AgeTextBox.ClientID), true);
Der obige Code fügt den Wert der ClientID-Eigenschaft von Age
TextBox in den JavaScript-Aufruf von ein getElementById
. Wenn Sie diese Seite über einen Browser aufrufen 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, dass 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
In diesem JavaScript-Beispiel wird lediglich gezeigt, 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.