Nazewnictwo identyfikatorów kontrolek na stronach zawartości (VB)
Autor: Scott Mitchell
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
.
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.
Rysunek 02. Sekcja Lekcji zawiera teraz link do sekcji "Problemy z nazewnictwem identyfikatora sterowania"
Krok 2. Badanie renderowanychID
zmian
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.
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 id
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.aspx
jako 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 ctlXX
nazewnictwa , 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 SubmitButton
zdarzenia .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).
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 ctl00
strony wzorcowej . (Wróć do rysunku 4, aby wyświetlić hierarchię sterowania, która pokazuje Page
obiekt jako element nadrzędny obiektu ctl00
strony wzorcowej). W związku z Results
tym nie można odnaleźć etykiety i Age
pola tekstowego Nothing
i 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 MainContent
elementu . 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 MainContent
elementu , 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 MainContent
elementu 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.
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 MainContent
klasy , 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.
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 poprawnejid
wartoś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_Age
pojawia 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:
- ASP.NET stron wzorcowych i
FindControl
- Tworzenie dynamicznych interfejsów użytkownika wprowadzania danych
- Instrukcje: odwołanie ASP.NET zawartości strony wzorcowej
- Strony mater: porady, wskazówki i pułapki
- Praca ze skryptem po stronie klienta
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.