Именование идентификатора элемента управления на страницах содержимого (VB)Control ID Naming in Content Pages (VB)

по Скотт Митчеллby Scott Mitchell

Скачать код или скачать PDFDownload Code or Download PDF

Показывает, как элементы управления ContentPlaceHolder служат в качестве контейнера именования и, следовательно, программно работают с сложностью элемента управления (через Финдконтрол).Illustrates how ContentPlaceHolder controls serve as a naming container and therefore make programmatically working with a control difficult (via FindControl). Рассматриваются эти проблемы и способы их обхода.Looks at this issue and workarounds. Также обсуждается программный доступ к результирующему значению ClientID.Also discusses how to programmatically access the resulting ClientID value.

ВведениеIntroduction

Все серверные элементы управления ASP.NET включают свойство ID, уникально идентифицирующее элемент управления, а также средства, с помощью которых программный доступ к элементу управления осуществляется в классе кода программной части.All ASP.NET server controls include an ID property that uniquely identifies the control and is the means by which the control is programmatically accessed in the code-behind class. Аналогичным образом элементы в HTML-документе могут включать атрибут id, однозначно определяющий элемент. Эти id значения часто используются в скрипте на стороне клиента для программной ссылки на определенный HTML-элемент.Similarly, the elements in an HTML document may include an id attribute that uniquely identifies the element; these id values are often used in client-side script to programmatically reference a particular HTML element. Учитывая это, можно предположить, что при подготовке к просмотру в HTML элемента управления сервера ASP.NET его значение ID используется в качестве id значения отображаемого HTML-элемента.Given this, you may assume that when an ASP.NET server control is rendered into HTML, its ID value is used as the id value of the rendered HTML element. Это необязательно, поскольку в некоторых обстоятельствах один элемент управления с одним IDным значением может встречаться несколько раз в отображаемой разметке.This is not necessarily the case because in certain circumstances a single control with a single ID value may appear multiple times in the rendered markup. Рассмотрим элемент управления GridView, который включает TemplateField с веб-элементом управления Label со значением ID ProductName.Consider a GridView control that includes a TemplateField with a Label Web control with an ID value of ProductName. Если элемент управления GridView привязан к источнику данных во время выполнения, эта метка повторяется один раз для каждой строки GridView.When the GridView is bound to its data source at runtime, this Label is repeated once for every GridView row. Каждой отображаемой метке требуется уникальное значение id.Each rendered Label needs a unique id value.

Чтобы обрабатывать такие сценарии, ASP.NET позволяет обозначать определенные элементы управления как контейнеры именования.To handle such scenarios, ASP.NET allows certain controls to be denoted as naming containers. Контейнер именования служит в качестве нового пространства имен ID.A naming container serves as a new ID namespace. Все серверные элементы управления, отображаемые в контейнере именования, имеют id значение с префиксом ID элемента управления контейнера именования.Any server controls that appear within the naming container have their rendered id value prefixed with the ID of the naming container control. Например, классы GridView и GridViewRow являются контейнерами именования.For example, the GridView and GridViewRow classes are both naming containers. Следовательно, элементу управления Label, определенному в элементе GridView TemplateField с ID ProductName, присваивается значение id GridViewID_GridViewRowID_ProductName.Consequently, a Label control defined in a GridView TemplateField with ID ProductName is given a rendered id value of GridViewID_GridViewRowID_ProductName. Поскольку гридвиевровид уникален для каждой строки GridView, результирующие id значения являются уникальными.Because GridViewRowID is unique for each GridView row, the resulting id values are unique.

Note

ИнтерфейсINamingContainer используется для указания того, что конкретный серверный элемент управления ASP.NET должен функционировать как контейнер именования.The INamingContainer interface is used to indicate that a particular ASP.NET server control should function as a naming container. Интерфейс INamingContainer не содержит методов, которые должен реализовать серверный элемент управления; Вместо этого он используется в качестве маркера.The INamingContainer interface does not spell out any methods that the server control must implement; rather, it's used as a marker. Если при создании отображаемой разметки элемент управления реализует этот интерфейс, то обработчик ASP.NET автоматически добавляет свое значение ID к отображаемым id значениям атрибутов потомков.In generating the rendered markup, if a control implements this interface then the ASP.NET engine automatically prefixes its ID value to its descendents' rendered id attribute values. Этот процесс подробно описан на шаге 2.This process is discussed in more detail in Step 2.

Контейнеры именования не только изменяют значение отображаемого id атрибута, но и влияют на то, как программный элемент управления может ссылаться из класса кода программной части страницы ASP.NET.Naming containers not only change the rendered id attribute value, but also affect how the control may be programmatically referenced from the ASP.NET page's code-behind class. Метод FindControl("controlID") обычно используется для программной ссылки на веб-элемент управления.The FindControl("controlID") method is commonly used to programmatically reference a Web control. Однако FindControl не проникнуть через контейнеры именования.However, FindControl does not penetrate through naming containers. Следовательно, вы не можете напрямую использовать метод Page.FindControl для ссылки на элементы управления в контейнере GridView или другого контейнера именования.Consequently, you cannot directly use the Page.FindControl method to reference controls within a GridView or other naming container.

Как вы могли иметь предполагаете, главные страницы и элементов управления ContentPlaceHolder реализуются как контейнеры именования.As you may have surmised, master pages and ContentPlaceHolders are both implemented as naming containers. В этом учебнике мы рассмотрим, как главные страницы влияют на HTML-элемент id значения и каким образом программно ссылаться на Web Controls в пределах страницы содержимого с помощью FindControl.In this tutorial we examine how master pages affect HTML element id values and ways to programmatically reference Web controls within a content page using FindControl.

Шаг 1. Добавление новой страницы ASP.NETStep 1: Adding a New ASP.NET Page

Для демонстрации концепций, обсуждаемых в этом руководстве, мы добавим новую страницу ASP.NET на наш веб-сайт.To demonstrate the concepts discussed in this tutorial, let's add a new ASP.NET page to our website. Создайте новую страницу содержимого с именем IDIssues.aspx в корневой папке, привязывая ее к главной странице Site.master.Create a new content page named IDIssues.aspx in the root folder, binding it to the Site.master master page.

Добавление страницы содержимого Идиссуес. aspx в корневую папку

Рис. 01. Добавление страницы содержимого IDIssues.aspx в корневую папкуFigure 01: Add the Content Page IDIssues.aspx to the Root Folder

Visual Studio автоматически создает элемент управления содержимым для каждой из четырех элементов управления ContentPlaceHolder на главной странице.Visual Studio automatically creates a Content control for each of the master page's four ContentPlaceHolders. Как отмечалось в руководстве по использованию нескольких элементов управления ContentPlaceHolder и содержимого по умолчанию , если элемент управления содержимым отсутствует, вместо него создается содержимое ContentPlaceHolder по умолчанию для главной страницы.As noted in the Multiple ContentPlaceHolders and Default Content tutorial, if a Content control is not present the master page's default ContentPlaceHolder content is emitted instead. Так как QuickLoginUI и LeftColumnContent элементов управления ContentPlaceHolder содержат подходящую разметку по умолчанию для этой страницы, удалите соответствующие элементы управления содержимым из IDIssues.aspx.Because the QuickLoginUI and LeftColumnContent ContentPlaceHolders contain suitable default markup for this page, go ahead and remove their corresponding Content controls from IDIssues.aspx. На этом этапе декларативная разметка страницы содержимого должна выглядеть следующим образом:At this point, the content page's declarative markup should look like the following:

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

В разделе Указание заголовка, Meta-тегов и других заголовков HTML в учебнике по главной странице мы создали пользовательский класс базовой страницы (BasePage), который автоматически настраивает заголовок страницы, если он не задан явно.In the Specifying the Title, Meta Tags, and Other HTML Headers in the Master Page tutorial we created a custom base page class (BasePage) that automatically configures the page's title if it is not explicitly set. Для использования этой функции IDIssues.aspxной страницей класс кода программной части страницы должен быть производным от класса BasePage (вместо System.Web.UI.Page).For the IDIssues.aspx page to employ this functionality, the page's code-behind class must derive from the BasePage class (instead of System.Web.UI.Page). Измените определение класса кода программной части так, чтобы оно выглядело следующим образом:Modify the code-behind class's definition so that it looks like the following:

Partial Class IDIssues
 Inherits BasePage

End Class

Наконец, обновите файл Web.sitemap, включив в него запись для этого нового занятия.Finally, update the Web.sitemap file to include an entry for this new lesson. Добавьте элемент <siteMapNode> и задайте для его title и url атрибутов значение "проблемы именования идентификатора управления" и ~/IDIssues.aspxсоответственно.Add a <siteMapNode> element and set its title and url attributes to "Control ID Naming Issues" and ~/IDIssues.aspx, respectively. После внесения этого добавления разметка файла Web.sitemap должна выглядеть следующим образом:After making this addition your Web.sitemap file's markup should look similar to the following:

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

Как показано на рис. 2, Новая запись схемы узла в Web.sitemap сразу же отражается в разделе «уроки» в левом столбце.As Figure 2 illustrates, the new site map entry in Web.sitemap is immediately reflected in the Lessons section in the left column.

В разделе "уроки" теперь имеется ссылка на "проблемы именования ИДЕНТИФИКАТОРов элементов управления"

Рис. 02. Теперь в разделе "уроки" содержится ссылка на "проблемы ИМЕНОВАНия идентификаторов управления".Figure 02: The Lessons Section Now Includes a Link to "Control ID Naming Issues"

Шаг 2. Проверка отображаемыхIDизмененийStep 2: Examining the RenderedIDChanges

Чтобы лучше понять изменения, вносимые ядром ASP.NET в отображаемые id значения серверных элементов управления, давайте добавим несколько веб-элементов управления на страницу IDIssues.aspx, а затем проверим отрисованную разметку, отправленную в браузер.To better understand the modifications the ASP.NET engine makes to the rendered id values of server controls, let's add a few Web controls to the IDIssues.aspx page and then view the rendered markup sent to the browser. В частности, введите текст "введите возраст:", а затем — веб-элемент управления TextBox.Specifically, type in the text "Please enter your age:" followed by a TextBox Web control. На следующей странице добавьте веб-элемент управления Button и элемент управления Label.Further down on the page add a Button Web control and a Label Web control. Задайте для свойств ID и Columns текстового поля значение Age и 3 соответственно.Set the TextBox's ID and Columns properties to Age and 3, respectively. Задайте свойства Text и ID кнопки "Отправить" и SubmitButton.Set the Button's Text and ID properties to "Submit" and SubmitButton. Очистите свойство Text метки и задайте для его ID значение Results.Clear out the Label's Text property and set its ID to Results.

На этом этапе декларативная разметка элемента управления содержимым должна выглядеть следующим образом:At this point your Content control's declarative markup should look similar to the following:

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

На рис. 3 показана страница при просмотре в конструкторе Visual Studio.Figure 3 shows the page when viewed through Visual Studio's designer.

страница включает три веб-элемента управления: текстовое поле, кнопку и метку.The Page Includes Three Web Controls: a TextBox, Button, and Label

Рис. 03. страница включает три веб-элемента управления: текстовое поле, кнопку и метку (щелкните, чтобы просмотреть изображение с полным размером).Figure 03: The Page Includes Three Web Controls: a TextBox, Button, and Label (Click to view full-size image)

Откройте страницу в браузере и просмотрите исходный код HTML.Visit the page through a browser and then view the HTML source. Как показано ниже, id значения HTML-элементов для элементов управления TextBox, Button и Label представляют собой сочетание ID значений веб-элементов управления и ID значений контейнеров именования на странице.As the markup below shows, the id values of the HTML elements for the TextBox, Button, and Label Web controls are a combination of the ID values of the Web controls and the ID values of the naming containers in the page.

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

Как отмечалось ранее в этом руководстве, как Главная страница, так и ее элементов управления ContentPlaceHolder служат контейнерами именования.As noted earlier in this tutorial, both the master page and its ContentPlaceHolders serve as naming containers. Как следствие, и вносят отображаемые ID значения вложенных элементов управления.Consequently, both contribute the rendered ID values of their nested controls. Возьмем id атрибут текстового поля, например: ctl00_MainContent_Age.Take the TextBox's id attribute, for instance: ctl00_MainContent_Age. Помните, что значение ID элемента управления TextBox было Age.Recall that the TextBox control's ID value was Age. Он имеет префикс с IDным значением элемента управления ContentPlaceHolder MainContent.This is prefixed with its ContentPlaceHolder control's ID value, MainContent. Кроме того, это значение добавляется с префиксом ID значения главной страницы ctl00.Furthermore, this value is prefixed with the master page's ID value, ctl00. Результат «NET» является id значением атрибута, состоящим из ID значений главной страницы, элемента управления ContentPlaceHolder и самого текстового поля.The net effect is an id attribute value consisting of the ID values of the master page, the ContentPlaceHolder control, and the TextBox itself.

Это поведение показано на рис. 4.Figure 4 illustrates this behavior. Чтобы определить отображаемые id текстового поля Age, начните со значения ID элемента управления TextBox Age.To determine the rendered id of the Age TextBox, start with the ID value of the TextBox control, Age. Затем наработаем вверх по иерархии элементов управления.Next, work your way up the control hierarchy. В каждом контейнере именования (эти узлы имеют цвет печь) добавьте префикс к текущему отображаемому id с idконтейнера именования.At each naming container (those nodes with a peach color), prefix the current rendered id with the naming container's id.

Отображаемые атрибуты идентификатора основываются на значениях ИДЕНТИФИКАТОРов контейнеров именования.

Рис. 04. отображаемые id атрибуты основаны на ID значениях контейнеров именованияFigure 04: The Rendered id Attributes are Based On the ID Values of the Naming Containers

Note

Как мы обсуждали, ctl00 часть отображаемого id атрибута составляет ID значение главной страницы, но вы, возможно, захотите узнать о том, как это ID было получено.As we discussed, the ctl00 portion of the rendered id attribute constitutes the ID value of the master page, but you may be wondering how this ID value came about. Мы не указали его ни в каком месте главной страницы, ни на странице содержимого.We did not specify it anywhere in our master or content page. Большинство серверных элементов управления на странице ASP.NET добавляются явным образом через декларативную разметку страницы.Most server controls in an ASP.NET page are added explicitly through the page's declarative markup. Элемент управления ContentPlaceHolder MainContent был явно указан в разметке Site.master. Текстовое поле Age было определено IDIssues.aspxразметке.The MainContent ContentPlaceHolder control was explicitly specified in the markup of Site.master; the Age TextBox was defined IDIssues.aspx's markup. Можно указать ID значения для этих типов элементов управления с помощью окно свойств или декларативного синтаксиса.We can specify the ID values for these types of controls through the Properties window or from the declarative syntax. Другие элементы управления, как и Главная страница, не определяются в декларативной разметке.Other controls, like the master page itself, are not defined in the declarative markup. Следовательно, значения их ID должны быть автоматически созданы для нас.Consequently, their ID values must be automatically generated for us. Подсистема ASP.NET устанавливает значения ID во время выполнения для тех элементов управления, идентификаторы которых не были заданы явным образом.The ASP.NET engine sets the ID values at runtime for those controls whose IDs have not been explicitly set. Он использует шаблон именования ctlXX, где XX — это последовательно увеличивающееся целочисленное значение.It uses the naming pattern ctlXX, where XX is a sequentially increasing integer value.

Так как Главная страница выступает в качестве контейнера именования, веб-элементы управления, определенные на главной странице, также имеют измененные значения id атрибутов.Because the master page itself serves as a naming container, the Web controls defined in the master page also have altered rendered id attribute values. Например, метка DisplayDate, добавленная на главную страницу, в учебнике Создание макета на уровне сайта с помощью главных страниц содержит следующую подготовленную разметку:For example, the DisplayDate Label we added to the master page in the Creating a Site-Wide Layout with Master Pages tutorial has the following rendered markup:

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

Обратите внимание, что атрибут id включает значение ID главной страницы (ctl00) и ID значение веб-элемента управления Label (DateDisplay).Note that the id attribute includes both the master page's ID value (ctl00) and the ID value of the Label Web control (DateDisplay).

Шаг 3. Программная ссылка на веб-элементы управления черезFindControlStep 3: Programmatically Referencing Web Controls viaFindControl

Каждый серверный элемент управления ASP.NET содержит метод FindControl("controlID"), который ищет в потомках элемента управления элемент управления с именем ControlID.Every ASP.NET server control includes a FindControl("controlID") method that searches the control's descendents for a control named controlID. Если такой элемент управления найден, он возвращается; Если соответствующий элемент управления не найден, FindControl возвращает Nothing.If such a control is found, it is returned; if no matching control is found, FindControl returns Nothing.

FindControl удобно использовать в сценариях, где требуется доступ к элементу управления, но у вас нет прямой ссылки на него.FindControl is useful in scenarios where you need to access a control but you don't have a direct reference to it. Например, при работе с веб-элементами управления данными, такими как GridView, элементы управления в полях GridView определяются один раз в декларативном синтаксисе, но во время выполнения экземпляр элемента управления создается для каждой строки GridView.When working with data Web controls like the GridView, for example, the controls within the GridView's fields are defined once in the declarative syntax, but at runtime an instance of the control is created for each GridView row. Следовательно, элементы управления, созданные во время выполнения, существуют, но у нас нет прямой ссылки, доступной из класса кода программной части.Consequently, the controls generated at runtime exist, but we do not have a direct reference available from the code-behind class. В результате необходимо использовать FindControl для программной работы с конкретным элементом управления в полях GridView.As a result we need to use FindControl to programmatically work with a specific control within the GridView's fields. (Дополнительные сведения об использовании FindControl для доступа к элементам управления в шаблонах веб-элемента управления данными см. в разделе настраиваемое форматирование на основе данных.) Такой же сценарий возникает при динамическом добавлении веб-элементов управления в веб-форму, которая обсуждается в разделе Создание пользовательских интерфейсов платформа динамических данных записи.(For more information on using FindControl to access the controls within a data Web control's templates, see Custom Formatting Based Upon Data.) This same scenario occurs when dynamically adding Web controls to a Web Form, a topic discussed in Creating Dynamic Data Entry User Interfaces.

Чтобы продемонстрировать использование метода FindControl для поиска элементов управления на странице содержимого, создайте обработчик событий для события Click SubmitButton.To illustrate using the FindControl method to search for controls within a content page, create an event handler for the SubmitButton's Click event. В обработчике событий добавьте следующий код, который программно ссылается на текстовое поле Age и Results метку с помощью метода FindControl, а затем отображает сообщение в Results на основе входных данных пользователя.In the event handler, add the following code, which programmatically references the Age TextBox and Results Label using the FindControl method and then displays a message in Results based on the user's input.

Note

Конечно, не нужно использовать FindControl для ссылки на элементы управления Label и TextBox в этом примере.Of course, we don't need to use FindControl to reference the Label and TextBox controls for this example. Мы могли бы ссылаться на них напрямую через их ID значения свойств.We could reference them directly via their ID property values. Я использую FindControl здесь, чтобы продемонстрировать, что происходит при использовании FindControl на странице содержимого.I use FindControl here to illustrate what happens when using FindControl from a content page.

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

Хотя синтаксис, используемый для вызова метода FindControl немного отличается в первых двух строках SubmitButton_Click, они семантически эквивалентны.While the syntax used to call the FindControl method differs slightly in the first two lines of SubmitButton_Click, they are semantically equivalent. Вспомним, что все серверные элементы управления ASP.NET включают метод FindControl.Recall that all ASP.NET server controls include a FindControl method. Это включает класс Page, из которого все классы кода программной части ASP.NET должны быть производными от.This includes the Page class, from which all ASP.NET code-behind classes must derive from. Таким образом, вызов FindControl("controlID") эквивалентен вызову Page.FindControl("controlID"). при условии, что вы не переопределили метод FindControl в классе кода программной части или в пользовательском базовом классе.Therefore, calling FindControl("controlID") is equivalent to calling Page.FindControl("controlID"), assuming you haven't overridden the FindControl method in your code-behind class or in a custom base class.

После ввода этого кода перейдите на страницу IDIssues.aspx в браузере, введите свой возраст и нажмите кнопку "Отправить".After entering this code, visit the IDIssues.aspx page through a browser, enter your age, and click the "Submit" button. При нажатии кнопки "Отправить" возникает NullReferenceException (см. рис. 5).Upon clicking the "Submit" button a NullReferenceException is raised (see Figure 5).

создается NullReferenceExceptionA NullReferenceException is Raised

Рис. 05. вызывается NullReferenceException (щелкните, чтобы просмотреть изображение с полным размером)Figure 05: A NullReferenceException is Raised (Click to view full-size image)

Если задать точку останова в обработчике событий SubmitButton_Click, то вы увидите, что оба вызова FindControl возвращают Nothing.If you set a breakpoint in the SubmitButton_Click event handler you will see that both calls to FindControl return Nothing. NullReferenceException возникает при попытке доступа к свойству Text текстового поля Age.The NullReferenceException is raised when we attempt to access the Age TextBox's Text property.

Проблема заключается в том, что Control.FindControl выполняет поиск только тех потомков элемента управления, которые находятся в одном контейнере именования.The problem is that Control.FindControl only searches Control's descendents that are in the same naming container. Поскольку Главная страница создает новый контейнер именования, вызов Page.FindControl("controlID") никогда не пермеатес объект главной страницы ctl00.Because the master page constitutes a new naming container, a call to Page.FindControl("controlID") never permeates the master page object ctl00. (См. рис. 4 для просмотра иерархии элементов управления, которая показывает объект Page как родительский для объекта главной страницы ctl00.) Поэтому текстовое поле Results метка и Age не найдены, а ResultsLabel и AgeTextBox присваиваются значения Nothing.(Refer back to Figure 4 to view the control hierarchy, which shows the Page object as the parent of the master page object ctl00.) Therefore, the Results Label and Age TextBox are not found and ResultsLabel and AgeTextBox are assigned values of Nothing.

Существует два обходных решения этой проблемы: можно детализировать по одному контейнеру именования для соответствующего элемента управления. или можно создать собственный метод FindControl, пермеатес контейнеры именования.There are two workarounds to this challenge: we can drill down, one naming container at a time, to the appropriate control; or we can create our own FindControl method that permeates naming containers. Давайте рассмотрим каждый из этих вариантов.Let's examine each of these options.

Детализация соответствующего контейнера именованияDrilling Into the Appropriate Naming Container

Чтобы использовать FindControl для ссылки на Results метку или в текстовое поле Age, необходимо вызвать FindControl из элемента управления предка в том же контейнере именования.To use FindControl to reference the Results Label or Age TextBox, we need to call FindControl from an ancestor control in the same naming container. Как показано на рис. 4, элемент управления MainContent ContentPlaceHolder является единственным предком Results или Age, находящихся в одном и том же контейнере именования.As Figure 4 showed, the MainContent ContentPlaceHolder control is the only ancestor of Results or Age that is within the same naming container. Иными словами, вызов метода FindControl из элемента управления MainContent, как показано в приведенном ниже фрагменте кода, правильно возвращает ссылку на элементы управления Results или Age.In other words, calling the FindControl method from the MainContent control, as shown in the code snippet below, correctly returns a reference to the Results or Age controls.

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

Однако мы не можем работать с MainContent ContentPlaceHolder из класса кода программной части страницы содержимого, используя приведенный выше синтаксис, так как ContentPlaceHolder определен на главной странице.However, we cannot work with the MainContent ContentPlaceHolder from our content page's code-behind class using the above syntax because the ContentPlaceHolder is defined in the master page. Вместо этого для получения ссылки на MainContentнеобходимо использовать FindControl.Instead, we have to use FindControl to get a reference to MainContent. Замените код в обработчике событий SubmitButton_Click следующими изменениями:Replace the code in the SubmitButton_Click event handler with the following modifications:

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

Если вы посещаете страницу в браузере, введите свой возраст и нажмите кнопку "Отправить", NullReferenceException будет создано исключение.If you visit the page through a browser, enter your age, and click the "Submit" button, a NullReferenceException is raised. Если задать точку останова в обработчике событий SubmitButton_Click, вы увидите, что это исключение возникает при попытке вызова метода FindControl объекта MainContent.If you set a breakpoint in the SubmitButton_Click event handler you will see that this exception occurs when attempting to call the MainContent object's FindControl method. Объект MainContent равен Nothing, так как метод FindControl не может располагаться в объекте с именем "MainContent".The MainContent object is equal to Nothing because the FindControl method cannot locate an object named "MainContent". Основная причина такой же, как и для Results меток и Age элементов управления TextBox: FindControl начинает поиск с верхней части иерархии элементов управления и не проникнуть контейнеры именования, но MainContent ContentPlaceHolder находится на главной странице, которая является контейнером именования.The underlying reason is the same as with the Results Label and Age TextBox controls: FindControl starts its search from the top of the control hierarchy and does not penetrate naming containers, but the MainContent ContentPlaceHolder is within the master page, which is a naming container.

Прежде чем можно будет использовать FindControl для получения ссылки на MainContent, сначала требуется ссылка на элемент управления главной страницы.Before we can use FindControl to get a reference to MainContent, we first need a reference to the master page control. После получения ссылки на главную страницу можно получить ссылку на MainContent ContentPlaceHolder через FindControl и, в нем, ссылаться на текстовое поле Results Label и Age (опять же, с помощью FindControl).Once we have a reference to the master page we can get a reference to the MainContent ContentPlaceHolder via FindControl and, from there, references to the Results Label and Age TextBox (again, through using FindControl). Но как получить ссылку на главную страницу?But how do we get a reference to the master page? Изучив атрибуты id в отображаемой разметке, очевидно, что ID значение главной страницы ctl00.By inspecting the id attributes in the rendered markup it's evident that the master page's ID value is ctl00. Поэтому можно использовать Page.FindControl("ctl00") для получения ссылки на главную страницу, а затем использовать этот объект для получения ссылки на MainContentи т. д.Therefore, we could use Page.FindControl("ctl00") to get a reference to the master page, then use that object to get a reference to MainContent, and so on. Эта логика показана в следующем фрагменте кода:The following snippet illustrates this logic:

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

Хотя этот код наверняка будет работать, предполагается, что автоматически сформированные ID главной страницы всегда будут ctl00.While this code will certainly work, it assumes that the master page's autogenerated ID will always be ctl00. Не рекомендуется делать предположения об автоматически сформированных значениях.It's never a good idea to make assumptions about autogenerated values.

К счастью, ссылка на главную страницу доступна через свойство Master класса Page.Fortunately, a reference to the master page is accessible through the Page class's Master property. Таким образом, вместо того чтобы использовать FindControl("ctl00") для получения ссылки на главную страницу для доступа к MainContent ContentPlaceHolder, мы можем использовать Page.Master.FindControl("MainContent").Therefore, instead of having to use FindControl("ctl00") to get a reference of the master page in order to access the MainContent ContentPlaceHolder, we can instead use Page.Master.FindControl("MainContent"). Обновите обработчик событий SubmitButton_Click следующим кодом:Update the SubmitButton_Click event handler with the following code:

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

На этот раз откройте страницу в браузере, введите возраст и нажмите кнопку "Отправить", чтобы сообщение отображалось в Results метке, как ожидалось.This time, visiting the page through a browser, entering your age, and clicking the "Submit" button displays the message in the Results Label, as expected.

возраст пользователя отображается в меткеThe User's Age is Displayed in the Label

Рис. 06. возраст пользователя отображается в метке (щелкните, чтобы просмотреть изображение с полным размером).Figure 06: The User's Age is Displayed in the Label (Click to view full-size image)

Рекурсивный поиск по контейнерам именованияRecursively Searching Through Naming Containers

Причина, по которой предыдущий пример кода ссылался на MainContent элемент управления ContentPlaceHolder с главной страницы, а затем Results метка и Age элементы управления TextBox из MainContent, является потому, что метод Control.FindControl выполняет поиск только в контейнере именования элемента управления.The reason the previous code example referenced the MainContent ContentPlaceHolder control from the master page, and then the Results Label and Age TextBox controls from MainContent, is because the Control.FindControl method only searches within Control's naming container. Наличие FindControl в контейнере именования имеет смысл в большинстве сценариев, поскольку два элемента управления в двух разных контейнерах именования могут иметь одинаковые значения ID.Having FindControl stay within the naming container makes sense in most scenarios because two controls in two different naming containers may have the same ID values. Рассмотрим регистр GridView, определяющий веб-элемент управления Label с именем ProductName в одном из его полей TemplateField.Consider the case of a GridView that defines a Label Web control named ProductName within one of its TemplateFields. Когда данные привязываются к GridView во время выполнения, для каждой строки GridView создается ProductName метка.When the data is bound to the GridView at runtime, a ProductName Label is created for each GridView row. Если FindControl выполнить поиск по всем контейнерам именования и назвать Page.FindControl("ProductName"), то какой экземпляр метки должен возвращаться FindControl?If FindControl searched through all naming containers and we called Page.FindControl("ProductName"), what Label instance should the FindControl return? ProductName метку в первой строке GridView?The ProductName Label in the first GridView row? Он находится в последней строке?The one in the last row?

Поэтому в большинстве случаев есть смысл в контейнере именования только элемента управленияControl.FindControl поиска.So having Control.FindControl search just Control's naming container makes sense in most cases. Но существуют и другие случаи, в том числе те, у которых имеется уникальный ID по всем контейнерам именования, и необходимо избегать необходимости в тщательной ссылке на каждый контейнер именования в иерархии элементов управления для доступа к элементу управления.But there are other cases, such as the one facing us, where we have a unique ID across all naming containers and want to avoid having to meticulously reference each naming container in the control hierarchy to access a control. Наличие FindControl вариант, который рекурсивно ищет все контейнеры именования, имеет смысл.Having a FindControl variant that recursively searches all naming containers makes sense, too. К сожалению, .NET Framework не включает такой метод.Unfortunately, the .NET Framework does not include such a method.

Хорошая новость состоит в том, что мы можем создать собственный метод FindControl, который рекурсивно ищет все контейнеры именования.The good news is that we can create our own FindControl method that recursively searches all naming containers. На самом деле, использование методов расширения можно присвоить методу FindControlRecursive к классу Control, чтобы сопровождать его существующим методом FindControl.In fact, using extension methods we can tack on a FindControlRecursive method to the Control class to accompany its existing FindControl method.

Note

Методы расширения — это новая функция для C# 3,0 и Visual Basic 9, которые являются языками, поставляемыми с .NET Framework версиями 3,5 и Visual Studio 2008.Extension methods are a feature new to C# 3.0 and Visual Basic 9, which are the languages that ship with the .NET Framework version 3.5 and Visual Studio 2008. Вкратце, методы расширения позволяют разработчику создавать новый метод для существующего типа класса с помощью специального синтаксиса.In short, extension methods allow for a developer to create a new method for an existing class type through a special syntax. Дополнительные сведения об этой полезной функции см. в моей статье расширение функциональности базового типа с помощью методов расширения.For more information on this helpful feature, refer to my article, Extending Base Type Functionality with Extension Methods.

Чтобы создать метод расширения, добавьте новый файл в папку App_Code с именем PageExtensionMethods.vb.To create the extension method, add a new file to the App_Code folder named PageExtensionMethods.vb. Добавьте метод расширения с именем FindControlRecursive, который принимает в качестве входных данных параметр String с именем controlID.Add an extension method named FindControlRecursive that takes as an input a String parameter named controlID. Чтобы методы расширения работали правильно, крайне важно, чтобы класс был помечен как Module и что методы расширения имеют префикс <Extension()> атрибута.For extension methods to work properly, it is vital that the class be marked as a Module and that the extension methods be prefixed with the <Extension()> attribute. Более того, все методы расширения должны принять в качестве первого параметра объект типа, к которому применяется метод расширения.Moreover, all extension methods must accept as their first parameter an object of the type to which the extension method applies.

Добавьте следующий код в файл PageExtensionMethods.vb, чтобы определить этот Module и метод расширения FindControlRecursive:Add the following code to the PageExtensionMethods.vb file to define this Module and the FindControlRecursive extension method:

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

После этого кода вернитесь к классу кода программной части IDIssues.aspx страницы и закомментируйте текущий метод FindControl.With this code in place, return to the IDIssues.aspx page's code-behind class and comment out the current FindControl method calls. Замените их вызовами Page.FindControlRecursive("controlID").Replace them with calls to Page.FindControlRecursive("controlID"). О методах расширения следует присутствовать непосредственно в раскрывающихся списках IntelliSense.What's neat about extension methods is that they appear directly within the IntelliSense drop-down lists. Как показано на рис. 7, при вводе Page и последующем нажатии FindControlRecursive метод включается в раскрывающийся список IntelliSense вместе с другими методами класса Control.As Figure 7 shows, when you type Page and then hit period, the FindControlRecursive method is included in the IntelliSense drop-down along with the other Control class methods.

методы расширения включены в раскрывающиеся окна IntelliSenseExtension Methods are Included in the IntelliSense Drop-Downs

Рис. 07. методы расширения включены в раскрывающиеся меню IntelliSense (щелкните, чтобы просмотреть изображение с полным размером)Figure 07: Extension Methods are Included in the IntelliSense Drop-Downs (Click to view full-size image)

Введите следующий код в обработчик событий SubmitButton_Click, а затем протестируйте его, перейдя на страницу, введя свой возраст и нажав кнопку "Отправить".Enter the following code into the SubmitButton_Click event handler and then test it by visiting the page, entering your age, and clicking the "Submit" button. Как показано на рис. 6, полученный результат будет представлять собой сообщение "срок давно года устарел!".As shown back in Figure 6, the resulting output will be the message, "You are age years old!"

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

Note

Так как методы расширения являются новыми C# для 3,0 и Visual Basic 9, при использовании Visual Studio 2005 нельзя использовать методы расширения.Because extension methods are new to C# 3.0 and Visual Basic 9, if you are using Visual Studio 2005 you cannot use extension methods. Вместо этого необходимо реализовать метод FindControlRecursive в вспомогательном классе.Instead, you'll need to implement the FindControlRecursive method in a helper class. Рик Штраль имеет такой пример в записи блога, ASP.NET главного pages и FindControl.Rick Strahl has such an example in his blog post, ASP.NET Maser Pages and FindControl.

Шаг 4. Использование правильного значения атрибутаidв скрипте на стороне клиентаStep 4: Using the CorrectidAttribute Value in Client-Side Script

Как отмечалось в этом учебнике, в клиентской части скрипта для программной ссылки на определенный элемент HTML часто используется idный атрибут веб-элемента управления.As noted in this tutorial's introduction, a Web control's rendered id attribute is oftentimes used in client-side script to programmatically reference a particular HTML element. Например, следующий код JavaScript ссылается на элемент HTML по его id, а затем отображает его значение в модальном окне сообщения:For example, the following JavaScript references an HTML element by its id and then displays its value in a modal message box:

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

Помните, что на страницах ASP.NET, не содержащих контейнер именования, отображаемый атрибут id элемента HTML идентичен значению свойства ID веб-элемента управления.Recall that in ASP.NET pages that do not include a naming container, the rendered HTML element's id attribute is identical to the Web control's ID property value. В связи с этим в коде JavaScript для id значений атрибутов используется жесткий код.Because of this, it is tempting to hard code in id attribute values into JavaScript code. Это значит, что если вы хотите получить доступ к веб-элементу управления TextBox Age с помощью клиентского скрипта, сделайте это с помощью вызова document.getElementById("Age").That is, if you know you want to access the Age TextBox Web control through client-side script, do so via a call to document.getElementById("Age").

Проблема этого подхода заключается в том, что при использовании главных страниц (или других элементов управления контейнера именования) отображаемый HTML-id не является синонимом свойства ID веб-элемента управления.The problem with this approach is that when using master pages (or other naming container controls), the rendered HTML id is not synonymous with the Web control's ID property. Первым наклона может быть посещение страницы в браузере и просмотр источника, чтобы определить фактический атрибут id.Your first inclination may be to visit the page through a browser and view the source to determine the actual id attribute. Зная значение, отображаемое id, можно вставить его в вызов getElementById, чтобы получить доступ к элементу HTML, с которым необходимо работать с помощью клиентского скрипта.Once you know the rendered id value, you can paste it into the call to getElementById to access the HTML element you need to work with through client-side script. Этот подход является менее идеальным, поскольку некоторые изменения в иерархии элементов управления страницы или изменения свойств ID элементов управления именованием изменяют результирующий id атрибут, тем самым нарушая код JavaScript.This approach is less than ideal because certain changes to the page's control hierarchy or changes to the ID properties of the naming controls will alter the resulting id attribute, thereby breaking your JavaScript code.

Хорошая новость заключается в том, что отображаемое значение id атрибута доступно в коде на стороне сервера через свойствоClientIDвеб-элемента управления.The good news is that the id attribute value that is rendered is accessible in server-side code through the Web control's ClientID property. Это свойство следует использовать для определения значения атрибута id, используемого в скрипте на стороне клиента.You should use this property to determine the id attribute value used in client-side script. Например, чтобы добавить на страницу функцию JavaScript, которая при вызове отображает значение текстового поля Age в модальном окне сообщения, добавьте следующий код в обработчик событий Page_Load:For example, to add a JavaScript function to the page that, when called, displays the value of the Age TextBox in a modal message box, add the following code to the Page_Load event handler:

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)

Приведенный выше код вставляет значение свойства ClientID текстового поля Age в вызов JavaScript в getElementById.The above code injects the value of the Age TextBox's ClientID property into the JavaScript call to getElementById. Если открыть эту страницу в браузере и просмотреть исходный код HTML, вы найдете следующий код JavaScript:If you visit this page through a browser and view the HTML source, you'll find the following JavaScript code:

<script type="text/javascript">
//<![CDATA[
function ShowAge()
{
 var elem = document.getElementById('ctl00_MainContent_Age');
 if (elem != null)
 alert('You entered ' + elem.value + ' into the Age text box.');
}//]]>
</script>

Обратите внимание, что в вызове getElementByIdотображается правильное значение id атрибута ctl00_MainContent_Age.Notice how the correct id attribute value, ctl00_MainContent_Age, appears within the call to getElementById. Так как это значение вычисляется во время выполнения, оно работает независимо от последующих изменений в иерархии элементов управления страницы.Because this value is calculated at runtime, it works regardless of later changes to the page control hierarchy.

Note

В этом примере JavaScript просто показано, как добавить функцию JavaScript, которая правильно ссылается на HTML-элемент, отображаемый серверным элементом управления.This JavaScript example merely shows how to add a JavaScript function that correctly references the HTML element rendered by a server control. Чтобы использовать эту функцию, необходимо создать дополнительный код JavaScript для вызова функции при загрузке документа или при выполнении определенного действия пользователя.To use this function you would need to author additional JavaScript to call the function when the document loads or when some specific user action transpires. Дополнительные сведения об этих и связанных разделах см. в статье Работа со сценарием на стороне клиента.For more information on these and related topics, read Working with Client-Side Script.

СводкаSummary

Некоторые серверные элементы управления ASP.NET действуют как контейнеры именования, что влияет на отображаемые id значения атрибутов их дочерних элементов управления, а также на область элементов управления, канвассед методом FindControl.Certain ASP.NET server controls act as naming containers, which affects the rendered id attribute values of their descendent controls as well as the scope of controls canvassed by the FindControl method. В отношении главных страниц как Главная страница, так и ее элементы управления ContentPlaceHolder являются контейнерами именования.With regards to master pages, both the master page itself and its ContentPlaceHolder controls are naming containers. Следовательно, необходимо положить немного больше работы, чтобы программно ссылаться на элементы управления на странице содержимого с помощью FindControl.Consequently, we need to put forth a bit more work to programmatically reference controls within the content page using FindControl. В этом учебнике мы рассмотрели два метода: детализация элемента управления ContentPlaceHolder и вызов метода FindControl. и пошаговая реализация собственного FindControl, которая рекурсивно выполняет поиск по всем контейнерам именования.In this tutorial we examined two techniques: drilling into the ContentPlaceHolder control and calling its FindControl method; and rolling our own FindControl implementation that recursively searches through all naming containers.

Наряду с тем, что контейнеры именования проблем на стороне сервера содержат ссылки на веб-элементы управления, существуют и проблемы на стороне клиента.In addition to the server-side issues naming containers introduce with regards to referencing Web controls, there are also client-side issues. При отсутствии контейнеров именования значение свойства ID веб-элемента управления и отображаемое id значение атрибута — одно в одном.In the absence of naming containers, the Web control's ID property value and rendered id attribute value are one in the same. Но с добавлением контейнера именования отображаемый id атрибут содержит как ID значения веб-элемента управления, так и контейнеры именования в происхождение иерархии элементов управления.But with the addition of naming container, the rendered id attribute includes both the ID values of the Web control and the naming container(s) in its control hierarchy's ancestry. Эти проблемы именования не являются проблемой, если вы используете свойство ClientID веб-элемента управления для определения отображаемого id значения атрибута в скрипте на стороне клиента.These naming concerns are a non-issue as long as you use the Web control's ClientID property to determine the rendered id attribute value in your client-side script.

Поздравляем с программированием!Happy Programming!

Дополнительные материалыFurther Reading

Дополнительные сведения о разделах, обсуждаемых в этом руководстве, см. в следующих ресурсах:For more information on the topics discussed in this tutorial, refer to the following resources:

Об автореAbout the Author

Скотт Митчелл, автор нескольких книг по ASP/ASP. NET и основатель 4GuysFromRolla.com, работал с веб-технологиями майкрософт с 1998.Scott Mitchell, author of multiple ASP/ASP.NET books and founder of 4GuysFromRolla.com, has been working with Microsoft Web technologies since 1998. Скотт работает как независимый консультант, преподаватель и модуль записи.Scott works as an independent consultant, trainer, and writer. Его последняя книга — Sams обучать себя ASP.NET 3,5 за 24 часа.His latest book is Sams Teach Yourself ASP.NET 3.5 in 24 Hours. Скотт можно получить по адресу mitchell@4GuysFromRolla.com или через свой блог по адресу http://ScottOnWriting.NET.Scott can be reached at mitchell@4GuysFromRolla.com or via his blog at http://ScottOnWriting.NET.

Специальная благодарностьSpecial Thanks To

Эта серия руководств была рассмотрена многими полезными рецензентами.This tutorial series was reviewed by many helpful reviewers. Потенциальным рецензентам для этого учебника были Зак Jones и Сучи Барнержи.Lead reviewers for this tutorial were Zack Jones and Suchi Barnerjee. Хотите ознакомиться с моими будущими статьями MSDN?Interested in reviewing my upcoming MSDN articles? Если это так, расположите строку в mitchell@4GuysFromRolla.com.If so, drop me a line at mitchell@4GuysFromRolla.com.