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

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

Загрузить PDF-файл

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

Введение

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

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

Примечание

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

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

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

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

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

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

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

Visual Studio автоматически создает элемент управления контентом для каждого из четырех contentPlaceHolders страницы master. Как указано в учебнике Multiple ContentPlaceHolders and Default Content Content , если элемент управления контентом отсутствует, вместо него создается содержимое contentPlaceHolder по умолчанию на странице master. QuickLoginUI Так как и LeftColumnContent ContentPlaceHolders содержат подходящую разметку по умолчанию для этой страницы, удалите соответствующие элементы управления Контентом из 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>

В учебнике Указание заголовка, метатегов и других заголовков 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. Далее на странице добавьте веб-элементы управления Кнопка и Веб-элемент управления Метка. Задайте для свойств TextBox ID и Columns значение Age и 3 соответственно. Задайте для свойств и ID Button Text значение "Отправить" и SubmitButton. Очистите свойство Label 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.

Страница содержит три веб-элемента управления: TextBox, Кнопка и Метка

Рис. 03. Страница содержит три веб-элемента управления: Текстовое поле, Кнопка и Метка (щелкните для просмотра полноразмерного изображения)

Перейдите на страницу в браузере и просмотрите источник HTML. Как показано в приведенной ниже разметке, id значения элементов HTML для элементов управления TextBox, Button и Label Web представляют собой сочетание значений 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>

Как упоминалось ранее в этом руководстве, как страница master, так и ее ContentPlaceHolders служат в качестве контейнеров именования. Следовательно, оба элемента управления вносят отображаемые ID значения вложенных элементов управления. Возьмем атрибут TextBox id , например : ctl00_MainContent_Age. Помните, что для элемента управления ID TextBox использовалось Ageзначение . Он имеет префикс со значением элемента управления ID ContentPlaceHolder, MainContent. Кроме того, это значение имеет префикс со значением страницы ID master , ctl00. Результатом является значение атрибутаid, состоящее из ID значений страницы master, элемента управления ContentPlaceHolder и самого элемента TextBox.

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

Атрибуты идентификатора для просмотра основаны на значениях идентификаторов контейнеров именования.

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

Примечание

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

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

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

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

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

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

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

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

Примечание

Конечно, нам не нужно использовать для 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. A NullReferenceException is Raised (Click to view full-size image)

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

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

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

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

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

Прежде чем мы сможем использовать FindControl для получения ссылки на MainContent, сначала потребуется ссылка на элемент управления master страницы. Получив ссылку на страницу master, мы можем получить ссылку на MainContent ContentPlaceHolder через FindControl , а затем ссылки на Results Label и Age TextBox (опять же с помощью ).FindControl Но как получить ссылку на страницу master? При проверке id атрибутов в отрисоченной разметке становится очевидно, что значение страницы ID master равно ctl00. Поэтому можно использовать Page.FindControl("ctl00") для получения ссылки на страницу master, а затем использовать этот объект для получения ссылки на 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 страница master всегда будет иметь значение ctl00. Никогда не рекомендуется делать предположения об автоматически создаваемых значениях.

К счастью, ссылка на страницу master доступна через Page свойство класса Master . Поэтому вместо того, чтобы использовать FindControl("ctl00") для получения ссылки на страницу master для доступа 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 со страницы master, а затем Results на элементы управления Label и Age TextBox из MainContent, заключается в Control.FindControl том, что метод выполняет поиск только в контейнере именования Control. В FindControl большинстве сценариев имеет смысл оставаться в контейнере именования, так как два элемента управления в двух разных контейнерах именования могут иметь одинаковые ID значения. Рассмотрим случай GridView, который определяет веб-элемент управления Label с именем ProductName в одном из его TemplateFields. Когда данные привязаны к GridView во время выполнения, для каждой ProductName строки GridView создается метка. Если FindControl выполнен поиск по всем контейнерам именования и мы вызвали Page.FindControl("ProductName"), какой экземпляр Label должен возвращать FindControl ? Метка ProductName в первой строке GridView? В последней строке?

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

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

Примечание

Методы расширения — это новая функция 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. Методы расширения включены в Drop-Downs 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

Примечание

Так как методы расширения являются новыми для C# 3.0 и Visual Basic 9, при использовании Visual Studio 2005 нельзя использовать методы расширения. Вместо этого необходимо реализовать FindControlRecursive метод во вспомогательном классе. Рик Штрал имеет такой пример в своем блоге, ASP.NET Maser Pages и FindControl.

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

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

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

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

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

Хорошей новостью id является то, что отображаемое значение атрибута доступно в серверном коде через свойство веб-элемента управленияClientID. Это свойство следует использовать для определения значения атрибута, используемого id в клиентском скрипте. Например, чтобы добавить функцию JavaScript на страницу, которая при вызове отображает значение Age TextBox в модальном окне сообщения, добавьте следующий код в 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)

Приведенный выше код внедряет значение Age свойства TextBox ClientID в вызов 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>

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

Примечание

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

Сводка

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

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

Счастливого программирования!

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

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

Об авторе

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

Особая благодарность

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