Udostępnij za pośrednictwem


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

Autor: Scott Mitchell

Pobierz plik PDF

Ilustruje, jak kontrolki ContentPlaceHolder pełnią rolę kontenera nazewnictwa i dlatego programowo działają z kontrolką trudną (za pomocą funkcji 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 ASP.NET kontrolki serwera obejmują ID właściwość, która jednoznacznie identyfikuje kontrolkę i jest sposobem, za pomocą którego kontrolka jest programowo uzyskiwana w klasie za pomocą kodu. 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łowania się do określonego elementu HTML. Biorąc pod uwagę to, można założyć, że gdy kontrolka serwera ASP.NET jest renderowana w kodzie HTML, jej 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 renderowanych znacznikach. Rozważ kontrolkę GridView zawierającą pole TemplateField 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 elementem ID ProductName ma 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 renderowanego 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 id atrybutu, ale także wpływa na sposób programowego odwołowania się do kontrolki z klasy za pomocą kodu strony ASP.NET. Metoda FindControl("controlID") jest często używana do programowego odwołowania 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 w celu odwołania się do kontrolek w kontrolce GridView lub innym kontenerze nazewnictwa.

Jak można przypuszczać, strony wzorcowe i ContentPlaceHolders są implementowane jako kontenery nazewnictwa. W tym samouczku dowiesz się, jak strony wzorcowe wpływają na wartości elementów id HTML i sposoby programowego odwołowania się do kontrolek sieci Web na stronie zawartości przy użyciu metody 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, powiązanie jej ze stroną wzorcową Site.master .

Dodawanie strony zawartości IDIssues.aspx 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 witryny IDIssues.aspx. Na tym etapie znaczniki deklaratywne strony zawartości powinny wyglądać następująco:

<%@ Page Language="VB" MasterPageFile="~/Site.master" AutoEventWireup="false" CodeFile="IDIssues.aspx.vb" Inherits="IDIssues" Title="Untitled Page" %>

<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" Runat="Server">
</asp:Content>

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

Partial Class IDIssues
 Inherits BasePage

End Class

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

<?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 jest 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 aparatu ASP.NET wprowadzone w renderowanych wartościach kontrolek serwera, dodajmy kilka kontrolek sieci Web do IDIssues.aspx strony, a następnie wyświetl renderowane id znaczniki wysłane do przeglądarki. W szczególności wpisz tekst "Wprowadź wiek", a następnie kontrolkę tekstową TextBox Web. Dalej na stronie dodaj kontrolkę Sieć Web przycisku i kontrolkę Etykieta 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 deklaratywne 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ę w przypadku wyświetlania za pomocą projektanta programu Visual Studio.

Strona zawiera trzy kontrolki sieci Web: TextBox, Button i Label

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

Odwiedź stronę za pośrednictwem przeglądarki, a następnie wyświetl źródło HTML. Jak pokazuje poniższy znacznik, id wartości elementów HTML dla 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 symbole ContentPlaceHolder służą jako kontenery nazewnictwa. W związku z tym obie współtworzyją renderowane wartości zagnieżdżonych ID 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 netto to wartość atrybutu id składająca się z ID wartości strony wzorcowej, kontrolki ContentPlaceHolder i samej kontrolki TextBox.

Rysunek 4 ilustruje to zachowanie. Aby określić renderowanie id kontrolki Age TextBox, zacznij od ID wartości kontrolki TextBox. Age Następnie przejmij drogę w górę hierarchii sterowania. W każdym kontenerze nazewnictwa (węzły o kolorze brzoskwini) 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 nigdzie 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 znaczniku Site.master; Age element TextBox został zdefiniowany IDIssues.aspxjako znacznik. Możemy określić ID wartości dla 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 znaczników 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 obejmującego całą witrynę 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 ID wzorcowej (ctl00) jak i ID wartość kontrolki Etykieta sieci Web (DateDisplay).

Krok 3. Programowe odwoływanie się do kontrolek sieci Web za pośrednictwemFindControl

Każda kontrolka serwera ASP.NET zawiera metodę FindControl("controlID") , która wyszukuje malejące 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ść Nothing.

FindControl jest przydatna w scenariuszach, w których musisz uzyskać dostęp do kontroli, ale nie masz bezpośredniego odwołania do niej. 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 wystąpienie kontrolki jest tworzone dla każdego wiersza kontrolki GridView. 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 GridView. (Aby uzyskać więcej informacji na temat uzyskiwania FindControl 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 interfejsów użytkownika dynamicznego wprowadzania danych).

Aby zilustrować FindControl użycie metody do 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 Age kontrolki 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ę dzieje podczas korzystania FindControl ze strony zawartości.

Protected Sub SubmitButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SubmitButton.Click
 Dim ResultsLabel As Label = CType(FindControl("Results"), Label)
 Dim AgeTextBox As TextBox = CType(Page.FindControl("Age"), TextBox)

 ResultsLabel.Text = String.Format("You are {0} years old!", AgeTextBox.Text)
End Sub

Chociaż składnia używana do wywoływania FindControl metody nieznacznie różni się 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, że metoda nie jest zastępowana FindControl w klasie za pomocą kodu lub 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 NullReferenceException zgłoszony element (zobacz Rysunek 5).

Wyjątek NullReferenceException jest zgłaszany

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

Jeśli ustawisz punkt przerwania w procedurze SubmitButton_Click obsługi zdarzeń, zobaczysz, że oba wywołania zwracają FindControl wartość Nothing. Element 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 tylko przeszukuje zstępne kontrolce, 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 Nothingi ResultsLabel AgeTextBox są przypisane wartości .

Istnieją dwa obejścia tego wyzwania: możemy przejść do szczegółów, jeden kontener nazewnictwa jednocześnie, do odpowiedniej kontrolki; możemy też 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 kontrolkę ancestor w tym samym kontenerze nazewnictwa. Jak pokazano na rysunku 4, kontrolka MainContent ContentPlaceHolder jest jedynym elementem nadrzędnym Results 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 .

Dim ResultsLabel As Label = CType(MainContent.FindControl("Results"), Label)
Dim AgeTextBox As TextBox = CType(MainContent.FindControl("Age"), TextBox)

Nie możemy jednak pracować z elementem MainContent ContentPlaceHolder z klasy za pomocą 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 programu SubmitButton_Click obsługi zdarzeń następującymi modyfikacjami:

Protected Sub SubmitButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SubmitButton.Click
 Dim MainContent As ContentPlaceHolder = CType(FindControl("MainContent"), ContentPlaceHolder)

 Dim ResultsLabel As Label = CType(MainContent.FindControl("Results"), Label)
 Dim AgeTextBox As TextBox = CType(MainContent.FindControl("Age"), TextBox)

 ResultsLabel.Text = String.Format("You are {0} years old!", AgeTextBox.Text)
End Sub

Jeśli odwiedzasz stronę za pośrednictwem przeglądarki, wprowadź swój wiek, a następnie kliknij przycisk "Prześlij", NullReferenceException zostanie zgłoszony element . Jeśli ustawisz punkt przerwania w SubmitButton_Click procedurze obsługi zdarzeń, zobaczysz, że ten wyjątek występuje podczas próby wywołania MainContent metody obiektu FindControl . Obiekt MainContent jest równy Nothing , ponieważ FindControl 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 utworzeniu odwołania do strony wzorcowej możemy uzyskać odwołanie do MainContent elementu ContentPlaceHolder za pośrednictwem elementu FindControl i, z tego miejsca, odwołania do Results etykiety i Age pola tekstowego (ponownie za pomocą polecenia FindControl). Jak jednak uzyskać odwołanie do strony wzorcowej? id Sprawdzając atrybuty w renderowanej adiustacji, widać, że wartość strony wzorcowej ID to ctl00. W związku z tym możemy użyć Page.FindControl("ctl00") polecenia , 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
Dim ctl00 As MasterPage = CType(FindControl("ctl00"), MasterPage)

'Get a reference to the ContentPlaceHolder
Dim MainContent As ContentPlaceHolder = CType(ctl00.FindControl("MainContent"), ContentPlaceHolder)

'Reference the Label and TextBox controls
Dim ResultsLabel As Label = CType(MainContent.FindControl("Results"), Label)
Dim AgeTextBox As TextBox = CType(MainContent.FindControl("Age"), TextBox)

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 wartości wygenerowanych automatycznie.

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żywać FindControl("ctl00") polecenia w celu uzyskania odwołania do strony wzorcowej w celu uzyskania dostępu MainContent do elementu ContentPlaceHolder, zamiast tego możemy użyć elementu Page.Master.FindControl("MainContent"). SubmitButton_Click Zaktualizuj program obsługi zdarzeń przy użyciu następującego kodu:

Protected Sub SubmitButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SubmitButton.Click
 'Get a reference to the ContentPlaceHolder
 Dim MainContent As ContentPlaceHolder = CType(Page.Master.FindControl("MainContent"), ContentPlaceHolder)

 'Reference the Label and TextBox controls
 Dim ResultsLabel As Label = CType(MainContent.FindControl("Results"), Label)
 Dim AgeTextBox As TextBox = CType(MainContent.FindControl("Age"), TextBox)

 ResultsLabel.Text = String.Format("You are {0} years old!", AgeTextBox.Text)
End Sub

Tym razem odwiedź stronę za pośrednictwem przeglądarki, wprowadzając swój wiek i klikając przycisk "Prześlij" wyświetla komunikat 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 pełnowymiarowy)

Cykliczne wyszukiwanie za pomocą kontenerów nazewnictwa

Przyczyną, 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 to, że Control.FindControl metoda wyszukuje tylko w kontenerze nazewnictwa kontrolki. Zachowanie 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ę Sieć Web etykiet o nazwie ProductName w ramach jednego z pól szablonów. Gdy dane są powiązane z kontrolką GridView w czasie wykonywania, dla każdego wiersza kontrolki GridView zostanie utworzona etykieta ProductName . Jeśli FindControl przeszukano wszystkie kontenery nazewnictwa i wywołaliśmy Page.FindControl("ProductName")polecenie , jakie wystąpienie etykiety powinno FindControl zwrócić? 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, z którym mamy do czynienia, gdzie mamy unikatowe ID dla wszystkich kontenerów nazewnictwa i chcemy uniknąć konieczności skrupulatnego odwołowania się do każdego kontenera nazewnictwa w hierarchii kontroli w celu uzyskania dostępu do kontroli. FindControl Wariant, który cyklicznie wyszukuje wszystkie kontenery nazewnictwa, ma sens. Niestety, program .NET Framework nie zawiera takiej metody.

Dobrą wiadomością jest to, że możemy utworzyć własną FindControl metodę, która cyklicznie wyszukuje wszystkie kontenery nazewnictwa. W rzeczywistości przy użyciu metod rozszerzeń możemy zastosować 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 programem .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.vb. 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 klasa była oznaczona jako i Module że metody rozszerzeń mają być poprzedzone atrybutem <Extension()> . Ponadto wszystkie metody rozszerzenia muszą akceptować jako pierwszy parametr obiektu typu, do którego stosuje się metodę rozszerzenia.

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

Imports System.Runtime.CompilerServices

Public Module PageExtensionMethods
 <Extension()> _
  Public Function FindControlRecursive(ByVal ctrl As Control, ByVal controlID As String) As Control
 If String.Compare(ctrl.ID, controlID, True) = 0 Then
 ' We found the control!
 Return ctrl
 Else
 ' Recurse through ctrl's Controls collections
 For Each child As Control In ctrl.Controls
 Dim lookFor As Control = FindControlRecursive(child, controlID)

 If lookFor IsNot Nothing Then
 Return lookFor  ' We found the control
 End If
 Next

 ' If we reach here, control was not found
 Return Nothing
 End If
 End Function
End Module

Za pomocą 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 do Page.FindControlRecursive("controlID"). Co jest schludne w przypadku metod rozszerzeń, jest to, że są one wyświetlane bezpośrednio na listach rozwijanych Funkcji IntelliSense. Jak pokazano na rysunku 7, po wpisaniu Page , 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 rozszerzeń są uwzględniane na listach rozwijanych Funkcji IntelliSense (kliknij, aby wyświetlić obraz o 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 wiek i klikając przycisk "Prześlij". Jak pokazano na rysunku 6, wynikowe dane wyjściowe będą komunikatem "Masz wiek!"

Protected Sub SubmitButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SubmitButton.Click
 Dim ResultsLabel As Label = CType(Page.FindControlRecursive("Results"), Label)
 Dim AgeTextBox As TextBox = CType(Page.FindControlRecursive("Age"), TextBox)

 ResultsLabel.Text = String.Format("You are {0} years old!", AgeTextBox.Text)
End Sub

Uwaga

Ponieważ metody rozszerzeń są nowe dla języka C# 3.0 i Visual Basic 9, jeśli używasz programu Visual Studio 2005, nie możesz 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 skry skry skryptu po stronie klienta

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łowania 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 polu 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 kuszące jest kodowanie w wartościach atrybutów w id kodzie JavaScript. Oznacza to, że jeśli wiesz, że chcesz uzyskać dostęp do kontrolki Age sieci Web 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. Pierwszą skłonność do odwiedzenia strony za pośrednictwem przeglądarki i wyświetlenia ź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 ClientID sieci Web. Ta właściwość powinna służyć do określania wartości atrybutu używanej id w skry skryptie 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(Me.GetType(), "ShowAgeTextBoxScript", _
 "function ShowAge() " & vbCrLf & _
 "{" & vbCrLf & _
 " var elem = document.getElementById('" & AgeTextBox.ClientID & "');" & vbCrLf & _
 " if (elem != null)" & vbCrLf & _
 " alert('You entered ' + elem.value + ' into the Age text box.');" & vbCrLf & _
 "}", True)

Powyższy kod wprowadza wartość Age właściwości TextBox ClientID do wywołania javaScript metody getElementById. 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 , ctl00_MainContent_Agepojawia się w wywołaniu metody getElementById. Ponieważ ta wartość jest obliczana w czasie wykonywania, działa niezależnie od późniejszych zmian w hierarchii kontroli strony.

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 podczas ładowania dokumentu lub gdy zostanie wyświetlona określona akcja użytkownika. Aby uzyskać więcej informacji na temat tych i powiązanych tematów, przeczytaj Praca ze skryptem po stronie klienta.

Podsumowanie

Niektóre ASP.NET kontrolki serwera działają jako kontenery nazewnictwa, które wpływają na wartości renderowanych id atrybutów ich kontrolek malejących, a także zakres kontrolek kanwy przez metodę FindControl . W odniesieniu do stron wzorcowych zarówno sama strona wzorcowa, jak i jej kontrolki ContentPlaceHolder są kontenerami nazewnictwa. W związku z tym musimy nieco 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 szczegółów kontrolki ContentPlaceHolder i wywoływanie jej FindControl metody oraz wprowadzanie własnej FindControl implementacji, która rekursywnie przeszukuje wszystkie kontenery nazewnictwa.

Oprócz problemów po stronie serwera nazewnictwa kontenerów 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 renderowana id wartość atrybutu są takie same. Jednak wraz z dodaniem kontenera nazewnictwa renderowany id atrybut zawiera zarówno ID wartości kontrolki sieci Web, jak i kontenery nazewnictwa w jego hierarchii kontroli. Te problemy dotyczące nazewnictwa nie są problemem, o ile używasz właściwości kontrolki ClientID sieci Web do określenia wartości renderowanego id atrybutu w skrycie 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 uzyskać na mitchell@4GuysFromRolla.com stronie lub za pośrednictwem swojego bloga pod adresem http://ScottOnWriting.NET.

Specjalne podziękowania

Ta seria samouczków została omówiona przez wielu przydatnych recenzentów. Recenzenci z tego samouczka to Zack Jones i Suchi Barnerjee. Chcesz przejrzeć nadchodzące artykuły MSDN? Jeśli tak, upuść mi wiersz pod adresem mitchell@4GuysFromRolla.com.