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

по Скотт Митчелл

Скачать код или скачать PDF

Показывает, как элементы управления ContentPlaceHolder служат в качестве контейнера именования и, следовательно, программно работают с сложностью элемента управления (через Финдконтрол). Рассматриваются эти проблемы и способы их обхода. Также обсуждается программный доступ к результирующему значению ClientID.

Введение

Все серверные элементы управления ASP.NET включают свойство ID, уникально идентифицирующее элемент управления, а также средства, с помощью которых программный доступ к элементу управления осуществляется в классе кода программной части. Аналогичным образом элементы в HTML-документе могут включать атрибут id, однозначно определяющий элемент. Эти id значения часто используются в скрипте на стороне клиента для программной ссылки на определенный HTML-элемент. Учитывая это, можно предположить, что при подготовке к просмотру в HTML элемента управления сервера ASP.NET его значение ID используется в качестве id значения отображаемого HTML-элемента. Это необязательно, поскольку в некоторых обстоятельствах один элемент управления с одним IDным значением может встречаться несколько раз в отображаемой разметке. Рассмотрим элемент управления GridView, который включает TemplateField с веб-элементом управления Label со значением ID ProductName. Если элемент управления GridView привязан к источнику данных во время выполнения, эта метка повторяется один раз для каждой строки GridView. Каждой отображаемой метке требуется уникальное значение id.

Чтобы обрабатывать такие сценарии, ASP.NET позволяет обозначать определенные элементы управления как контейнеры именования. Контейнер именования служит в качестве нового пространства имен ID. Все серверные элементы управления, отображаемые в контейнере именования, имеют id значение с префиксом ID элемента управления контейнера именования. Например, классы GridView и GridViewRow являются контейнерами именования. Следовательно, элементу управления Label, определенному в элементе GridView TemplateField с ID ProductName, присваивается значение id GridViewID_GridViewRowID_ProductName. Поскольку гридвиевровид уникален для каждой строки GridView, результирующие id значения являются уникальными.

Note

ИнтерфейсINamingContainer используется для указания того, что конкретный серверный элемент управления ASP.NET должен функционировать как контейнер именования. Интерфейс INamingContainer не содержит методов, которые должен реализовать серверный элемент управления; Вместо этого он используется в качестве маркера. Если при создании отображаемой разметки элемент управления реализует этот интерфейс, то обработчик ASP.NET автоматически добавляет свое значение ID к отображаемым id значениям атрибутов потомков. Этот процесс подробно описан на шаге 2.

Контейнеры именования не только изменяют значение отображаемого id атрибута, но и влияют на то, как программный элемент управления может ссылаться из класса кода программной части страницы ASP.NET. Метод FindControl("controlID") обычно используется для программной ссылки на веб-элемент управления. Однако FindControl не проникнуть через контейнеры именования. Следовательно, вы не можете напрямую использовать метод Page.FindControl для ссылки на элементы управления в контейнере GridView или другого контейнера именования.

Как вы могли иметь предполагаете, главные страницы и элементов управления ContentPlaceHolder реализуются как контейнеры именования. В этом учебнике мы рассмотрим, как главные страницы влияют на HTML-элемент id значения и каким образом программно ссылаться на Web Controls в пределах страницы содержимого с помощью FindControl.

Шаг 1. Добавление новой страницы ASP.NET

Для демонстрации концепций, обсуждаемых в этом руководстве, мы добавим новую страницу ASP.NET на наш веб-сайт. Создайте новую страницу содержимого с именем IDIssues.aspx в корневой папке, привязывая ее к главной странице Site.master.

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

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

Visual Studio автоматически создает элемент управления содержимым для каждой из четырех элементов управления ContentPlaceHolder на главной странице. Как отмечалось в руководстве по использованию нескольких элементов управления ContentPlaceHolder и содержимого по умолчанию , если элемент управления содержимым отсутствует, вместо него создается содержимое ContentPlaceHolder по умолчанию для главной страницы. Так как QuickLoginUI и LeftColumnContent элементов управления ContentPlaceHolder содержат подходящую разметку по умолчанию для этой страницы, удалите соответствующие элементы управления содержимым из IDIssues.aspx. На этом этапе декларативная разметка страницы содержимого должна выглядеть следующим образом:

<%@ 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), который автоматически настраивает заголовок страницы, если он не задан явно. Для использования этой функции IDIssues.aspxной страницей класс кода программной части страницы должен быть производным от класса BasePage (вместо System.Web.UI.Page). Измените определение класса кода программной части так, чтобы оно выглядело следующим образом:

Partial Class IDIssues
 Inherits BasePage

End Class

Наконец, обновите файл Web.sitemap, включив в него запись для этого нового занятия. Добавьте элемент <siteMapNode> и задайте для его title и url атрибутов значение "проблемы именования идентификатора управления" и ~/IDIssues.aspxсоответственно. После внесения этого добавления разметка файла Web.sitemap должна выглядеть следующим образом:

<?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 сразу же отражается в разделе «уроки» в левом столбце.

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

Рис. 02. Теперь в разделе "уроки" содержится ссылка на "проблемы ИМЕНОВАНия идентификаторов управления".

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

Чтобы лучше понять изменения, вносимые ядром ASP.NET в отображаемые id значения серверных элементов управления, давайте добавим несколько веб-элементов управления на страницу IDIssues.aspx, а затем проверим отрисованную разметку, отправленную в браузер. В частности, введите текст "введите возраст:", а затем — веб-элемент управления TextBox. На следующей странице добавьте веб-элемент управления Button и элемент управления Label. Задайте для свойств ID и Columns текстового поля значение Age и 3 соответственно. Задайте свойства Text и ID кнопки "Отправить" и SubmitButton. Очистите свойство Text метки и задайте для его ID значение Results.

На этом этапе декларативная разметка элемента управления содержимым должна выглядеть следующим образом:

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

страница включает три веб-элемента управления: текстовое поле, кнопку и метку.

Рис. 03. страница включает три веб-элемента управления: текстовое поле, кнопку и метку (щелкните, чтобы просмотреть изображение с полным размером).

Откройте страницу в браузере и просмотрите исходный код HTML. Как показано ниже, id значения HTML-элементов для элементов управления TextBox, Button и Label представляют собой сочетание ID значений веб-элементов управления и ID значений контейнеров именования на странице.

<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 служат контейнерами именования. Как следствие, и вносят отображаемые ID значения вложенных элементов управления. Возьмем id атрибут текстового поля, например: ctl00_MainContent_Age. Помните, что значение ID элемента управления TextBox было Age. Он имеет префикс с IDным значением элемента управления ContentPlaceHolder MainContent. Кроме того, это значение добавляется с префиксом ID значения главной страницы ctl00. Результат «NET» является id значением атрибута, состоящим из ID значений главной страницы, элемента управления ContentPlaceHolder и самого текстового поля.

Это поведение показано на рис. 4. Чтобы определить отображаемые id текстового поля Age, начните со значения ID элемента управления TextBox Age. Затем наработаем вверх по иерархии элементов управления. В каждом контейнере именования (эти узлы имеют цвет печь) добавьте префикс к текущему отображаемому id с idконтейнера именования.

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

Рис. 04. отображаемые id атрибуты основаны на ID значениях контейнеров именования

Note

Как мы обсуждали, ctl00 часть отображаемого id атрибута составляет ID значение главной страницы, но вы, возможно, захотите узнать о том, как это ID было получено. Мы не указали его ни в каком месте главной страницы, ни на странице содержимого. Большинство серверных элементов управления на странице ASP.NET добавляются явным образом через декларативную разметку страницы. Элемент управления ContentPlaceHolder MainContent был явно указан в разметке Site.master. Текстовое поле Age было определено IDIssues.aspxразметке. Можно указать ID значения для этих типов элементов управления с помощью окно свойств или декларативного синтаксиса. Другие элементы управления, как и Главная страница, не определяются в декларативной разметке. Следовательно, значения их ID должны быть автоматически созданы для нас. Подсистема ASP.NET устанавливает значения ID во время выполнения для тех элементов управления, идентификаторы которых не были заданы явным образом. Он использует шаблон именования ctlXX, где XX — это последовательно увеличивающееся целочисленное значение.

Так как Главная страница выступает в качестве контейнера именования, веб-элементы управления, определенные на главной странице, также имеют измененные значения id атрибутов. Например, метка DisplayDate, добавленная на главную страницу, в учебнике Создание макета на уровне сайта с помощью главных страниц содержит следующую подготовленную разметку:

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

Обратите внимание, что атрибут id включает значение ID главной страницы (ctl00) и ID значение веб-элемента управления Label (DateDisplay).

Шаг 3. Программная ссылка на веб-элементы управления черезFindControl

Каждый серверный элемент управления ASP.NET содержит метод FindControl("controlID"), который ищет в потомках элемента управления элемент управления с именем ControlID. Если такой элемент управления найден, он возвращается; Если соответствующий элемент управления не найден, FindControl возвращает Nothing.

FindControl удобно использовать в сценариях, где требуется доступ к элементу управления, но у вас нет прямой ссылки на него. Например, при работе с веб-элементами управления данными, такими как GridView, элементы управления в полях GridView определяются один раз в декларативном синтаксисе, но во время выполнения экземпляр элемента управления создается для каждой строки GridView. Следовательно, элементы управления, созданные во время выполнения, существуют, но у нас нет прямой ссылки, доступной из класса кода программной части. В результате необходимо использовать FindControl для программной работы с конкретным элементом управления в полях GridView. (Дополнительные сведения об использовании FindControl для доступа к элементам управления в шаблонах веб-элемента управления данными см. в разделе настраиваемое форматирование на основе данных.) Такой же сценарий возникает при динамическом добавлении веб-элементов управления в веб-форму, которая обсуждается в разделе Создание пользовательских интерфейсов платформа динамических данных записи.

Чтобы продемонстрировать использование метода FindControl для поиска элементов управления на странице содержимого, создайте обработчик событий для события Click SubmitButton. В обработчике событий добавьте следующий код, который программно ссылается на текстовое поле Age и Results метку с помощью метода FindControl, а затем отображает сообщение в Results на основе входных данных пользователя.

Note

Конечно, не нужно использовать FindControl для ссылки на элементы управления Label и TextBox в этом примере. Мы могли бы ссылаться на них напрямую через их ID значения свойств. Я использую FindControl здесь, чтобы продемонстрировать, что происходит при использовании FindControl на странице содержимого.

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, они семантически эквивалентны. Вспомним, что все серверные элементы управления ASP.NET включают метод FindControl. Это включает класс Page, из которого все классы кода программной части ASP.NET должны быть производными от. Таким образом, вызов FindControl("controlID") эквивалентен вызову Page.FindControl("controlID"). при условии, что вы не переопределили метод FindControl в классе кода программной части или в пользовательском базовом классе.

После ввода этого кода перейдите на страницу IDIssues.aspx в браузере, введите свой возраст и нажмите кнопку "Отправить". При нажатии кнопки "Отправить" возникает NullReferenceException (см. рис. 5).

создается NullReferenceException

Рис. 05. вызывается NullReferenceException (щелкните, чтобы просмотреть изображение с полным размером)

Если задать точку останова в обработчике событий SubmitButton_Click, то вы увидите, что оба вызова FindControl возвращают Nothing. NullReferenceException возникает при попытке доступа к свойству Text текстового поля Age.

Проблема заключается в том, что Control.FindControl выполняет поиск только тех потомков элемента управления, которые находятся в одном контейнере именования. Поскольку Главная страница создает новый контейнер именования, вызов Page.FindControl("controlID") никогда не пермеатес объект главной страницы ctl00. (См. рис. 4 для просмотра иерархии элементов управления, которая показывает объект Page как родительский для объекта главной страницы ctl00.) Поэтому текстовое поле Results метка и Age не найдены, а ResultsLabel и AgeTextBox присваиваются значения Nothing.

Существует два обходных решения этой проблемы: можно детализировать по одному контейнеру именования для соответствующего элемента управления. или можно создать собственный метод FindControl, пермеатес контейнеры именования. Давайте рассмотрим каждый из этих вариантов.

Детализация соответствующего контейнера именования

Чтобы использовать FindControl для ссылки на Results метку или в текстовое поле Age, необходимо вызвать FindControl из элемента управления предка в том же контейнере именования. Как показано на рис. 4, элемент управления MainContent ContentPlaceHolder является единственным предком Results или Age, находящихся в одном и том же контейнере именования. Иными словами, вызов метода FindControl из элемента управления MainContent, как показано в приведенном ниже фрагменте кода, правильно возвращает ссылку на элементы управления Results или Age.

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

Однако мы не можем работать с MainContent ContentPlaceHolder из класса кода программной части страницы содержимого, используя приведенный выше синтаксис, так как ContentPlaceHolder определен на главной странице. Вместо этого для получения ссылки на MainContentнеобходимо использовать FindControl. Замените код в обработчике событий SubmitButton_Click следующими изменениями:

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 будет создано исключение. Если задать точку останова в обработчике событий SubmitButton_Click, вы увидите, что это исключение возникает при попытке вызова метода FindControl объекта MainContent. Объект MainContent равен Nothing, так как метод FindControl не может располагаться в объекте с именем "MainContent". Основная причина такой же, как и для Results меток и Age элементов управления TextBox: FindControl начинает поиск с верхней части иерархии элементов управления и не проникнуть контейнеры именования, но MainContent ContentPlaceHolder находится на главной странице, которая является контейнером именования.

Прежде чем можно будет использовать FindControl для получения ссылки на MainContent, сначала требуется ссылка на элемент управления главной страницы. После получения ссылки на главную страницу можно получить ссылку на MainContent ContentPlaceHolder через FindControl и, в нем, ссылаться на текстовое поле Results Label и Age (опять же, с помощью FindControl). Но как получить ссылку на главную страницу? Изучив атрибуты id в отображаемой разметке, очевидно, что ID значение главной страницы ctl00. Поэтому можно использовать Page.FindControl("ctl00") для получения ссылки на главную страницу, а затем использовать этот объект для получения ссылки на MainContentи т. д. Эта логика показана в следующем фрагменте кода:

'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. Не рекомендуется делать предположения об автоматически сформированных значениях.

К счастью, ссылка на главную страницу доступна через свойство Master класса Page. Таким образом, вместо того чтобы использовать FindControl("ctl00") для получения ссылки на главную страницу для доступа к MainContent ContentPlaceHolder, мы можем использовать Page.Master.FindControl("MainContent"). Обновите обработчик событий SubmitButton_Click следующим кодом:

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 метке, как ожидалось.

возраст пользователя отображается в метке

Рис. 06. возраст пользователя отображается в метке (щелкните, чтобы просмотреть изображение с полным размером).

Рекурсивный поиск по контейнерам именования

Причина, по которой предыдущий пример кода ссылался на MainContent элемент управления ContentPlaceHolder с главной страницы, а затем Results метка и Age элементы управления TextBox из MainContent, является потому, что метод Control.FindControl выполняет поиск только в контейнере именования элемента управления. Наличие FindControl в контейнере именования имеет смысл в большинстве сценариев, поскольку два элемента управления в двух разных контейнерах именования могут иметь одинаковые значения ID. Рассмотрим регистр GridView, определяющий веб-элемент управления Label с именем ProductName в одном из его полей TemplateField. Когда данные привязываются к GridView во время выполнения, для каждой строки GridView создается ProductName метка. Если FindControl выполнить поиск по всем контейнерам именования и назвать Page.FindControl("ProductName"), то какой экземпляр метки должен возвращаться FindControl? ProductName метку в первой строке GridView? Он находится в последней строке?

Поэтому в большинстве случаев есть смысл в контейнере именования только элемента управленияControl.FindControl поиска. Но существуют и другие случаи, в том числе те, у которых имеется уникальный ID по всем контейнерам именования, и необходимо избегать необходимости в тщательной ссылке на каждый контейнер именования в иерархии элементов управления для доступа к элементу управления. Наличие FindControl вариант, который рекурсивно ищет все контейнеры именования, имеет смысл. К сожалению, .NET Framework не включает такой метод.

Хорошая новость состоит в том, что мы можем создать собственный метод FindControl, который рекурсивно ищет все контейнеры именования. На самом деле, использование методов расширения можно присвоить методу FindControlRecursive к классу Control, чтобы сопровождать его существующим методом FindControl.

Note

Методы расширения — это новая функция для C# 3,0 и Visual Basic 9, которые являются языками, поставляемыми с .NET Framework версиями 3,5 и Visual Studio 2008. Вкратце, методы расширения позволяют разработчику создавать новый метод для существующего типа класса с помощью специального синтаксиса. Дополнительные сведения об этой полезной функции см. в моей статье расширение функциональности базового типа с помощью методов расширения.

Чтобы создать метод расширения, добавьте новый файл в папку App_Code с именем PageExtensionMethods.vb. Добавьте метод расширения с именем FindControlRecursive, который принимает в качестве входных данных параметр String с именем controlID. Чтобы методы расширения работали правильно, крайне важно, чтобы класс был помечен как Module и что методы расширения имеют префикс <Extension()> атрибута. Более того, все методы расширения должны принять в качестве первого параметра объект типа, к которому применяется метод расширения.

Добавьте следующий код в файл PageExtensionMethods.vb, чтобы определить этот Module и метод расширения FindControlRecursive:

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. Замените их вызовами Page.FindControlRecursive("controlID"). О методах расширения следует присутствовать непосредственно в раскрывающихся списках IntelliSense. Как показано на рис. 7, при вводе Page и последующем нажатии FindControlRecursive метод включается в раскрывающийся список IntelliSense вместе с другими методами класса Control.

методы расширения включены в раскрывающиеся окна IntelliSense

Рис. 07. методы расширения включены в раскрывающиеся меню IntelliSense (щелкните, чтобы просмотреть изображение с полным размером)

Введите следующий код в обработчик событий SubmitButton_Click, а затем протестируйте его, перейдя на страницу, введя свой возраст и нажав кнопку "Отправить". Как показано на рис. 6, полученный результат будет представлять собой сообщение "срок давно года устарел!".

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 нельзя использовать методы расширения. Вместо этого необходимо реализовать метод FindControlRecursive в вспомогательном классе. Рик Штраль имеет такой пример в записи блога, ASP.NET главного pages и FindControl.

Шаг 4. Использование правильного значения атрибутаidв скрипте на стороне клиента

Как отмечалось в этом учебнике, в клиентской части скрипта для программной ссылки на определенный элемент HTML часто используется idный атрибут веб-элемента управления. Например, следующий код JavaScript ссылается на элемент HTML по его id, а затем отображает его значение в модальном окне сообщения:

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

Помните, что на страницах ASP.NET, не содержащих контейнер именования, отображаемый атрибут id элемента HTML идентичен значению свойства ID веб-элемента управления. В связи с этим в коде JavaScript для id значений атрибутов используется жесткий код. Это значит, что если вы хотите получить доступ к веб-элементу управления TextBox Age с помощью клиентского скрипта, сделайте это с помощью вызова document.getElementById("Age").

Проблема этого подхода заключается в том, что при использовании главных страниц (или других элементов управления контейнера именования) отображаемый HTML-id не является синонимом свойства ID веб-элемента управления. Первым наклона может быть посещение страницы в браузере и просмотр источника, чтобы определить фактический атрибут id. Зная значение, отображаемое id, можно вставить его в вызов getElementById, чтобы получить доступ к элементу HTML, с которым необходимо работать с помощью клиентского скрипта. Этот подход является менее идеальным, поскольку некоторые изменения в иерархии элементов управления страницы или изменения свойств ID элементов управления именованием изменяют результирующий id атрибут, тем самым нарушая код JavaScript.

Хорошая новость заключается в том, что отображаемое значение id атрибута доступно в коде на стороне сервера через свойствоClientIDвеб-элемента управления. Это свойство следует использовать для определения значения атрибута id, используемого в скрипте на стороне клиента. Например, чтобы добавить на страницу функцию JavaScript, которая при вызове отображает значение текстового поля Age в модальном окне сообщения, добавьте следующий код в обработчик событий Page_Load:

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. Если открыть эту страницу в браузере и просмотреть исходный код HTML, вы найдете следующий код 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>

Обратите внимание, что в вызове getElementByIdотображается правильное значение id атрибута ctl00_MainContent_Age. Так как это значение вычисляется во время выполнения, оно работает независимо от последующих изменений в иерархии элементов управления страницы.

Note

В этом примере JavaScript просто показано, как добавить функцию JavaScript, которая правильно ссылается на HTML-элемент, отображаемый серверным элементом управления. Чтобы использовать эту функцию, необходимо создать дополнительный код JavaScript для вызова функции при загрузке документа или при выполнении определенного действия пользователя. Дополнительные сведения об этих и связанных разделах см. в статье Работа со сценарием на стороне клиента.

Сводка

Некоторые серверные элементы управления ASP.NET действуют как контейнеры именования, что влияет на отображаемые id значения атрибутов их дочерних элементов управления, а также на область элементов управления, канвассед методом FindControl. В отношении главных страниц как Главная страница, так и ее элементы управления ContentPlaceHolder являются контейнерами именования. Следовательно, необходимо положить немного больше работы, чтобы программно ссылаться на элементы управления на странице содержимого с помощью FindControl. В этом учебнике мы рассмотрели два метода: детализация элемента управления ContentPlaceHolder и вызов метода FindControl. и пошаговая реализация собственного FindControl, которая рекурсивно выполняет поиск по всем контейнерам именования.

Наряду с тем, что контейнеры именования проблем на стороне сервера содержат ссылки на веб-элементы управления, существуют и проблемы на стороне клиента. При отсутствии контейнеров именования значение свойства ID веб-элемента управления и отображаемое id значение атрибута — одно в одном. Но с добавлением контейнера именования отображаемый id атрибут содержит как ID значения веб-элемента управления, так и контейнеры именования в происхождение иерархии элементов управления. Эти проблемы именования не являются проблемой, если вы используете свойство ClientID веб-элемента управления для определения отображаемого id значения атрибута в скрипте на стороне клиента.

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

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

Дополнительные сведения о разделах, обсуждаемых в этом руководстве, см. в следующих ресурсах:

Об авторе

Скотт Митчелл, автор нескольких книг по ASP/ASP. NET и основатель 4GuysFromRolla.com, работал с веб-технологиями майкрософт с 1998. Скотт работает как независимый консультант, преподаватель и модуль записи. Его последняя книга — Sams обучать себя ASP.NET 3,5 за 24 часа. Скотт можно получить по адресу mitchell@4GuysFromRolla.com или через свой блог по адресу http://ScottOnWriting.NET.

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

Эта серия руководств была рассмотрена многими полезными рецензентами. Потенциальным рецензентам для этого учебника были Зак Jones и Сучи Барнержи. Хотите ознакомиться с моими будущими статьями MSDN? Если это так, расположите строку в mitchell@4GuysFromRolla.com.