Nazewnictwo identyfikatorów kontrolek na stronach zawartości (C#)

Autor : Scott Mitchell

Pobierz plik PDF

Ilustruje, w jaki sposób kontrolki ContentPlaceHolder służą jako kontener nazewnictwa i w związku z tym sprawiają, że praca programowo z kontrolką jest trudna (za pośrednictwem kontrolki FindControl). Przyjrzyj się temu problemowi i obejściom. Omówiono również sposób programowego uzyskiwania dostępu do wynikowej wartości ClientID.

Wprowadzenie

Wszystkie kontrolki serwera ASP.NET obejmują ID właściwość, która jednoznacznie identyfikuje kontrolkę i jest środkiem, za pomocą którego kontrolka jest programowo dostępna w klasie za kodem. Podobnie elementy w dokumencie HTML mogą zawierać id atrybut, który jednoznacznie identyfikuje element. Te id wartości są często używane w skry skryptach po stronie klienta do programowego odwoływanie się do określonego elementu HTML. W związku z tym można założyć, że gdy kontrolka serwera ASP.NET jest renderowana w kodzie HTML, jego ID wartość jest używana jako id wartość renderowanego elementu HTML. Niekoniecznie tak jest, ponieważ w pewnych okolicznościach pojedyncza kontrolka z pojedynczą ID wartością może pojawiać się wiele razy w renderowanej adiustacji. Rozważmy kontrolkę GridView zawierającą pole szablonu z kontrolką Sieci Web Etykieta z wartością ID ProductName. Gdy element GridView jest powiązany ze źródłem danych w czasie wykonywania, ta etykieta jest powtarzana raz dla każdego wiersza kontrolki GridView. Każda renderowana etykieta wymaga unikatowej id wartości.

Aby obsłużyć takie scenariusze, ASP.NET umożliwia oznaczanie niektórych kontrolek jako kontenerów nazewnictwa. Kontener nazewnictwa służy jako nowa ID przestrzeń nazw. Wszystkie kontrolki serwera wyświetlane w kontenerze nazewnictwa mają ich wartość renderowaną id z prefiksem ID kontrolki kontenera nazewnictwa. Na przykład GridView klasy i GridViewRow to kontenery nazewnictwa. W związku z tym kontrolka Etykieta zdefiniowana w kontrolce GridView TemplateField z wartością ID ProductName otrzymuje wyrenderowaną id wartość GridViewID_GridViewRowID_ProductName. Ponieważ GridViewRowID jest unikatowy dla każdego wiersza GridView, wynikowe id wartości są unikatowe.

Uwaga

Interfejs INamingContainer służy do wskazywania, że określony ASP.NET kontroli serwera powinien działać jako kontener nazewnictwa. Interfejs INamingContainer nie określa żadnych metod, które musi zaimplementować kontrolka serwera, a raczej jest używany jako znacznik. Podczas generowania renderowanych znaczników, jeśli kontrolka implementuje ten interfejs, aparat ASP.NET automatycznie prefiksuje jego ID wartość do wartości atrybutów renderowanych id przez malejących. Ten proces został omówiony bardziej szczegółowo w kroku 2.

Nazewnictwo kontenerów nie tylko zmienia wartość renderowanego atrybutu, ale także wpływa na sposób, w jaki kontrolka może być programowo przywoływane id z klasy kodowej strony ASP.NET. Metoda FindControl("controlID") jest często używana do programowego odwoływanie się do kontrolki sieci Web. FindControl Jednak nie przeniknie przez kontenery nazewnictwa. W związku z tym nie można bezpośrednio użyć Page.FindControl metody do odwoływanie się do kontrolek w kontrolce GridView lub innym kontenerze nazewnictwa.

Jak mogło się wydawać, strony wzorcowe i ContentPlaceHolders są implementowane jako kontenery nazewnictwa. W tym samouczku sprawdzimy, jak strony wzorcowe wpływają na wartości elementów id HTML i sposoby programowego odwoływanie się do kontrolek sieci Web na stronie zawartości przy użyciu polecenia FindControl.

Krok 1. Dodawanie nowej strony ASP.NET

Aby zademonstrować koncepcje omówione w tym samouczku, dodajmy nową stronę ASP.NET do naszej witryny internetowej. Utwórz nową stronę zawartości o nazwie IDIssues.aspx w folderze głównym, wiążąc ją ze stroną wzorcową Site.master .

Dodawanie IDIssues.aspx strony zawartości do folderu głównego

Rysunek 01. Dodawanie strony IDIssues.aspx zawartości do folderu głównego

Program Visual Studio automatycznie tworzy kontrolkę Zawartość dla każdego z czterech symboli ContentPlaceHolder strony wzorcowej. Jak wspomniano w samouczku Multiple ContentPlaceHolders and Default Content (Wiele symboli zawartości i zawartości domyślnej ), zamiast tego jest emitowana domyślna zawartość contentPlaceHolder strony wzorcowej. Ponieważ symbole QuickLoginUI i LeftColumnContent ContentPlaceHolder zawierają odpowiednie domyślne znaczniki dla tej strony, przejdź do przodu i usuń odpowiednie kontrolki Zawartości z .IDIssues.aspx Na tym etapie znaczniki deklaratywne strony zawartości powinny wyglądać następująco:

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

W samouczku Określanie tytułu, tagów meta i innych nagłówków HTML na stronie wzorcowej utworzyliśmy niestandardową klasę strony podstawowej (BasePage), która automatycznie konfiguruje tytuł strony, jeśli nie jest jawnie ustawiona. IDIssues.aspx Aby strona używała tej funkcji, klasa kodu strony musi pochodzić z BasePage klasy (zamiast System.Web.UI.Page). Zmodyfikuj definicję klasy stojącej za kodem, tak aby wyglądała następująco:

public partial class IDIssues : BasePage
{
}

Na koniec zaktualizuj Web.sitemap plik, aby uwzględnić wpis dla tej nowej lekcji. <siteMapNode> Dodaj element i ustaw jego title atrybuty i url odpowiednio na "Problemy z nazewnictwem identyfikatora sterowania" i ~/IDIssues.aspx. Po dodaniu znaczniki Web.sitemap pliku powinny wyglądać podobnie do następujących:

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

Jak pokazano na rysunku 2, nowy wpis mapy witryny zostanie Web.sitemap natychmiast odzwierciedlony w sekcji Lekcje w lewej kolumnie.

Sekcja Lekcje zawiera teraz link do sekcji

Rysunek 02. Sekcja lekcji zawiera teraz link do sekcji "Problemy z nazewnictwem identyfikatora sterowania"

Krok 2. Badanie renderowanychIDzmian

Aby lepiej zrozumieć modyfikacje wprowadzone przez aparat ASP.NET do renderowanych wartości kontrolek serwera, dodajmy kilka kontrolek sieci Web do IDIssues.aspx strony, a następnie wyświetl renderowane id znaczniki wysyłane do przeglądarki. W szczególności wpisz tekst "Wprowadź wiek:", po którym następuje kontrolka TextBox Web. Dalej na stronie dodaj kontrolkę Sieć Web przycisku i kontrolkę Etykieta w sieci Web. Ustaw odpowiednio właściwości i Columns pola TextBox ID na Age i 3. Ustaw właściwości i ID przycisku Text na wartość "Prześlij" i SubmitButton. Wyczyść właściwość Label Text i ustaw jej ID wartość na Results.

W tym momencie znacznik deklaratywny kontrolki zawartości powinien wyglądać podobnie do następującego:

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

Rysunek 3 przedstawia stronę wyświetlaną za pośrednictwem projektanta programu Visual Studio.

Strona zawiera trzy kontrolki internetowe: pole tekstowe, przycisk i etykietę

Rysunek 03. Strona zawiera trzy kontrolki sieci Web: pole tekstowe, przycisk i etykietę (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Odwiedź stronę za pośrednictwem przeglądarki, a następnie wyświetl źródło HTML. Jak pokazano w poniższym znaczniku, id wartości elementów HTML kontrolek TextBox, Button i Label Web są kombinacją ID wartości kontrolek Sieci Web i ID wartości kontenerów nazewnictwa na stronie.

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

Jak wspomniano wcześniej w tym samouczku, zarówno strona wzorcowa, jak i jej elementy ContentPlaceHolders służą jako kontenery nazewnictwa. W związku z tym oba te elementy przyczyniają się do renderowania ID wartości zagnieżdżonych kontrolek. Weź atrybut TextBox id , na przykład: ctl00_MainContent_Age. Pamiętaj, że wartość kontrolki ID TextBox to Age. Jest to poprzedzone wartością kontrolki ID ContentPlaceHolder. MainContent Ponadto ta wartość jest poprzedzona wartością strony wzorcowej ID . ctl00 Efekt net jest wartością atrybutu składającą id się z ID wartości strony wzorcowej, kontrolki ContentPlaceHolder i samej kontrolki TextBox.

Rysunek 4 ilustruje to zachowanie. Aby określić renderowane id kontrolki Age TextBox, zacznij od ID wartości kontrolki TextBox. Age Następnie przejmij drogę do hierarchii sterowania. W każdym kontenerze nazewnictwa (węzły o kolorze brzoskwiniowym) prefiks bieżący renderowany id za pomocą kontenera idnazewnictwa .

Atrybuty identyfikatora renderowania są oparte na wartościach identyfikatorów kontenerów nazewnictwa

Rysunek 04. Renderowane id atrybuty są oparte na ID wartościach kontenerów nazewnictwa

Uwaga

Jak już wspomniano, ctl00 część renderowanego id atrybutu stanowi ID wartość strony wzorcowej, ale być może zastanawiasz się, jak ta ID wartość się pojawiła. Nie określiliśmy jej w żadnym miejscu na naszej stronie wzorcowej ani zawartości. Większość kontrolek serwera na stronie ASP.NET jest dodawana jawnie za pośrednictwem znaczników deklaratywnych strony. Kontrolka MainContent ContentPlaceHolder została jawnie określona w znacznikach elementu Site.master; Age element TextBox został zdefiniowany IDIssues.aspxjako znaczniki. Możemy określić ID wartości tych typów kontrolek za pośrednictwem okno Właściwości lub składni deklaratywnej. Inne kontrolki, takie jak sama strona wzorcowa, nie są zdefiniowane w znacznikach deklaratywnych. W związku z tym ich ID wartości muszą być generowane automatycznie. Aparat ASP.NET ustawia ID wartości w czasie wykonywania dla tych kontrolek, których identyfikatory nie zostały jawnie ustawione. Używa wzorca ctlXXnazewnictwa , gdzie XX jest sekwencyjnie zwiększającą wartość całkowitą.

Ponieważ sama strona wzorcowa służy jako kontener nazewnictwa, kontrolki sieci Web zdefiniowane na stronie wzorcowej również zmieniły wartości atrybutów renderowanych id . Na przykład etykieta DisplayDate dodana do strony wzorcowej w samouczku Tworzenie układu Site-Wide za pomocą stron wzorcowych zawiera następujące renderowane znaczniki:

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

Należy pamiętać, że id atrybut zawiera zarówno wartość strony wzorcowej ID (ctl00), jak i ID wartość kontrolki Etykieta sieci Web (DateDisplay).

Krok 3. Programowe odwoływanie się do kontrolek sieci Web za pomocą poleceniaFindControl

Każda kontrolka serwera ASP.NET zawiera metodę FindControl("controlID") , która przeszukuje malejące elementy kontrolki dla kontrolki o nazwie controlID. Jeśli taka kontrolka zostanie znaleziona, zostanie zwrócona; Jeśli nie znaleziono pasującej kontrolki, FindControl zwraca wartość null.

FindControl jest przydatna w scenariuszach, w których musisz uzyskać dostęp do kontroli, ale nie masz do niej bezpośredniego odwołania. Podczas pracy z kontrolkami sieci Web danych, takimi jak GridView, na przykład kontrolki w polach kontrolki GridView są definiowane raz w składni deklaratywnej, ale w czasie wykonywania dla każdego wiersza kontrolki GridView jest tworzone wystąpienie kontrolki. W związku z tym kontrolki generowane w czasie wykonywania istnieją, ale nie mamy bezpośredniego odwołania dostępnego z klasy za pomocą kodu. W związku z tym musimy użyć FindControl polecenia , aby programowo pracować z określoną kontrolką w polach kontrolki GridView. (Aby uzyskać więcej informacji na temat używania FindControl funkcji do uzyskiwania dostępu do kontrolek w szablonach kontrolki sieci Web danych, zobacz Niestandardowe formatowanie na podstawie danych). Ten sam scenariusz występuje podczas dynamicznego dodawania kontrolek sieci Web do formularza sieci Web, tematu omówionego w temacie Creating Dynamic Data Entry User Interfaces (Tworzenie dynamicznych interfejsów użytkownika wprowadzania danych).

Aby zilustrować FindControl użycie metody wyszukiwania kontrolek na stronie zawartości, utwórz procedurę obsługi zdarzeń dla SubmitButtonzdarzenia .Click W procedurze obsługi zdarzeń dodaj następujący kod, który programowo odwołuje się do kontrolki Age TextBox i Results Label przy użyciu FindControl metody , a następnie wyświetla komunikat na Results podstawie danych wejściowych użytkownika.

Uwaga

Oczywiście w tym przykładzie nie musimy odwoływać FindControl się do kontrolek Label i TextBox. Możemy odwoływać się do nich bezpośrednio za pośrednictwem ich ID wartości właściwości. Używam FindControl tutaj, aby zilustrować, co się stanie w przypadku korzystania FindControl ze strony zawartości.

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);
}

Chociaż składnia używana do wywołania FindControl metody różni się nieco w dwóch pierwszych wierszach SubmitButton_Click, są one semantycznie równoważne. Pamiętaj, że wszystkie kontrolki serwera ASP.NET obejmują metodę FindControl . Obejmuje to klasę Page , z której muszą pochodzić wszystkie klasy ASP.NET kodu. W związku z tym wywołanie FindControl("controlID") jest równoważne wywołaniu Page.FindControl("controlID")metody , przy założeniu FindControl , że metoda nie jest zastępowana w klasie kodu ani w niestandardowej klasie bazowej.

Po wprowadzeniu tego kodu odwiedź IDIssues.aspx stronę za pośrednictwem przeglądarki, wprowadź wiek i kliknij przycisk "Prześlij". Po kliknięciu przycisku "Prześlij" zostanie zgłoszony element NullReferenceException (zobacz Rysunek 5).

Wyjątek NullReferenceException jest zgłaszany

Rysunek 05. Element jest NullReferenceException podniesiony (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Jeśli ustawisz punkt przerwania w procedurze SubmitButton_Click obsługi zdarzeń, zobaczysz, że oba wywołania nullFindControl zwracają wartość. Obiekt NullReferenceException jest zgłaszany podczas próby uzyskania dostępu Age do właściwości TextBox Text .

Problem polega na tym, że Control.FindControl przeszukuje tylko zstąpień kontrolki, które znajdują się w tym samym kontenerze nazewnictwa. Ponieważ strona wzorcowa stanowi nowy kontener nazewnictwa, wywołanie Page.FindControl("controlID") nigdy nie przenika obiektu ctl00strony wzorcowej . (Wróć do rysunku 4, aby wyświetlić hierarchię sterowania, która pokazuje Page obiekt jako element nadrzędny obiektu ctl00strony wzorcowej). W związku z Results tym nie można odnaleźć etykiety i Age pola tekstowego nulli ResultsLabelAgeTextBox są przypisane wartości .

Istnieją dwa obejścia tego zadania: możemy przejść do szczegółów— jeden kontener nazewnictwa jednocześnie do odpowiedniej kontrolki; lub możemy utworzyć własną FindControl metodę, która przenika kontenery nazewnictwa. Przeanalizujmy każdą z tych opcji.

Przechodzenie do szczegółów odpowiedniego kontenera nazewnictwa

FindControl Aby odwołać się do kontrolki Results Label lub Age TextBox, musimy wywołać FindControl metodę z kontrolki nadrzędnych w tym samym kontenerze nazewnictwa. Jak pokazano na rysunku 4, kontrolka MainContent ContentPlaceHolder jest jedynym elementem nadrzędnym Results obiektu lub Age znajduje się w tym samym kontenerze nazewnictwa. Innymi słowy wywołanie FindControl metody z kontrolki MainContent , jak pokazano w poniższym fragmencie kodu, poprawnie zwraca odwołanie do Results kontrolek lub Age .

Label ResultsLabel = MainContent.FindControl("Results") as Label;
TextBox AgeTextBox = MainContent.FindControl("Age") as TextBox;

Nie możemy jednak pracować z elementem MainContent ContentPlaceHolder z klasy kodowej za naszą stroną zawartości przy użyciu powyższej składni, ponieważ element ContentPlaceHolder jest zdefiniowany na stronie wzorcowej. Zamiast tego musimy użyć FindControl polecenia , aby uzyskać odwołanie do MainContentelementu . Zastąp kod w procedurze SubmitButton_Click obsługi zdarzeń następującymi modyfikacjami:

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);
}

Jeśli odwiedzasz stronę za pośrednictwem przeglądarki, wprowadź swój wiek i kliknij przycisk "Prześlij", NullReferenceException zostanie zgłoszony element . Jeśli ustawisz punkt przerwania w procedurze SubmitButton_Click obsługi zdarzeń, ten wyjątek wystąpi podczas próby wywołania MainContent metody obiektu FindControl . Obiekt MainContent jest null spowodowany tym FindControl , że metoda nie może zlokalizować obiektu o nazwie "MainContent". Podstawowa przyczyna jest taka sama jak w Results przypadku kontrolek Label i Age TextBox: FindControl uruchamia wyszukiwanie w górnej części hierarchii kontrolek i nie przeniknie do kontenerów nazewnictwa, ale MainContent contentPlaceHolder znajduje się na stronie wzorcowej, która jest kontenerem nazewnictwa.

Aby można FindControl było uzyskać odwołanie do MainContentelementu , najpierw potrzebujemy odwołania do kontrolki strony wzorcowej. Po uzyskaniu odwołania do strony wzorcowej możemy uzyskać odwołanie do MainContent elementu ContentPlaceHolder za pośrednictwem metody FindControl i, z tego miejsca, odwołania do Results etykiety i Age kontrolki TextBox (ponownie za pomocą polecenia FindControl). Ale jak uzyskać odwołanie do strony wzorcowej? Sprawdzając id atrybuty w renderowanej adiustacji, widać, że wartość strony wzorcowej ID to ctl00. W związku z tym możemy użyć Page.FindControl("ctl00") metody , aby uzyskać odwołanie do strony wzorcowej, a następnie użyć tego obiektu, aby uzyskać odwołanie do MainContentelementu i tak dalej. Poniższy fragment kodu ilustruje tę 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;

Chociaż ten kod z pewnością zadziała, zakłada się, że automatycznie wygenerowana ID strona wzorcowa będzie zawsze .ctl00 Nigdy nie jest dobrym pomysłem, aby założyć założenia dotyczące automatycznie wygenerowanych wartości.

Na szczęście odwołanie do strony wzorcowej jest dostępne za pośrednictwem Page właściwości klasy Master . W związku z tym zamiast użyć FindControl("ctl00") polecenia , aby uzyskać odwołanie do strony wzorcowej w celu uzyskania dostępu MainContent do elementu ContentPlaceHolder, możemy zamiast tego użyć elementu Page.Master.FindControl("MainContent"). Zaktualizuj program obsługi zdarzeń SubmitButton_Click przy użyciu następującego kodu:

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);
}

Tym razem odwiedzanie strony za pośrednictwem przeglądarki, wprowadzenie wieku i kliknięcie przycisku "Prześlij" powoduje wyświetlenie komunikatu w etykiecie zgodnie z Results oczekiwaniami.

Wiek użytkownika jest wyświetlany w etykiecie

Rysunek 06. Wiek użytkownika jest wyświetlany w etykiecie (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Cykliczne wyszukiwanie za pomocą kontenerów nazewnictwa

Powodem, dla którego poprzedni przykład kodu odwołuje się do MainContent kontrolki ContentPlaceHolder ze strony wzorcowej, a następnie Results kontrolki Label i Age TextBox z MainContentklasy , jest spowodowane tym, że Control.FindControl metoda wyszukuje tylko w kontenerze nazewnictwa kontrolki. Pozostanie FindControl w kontenerze nazewnictwa ma sens w większości scenariuszy, ponieważ dwie kontrolki w dwóch różnych kontenerach nazewnictwa mogą mieć te same ID wartości. Rozważmy przypadek kontrolki GridView definiującej kontrolkę Sieci Web etykiet o nazwie ProductName w ramach jednego z pól szablonów. Gdy dane są powiązane z kontrolką GridView w czasie wykonywania, ProductName dla każdego wiersza GridView jest tworzona etykieta. Jeśli FindControl przeszukano wszystkie kontenery nazewnictwa i wywołaliśmy Page.FindControl("ProductName")metodę , jakie wystąpienie etykiety powinno zostać zwrócone FindControl ? Etykieta ProductName w pierwszym wierszu GridView? Ten w ostatnim wierszu?

Dlatego wyszukiwanie Control.FindControl tylko kontenera nazewnictwa kontrolki ma sens w większości przypadków. Istnieją jednak inne przypadki, takie jak ten, w którym mamy unikatowy identyfikator ID we wszystkich kontenerach nazewnictwa i chcemy uniknąć skrupulatnego odwoływanie się do każdego kontenera nazewnictwa w hierarchii kontroli w celu uzyskania dostępu do kontroli. FindControl Wariant, który rekursywnie wyszukuje wszystkie kontenery nazewnictwa, również ma sens. Niestety, .NET Framework nie zawiera takiej metody.

Dobrą wiadomością jest to, że możemy utworzyć własną FindControl metodę, która cyklicznie przeszukuje wszystkie kontenery nazewnictwa. W rzeczywistości przy użyciu metod rozszerzeń możemy wykorzystać metodę FindControlRecursive do Control klasy, aby towarzyszyć jej istniejącej FindControl metodzie.

Uwaga

Metody rozszerzeń to nowa funkcja dla języków C# 3.0 i Visual Basic 9, które są językami dostarczonymi z .NET Framework w wersji 3.5 i Visual Studio 2008. Krótko mówiąc, metody rozszerzeń umożliwiają deweloperowi utworzenie nowej metody dla istniejącego typu klasy za pomocą specjalnej składni. Aby uzyskać więcej informacji na temat tej przydatnej funkcji, zapoznaj się z artykułem Rozszerzanie funkcji typu podstawowego za pomocą metod rozszerzeń.

Aby utworzyć metodę rozszerzenia, dodaj nowy plik do App_Code folderu o nazwie PageExtensionMethods.cs. Dodaj metodę rozszerzenia o nazwie FindControlRecursive , która przyjmuje jako dane wejściowe string parametr o nazwie controlID. Aby metody rozszerzeń działały prawidłowo, ważne jest, aby sama klasa i jej metody rozszerzenia były oznaczone jako static. Ponadto wszystkie metody rozszerzenia muszą akceptować jako pierwszy parametr obiektu typu, do którego stosuje się metodę rozszerzenia, a ten parametr wejściowy musi być poprzedzony słowem kluczowym this.

Dodaj następujący kod do PageExtensionMethods.cs pliku klasy, aby zdefiniować tę klasę i metodę FindControlRecursive rozszerzenia:

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

Przy użyciu tego kodu wróć do IDIssues.aspx klasy za pomocą kodu strony i oznacz jako komentarz bieżące FindControl wywołania metody. Zastąp je wywołaniami funkcji Page.FindControlRecursive("controlID"). Co dobrze o metodach rozszerzeń jest to, że są one wyświetlane bezpośrednio na listach rozwijanych IntelliSense. Jak pokazano na rysunku 7, po wpisaniu strony, a następnie osiągnięciu okresu, FindControlRecursive metoda jest uwzględniona na liście rozwijanej IntelliSense wraz z innymi Control metodami klasy.

Metody rozszerzeń są uwzględniane na listach rozwijanych funkcji IntelliSense

Rysunek 07. Metody rozszerzenia są uwzględniane w Drop-Downs IntelliSense (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Wprowadź następujący kod w procedurze SubmitButton_Click obsługi zdarzeń, a następnie przetestuj go, odwiedzając stronę, wprowadzając swój wiek i klikając przycisk "Prześlij". Jak pokazano na rysunku 6, wynikowe dane wyjściowe będą komunikatem "Jesteś w wieku lat!"

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);
}

Uwaga

Ponieważ metody rozszerzeń są nowe w językach C# 3.0 i Visual Basic 9, jeśli używasz programu Visual Studio 2005, nie można używać metod rozszerzeń. Zamiast tego należy zaimplementować metodę FindControlRecursive w klasie pomocniczej. Rick Strahl ma taki przykład w swoim wpisie na blogu , ASP.NET Maser Pages i FindControl.

Krok 4. Używanie poprawnejidwartości atrybutu w skryscie Client-Side

Jak wspomniano we wprowadzeniu w tym samouczku, renderowany id atrybut kontrolki sieci Web jest często używany w skry skryptach po stronie klienta do programowego odwoływanie się do określonego elementu HTML. Na przykład poniższy kod JavaScript odwołuje się do elementu HTML według elementu id , a następnie wyświetla jego wartość w modalnym oknie komunikatu:

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

Pamiętaj, że na ASP.NET stronach, które nie zawierają kontenera nazewnictwa, atrybut renderowanego elementu id HTML jest identyczny z wartością właściwości kontrolki ID sieci Web. W związku z tym warto zakodować kod w id wartościach atrybutów w kodzie JavaScript. Oznacza to, że jeśli wiesz, że chcesz uzyskać dostęp do kontrolki Age internetowej TextBox za pośrednictwem skryptu po stronie klienta, wykonaj to za pośrednictwem wywołania metody document.getElementById("Age").

Problem z tym podejściem polega na tym, że w przypadku używania stron wzorcowych (lub innych kontrolek kontenera nazewnictwa) renderowany kod HTML id nie jest synonimem właściwości kontrolki ID Sieci Web. Pierwszym nachyleniem może być odwiedzenie strony za pośrednictwem przeglądarki i wyświetlenie źródła w celu określenia rzeczywistego id atrybutu. Gdy znasz wartość renderowaną id , możesz wkleić ją do wywołania w celu getElementById uzyskania dostępu do elementu HTML, z którym należy pracować za pomocą skryptu po stronie klienta. Takie podejście jest mniej niż idealne, ponieważ niektóre zmiany w hierarchii sterowania strony lub zmiany ID właściwości kontrolek nazewnictwa spowodują zmianę wynikowego id atrybutu, co spowoduje przerwanie kodu JavaScript.

Dobrą wiadomością jest to, że wartość atrybutu id renderowana jest dostępna w kodzie po stronie serwera za pośrednictwem właściwości kontrolki ClientIDsieci Web. Ta właściwość powinna służyć do określania wartości atrybutu używanej id w skry skryptu po stronie klienta. Aby na przykład dodać funkcję JavaScript do strony, która po wywołaniu wyświetla wartość Age kontrolki TextBox w modalnym oknie komunikatu, dodaj następujący kod do Page_Load procedury obsługi zdarzeń:

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

Powyższy kod wprowadza wartość Age właściwości ClientID kontrolki TextBox do wywołania języka JavaScript do getElementByIdmetody . Jeśli odwiedzisz tę stronę za pośrednictwem przeglądarki i wyświetlisz źródło HTML, znajdziesz następujący kod JavaScript:

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

Zwróć uwagę, jak prawidłowa id wartość atrybutu pojawia ctl00_MainContent_Agesię w wywołaniu metody getElementById. Ponieważ ta wartość jest obliczana w czasie wykonywania, działa niezależnie od późniejszych zmian w hierarchii sterowania stronami.

Uwaga

W tym przykładzie języka JavaScript pokazano jedynie, jak dodać funkcję JavaScript, która poprawnie odwołuje się do elementu HTML renderowanego przez kontrolkę serwera. Aby użyć tej funkcji, należy utworzyć dodatkowy kod JavaScript w celu wywołania funkcji, gdy dokument zostanie załadowany lub gdy zostanie wyświetlona określona akcja użytkownika. Aby uzyskać więcej informacji na temat tych i powiązanych tematów, przeczytaj Artykuł Praca ze skryptem Client-Side.

Podsumowanie

Niektóre kontrolki serwera ASP.NET działają jako kontenery nazewnictwa, które wpływają na renderowane id wartości atrybutów ich kontrolek malejących, a także zakres kontrolek kanwy za pomocą FindControl metody . W odniesieniu do stron wzorcowych zarówno sama strona wzorcowa, jak i jej kontrolki ContentPlaceHolder są kontenerami nazewnictwa. W związku z tym musimy jeszcze bardziej pracować, aby programowo odwoływać się do kontrolek na stronie zawartości przy użyciu polecenia FindControl. W tym samouczku przeanalizowaliśmy dwie techniki: przechodzenie do kontrolki ContentPlaceHolder i wywoływanie jej FindControl metody oraz wdrażanie własnej FindControl implementacji, która cyklicznie wyszukuje wszystkie kontenery nazewnictwa.

Oprócz problemów z nazewnictwa kontenerów po stronie serwera wprowadzono w odniesieniu do odwołań do kontrolek sieci Web, istnieją również problemy po stronie klienta. W przypadku braku kontenerów nazewnictwa wartość właściwości kontrolki ID sieci Web i wartość renderowanego id atrybutu są takie same. Jednak wraz z dodawaniem kontenera nazewnictwa renderowany id atrybut zawiera zarówno ID wartości kontrolki sieci Web, jak i kontenery nazewnictwa w jego hierarchii kontrolnej. Te obawy dotyczące nazewnictwa są problemem tak długo, jak używasz właściwości kontrolki ClientID sieci Web do określenia wartości atrybutu renderowanego id w skryscie po stronie klienta.

Szczęśliwe programowanie!

Dalsze informacje

Aby uzyskać więcej informacji na temat tematów omówionych w tym samouczku, zapoznaj się z następującymi zasobami:

Informacje o autorze

Scott Mitchell, autor wielu książek ASP/ASP.NET i założyciel 4GuysFromRolla.com, współpracuje z technologiami internetowymi firmy Microsoft od 1998 roku. Scott pracuje jako niezależny konsultant, trener i pisarz. Jego najnowsza książka to Sams Teach Yourself ASP.NET 3,5 w ciągu 24 godzin. Scott można dotrzeć na mitchell@4GuysFromRolla.com lub za pośrednictwem swojego bloga pod adresem http://ScottOnWriting.NET.

Specjalne podziękowania

Ta seria samouczków została sprawdzona przez wielu pomocnych recenzentów. Recenzenci w tym samouczku byli Zack Jones i Suchi Barnerjee. Chcesz przejrzeć nadchodzące artykuły MSDN? Jeśli tak, upuść mi wiersz pod adresem mitchell@4GuysFromRolla.com.