Freigeben über


Benennung von Steuerelement-IDs auf Inhaltsseiten (C#)

von Scott Mitchell

PDF herunterladen

Veranschaulicht, wie ContentPlaceHolder-Steuerelemente als Namenscontainer dienen und daher das programmgesteuerte Arbeiten mit einem Steuerelement (über FindControl) erschweren. Untersucht dieses Problem und 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 FindControlverweisen.

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.

Hinzufügen der Inhaltsseite IDIssues.aspx zum Stammordner

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

Visual Studio erstellt automatisch ein Content-Steuerelement für jeden der vier ContentPlaceHolder der master Seite. Wie im Tutorial Multiple ContentPlaceHolders and Default Content erwähnt, wird stattdessen der 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.

Der Abschnitt

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

Schritt 2: Untersuchen der gerendertenIDÄnderungen

Um besser zu verstehen, welche Änderungen die ASP.NET Engine an den gerenderten id Werten von Serversteuerelementen vornimmt, fügen wir der IDIssues.aspx Seite einige Websteuerelemente hinzu, und zeigen 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 SubmitButtonfest.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.

Die Seite enthält drei Websteuerelemente: TextBox, Schaltfläche und Bezeichnung

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.

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

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

Hinweis

Wie bereits erwähnt, stellt der ctl00 Teil des gerenderten id Attributs den ID Wert der master Seite dar, aber Sie fragen sich 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.masterangegeben. Das Age TextBox-Markup wurde definiert IDIssues.aspx. Wir können die ID Werte für diese Typen von Steuerelementen über die Eigenschaftenfenster oder über die deklarative Syntax angeben. Andere Steuerelemente, 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_Clickgeringfü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).

Eine NullReferenceException wird ausgelöst.

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 ctl00zeigt.) 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 -ResultsSteuerelement 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 MainContentusw. 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 ctl00wird. Es ist nie eine gute Idee, Annahmen über automatisch generierte Werte zu treffen.

Glücklicherweise ist ein Verweis auf die Seite master über die -Eigenschaft der Page -Klasse Master zugänglich. 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.

Das Alter des Benutzers wird in der Bezeichnung 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 MainContentverwiesen 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.cshinzu. Fügen Sie eine Erweiterungsmethode namens FindControlRecursive hinzu, die einen string Parameter namens controlIDals Eingabe akzeptiert. Damit Erweiterungsmethoden ordnungsgemäß funktionieren, ist es wichtig, dass die Klasse selbst und ihre Erweiterungsmethoden gekennzeichnet staticsind. 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) thisvorangestellt 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.

Erweiterungsmethoden sind in den IntelliSense-Dropdowns 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 "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 richtigenidAttributwerts 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 ClientIDzugegriffen 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 getElementByIdangezeigt 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 FindControlausführen. In diesem Tutorial haben wir zwei Techniken untersucht: Bohren in das ContentPlaceHolder-Steuerelement und aufrufen deren FindControl Methode; und Rollieren unserer eigenen FindControl Implementierung, die alle Benennungscontainer rekursiv durchsucht.

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

Viel Spaß beim Programmieren!

Weitere Informationen

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

Zum Autor

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

Besonderen Dank an

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