Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Иллюстрирует, как элементы управления ContentPlaceHolder служат контейнером имен и поэтому затрудняется программная работа с элементом управления (с помощью FindControl). Рассмотрим эту проблему и обходные пути. Также описывается, как программно получить доступ к полученному значению ClientID.
Введение
Все серверные элементы управления ASP.NET включают свойство ID, которое уникально идентифицирует элемент управления и является средством программного обращения к элементу управления в классе code-behind. Аналогичным образом элементы в HTML-документе могут включать id атрибут, который однозначно идентифицирует элемент. Эти id значения часто используются в клиентском скрипте для программной ссылки на определенный элемент HTML. Учитывая это, можно предположить, что при отображении серверного элемента управления ASP.NET в HTML его ID значение используется в качестве id значения отображенного HTML-элемента. Это не обязательно так, так как в определенных обстоятельствах один элемент управления с одним ID значением может отображаться несколько раз в отрисованной разметке. Рассмотрим элемент управления GridView, включающий поле шаблона с контролем 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 или другом контейнере именования.
Как вы могли предположить, главные страницы и ContentPlaceHolders реализуются как контейнеры именования. В этом руководстве мы рассмотрим, как эталонные страницы влияют на значения элементов id HTML и способы программно ссылаться на веб-элементы управления на странице содержимого с помощью FindControl.
Шаг 1. Добавление новой страницы ASP.NET
Чтобы продемонстрировать основные понятия, описанные в этом руководстве, давайте добавим новую страницу ASP.NET на наш веб-сайт. Создайте новую страницу содержимого с именем IDIssues.aspx в корневой папке, привязав ее к главной Site.master странице.
Рис. 01. Добавление страницы IDIssues.aspx содержимого в корневую папку
Visual Studio автоматически создает элемент управления контентом для каждого из четырех ContentPlaceHolders на главной странице. Как отмечалось в руководстве по нескольким ContentPlaceHolders и содержимому по умолчанию, если элемент управления контентом отсутствует, вместо этого выводится содержимое ContentPlaceHolder главной страницы по умолчанию. Так как ContentPlaceHolders QuickLoginUI и LeftColumnContent содержат подходящую разметку по умолчанию для этой страницы, продолжайте и удалите соответствующие элементы управления содержимым из 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 как "Control ID Naming Issues" и ~/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 Web. На странице добавьте дальше элемент управления веб-кнопкой и элемент управления веб-меткой. Установите свойства ID и Columns для TextBox на значения Age и 3 соответственно. Задайте для кнопки Text и ID свойства значение "Отправить" и SubmitButton. Удалите свойство Label Text и задайте для него значение IDResults.
На этом этапе декларативная разметка элемента управления контентом должна выглядеть следующим образом:
<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 Web controls являются сочетанием 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>
Как отмечалось ранее в этом руководстве, эталонная страница и ее ContentPlaceHolders служат контейнерами именования. Следовательно, оба вносят визуализированные ID значения вложенных элементов управления. Возьмите атрибут TextBoxid, например: ctl00_MainContent_Age Помните, что значение элемента управления ID TextBox было Age. Это префиксируется со значением элемента управления ID ContentPlaceHolder. MainContent Кроме того, это значение префиксируется со значением IDглавной страницыctl00. Чистый id эффект — это значение атрибута, состоящее из ID значений главной страницы, элемента управления ContentPlaceHolder и самого TextBox.
Рис. 4 иллюстрирует это поведение. Чтобы определить отрисовку id текстового Age поля, начните со ID значения элемента управления TextBox. Age Затем постепенно продвигайтесь вверх по иерархии управления. На каждом контейнере именования (узлы, выделенные персиковым цветом), добавьте префикс текущему элементу id, используя префикс контейнера id.
Рис. 04. Отрисованные id атрибуты основаны на ID значениях контейнеров именования
Примечание.
Как мы обсуждали, часть отрисованного ctl00 атрибута составляет ID значение главной страницы, но вы можете задаться вопросом, как появилось это ID значение. Мы не указали его где-либо на главной странице или странице содержимого. Большинство элементов управления серверами на странице ASP.NET добавляются явным образом через декларативную разметку страницы. Элемент MainContent управления ContentPlaceHolder был явно указан в разметке Site.master; элемент управления TextBox был определен в разметке 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 создается экземпляр элемента управления. Следовательно, элементы управления, созданные во время выполнения, существуют, но у нас нет прямой ссылки, доступной из класса code-behind. В результате необходимо использовать FindControl для программной работы с определенным элементом управления в поле управления GridView. (Дополнительные сведения об использовании FindControl для доступа к элементам управления в шаблонах веб-элемента управления данными см. в разделе "Настраиваемое форматирование на основе данных".) Этот же сценарий происходит при динамическом добавлении веб-элементов управления в веб-форму, тема, описанная в разделе "Создание пользовательских интерфейсов динамической записи данных".
Чтобы проиллюстрировать использование FindControl метода для поиска элементов управления на странице содержимого, создайте обработчик событий для SubmitButtonClick события. В обработчике событий добавьте следующий код, который программно ссылается на Age текстовое поле и Results метку с помощью 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).
Рис. 05: Возникает NullReferenceException (Щелкните, чтобы просмотреть изображение в полном размере)
Если установить точку останова в обработчике SubmitButton_Click событий, вы увидите, что оба вызова FindControl возвращают Nothing. Вызывается NullReferenceException при попытке получить доступ к свойству Age TextBox Text .
Проблема заключается в том, что 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 определен на главной странице. Вместо этого мы должны использовать 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 находится на главной странице, которая является контейнером именования.
Прежде чем использовать FindControl для получения ссылки на MainContent, нам сначала потребуется ссылка на управляющий элемент мастер-страницы. Получив ссылку на главную страницу, мы можем получить ссылку на MainContent ContentPlaceHolder с помощью FindControl, а затем ссылки на Results метку и Age текстовое поле (опять же, с использованием FindControl). Но как получить ссылку на главную страницу? Проверяя атрибуты id в отрисованной разметке, очевидно, что значение главной страницы IDctl00. Таким образом, мы можем использовать 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. Никогда не рекомендуется делать предположения об автогенерированных значениях.
К счастью, ссылка на мастер-страницу доступна через свойство класса PageMaster. Поэтому вместо использования 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. Возраст пользователя отображается в метке (щелкните, чтобы просмотреть изображение полного размера)
Рекурсивный поиск внутри контейнеров именования
Причина, по которой предыдущий пример кода ссылается на элемент управления FindControl в контейнере именования имеет смысл в большинстве сценариев, так как два элемента управления в двух разных контейнерах именования могут иметь одинаковые ID значения. Рассмотрим случай GridView, который определяет веб-элемент управления Label, названный ProductName в одном из его TemplateFields. Если данные привязаны к GridView во время выполнения, ProductName метка создается для каждой строки GridView. Если FindControl выполняется поиск по всем контейнерам именования и вызывается Page.FindControl("ProductName"), какой экземпляр Label должен FindControl возвращать? Метка ProductName в первой строке GridView? Тот в последней строке?
Поиск только в контейнере именования управления имеет смысл в большинстве случаев. Но существуют и другие случаи, например тот, с которым мы сталкиваемся, где у нас есть уникальный 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. Для правильной работы методов расширения важно, чтобы класс был помечен как a 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.
Рис. 07. Методы расширения включены в раскрывающийся список IntelliSense (щелкните, чтобы просмотреть изображение полного размера)
Введите следующий код в SubmitButton_Click обработчик событий, а затем протестируйте его, перейдя на страницу, введя возраст и нажав кнопку "Отправить". Как показано на рис. 6, результатом будет сообщение: "Вам age лет!"
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значения атрибута в клиентском скрипте
Как указывалось во введении этого руководства, атрибут отрисовки 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 . Из-за этого возникает соблазн жёстко закодировать значения атрибутов в код JavaScript. То есть, если вы знаете, что вы хотите получить доступ к Age веб-элементу управления TextBox через клиентский скрипт, сделайте это с помощью вызова document.getElementById("Age").
Проблема с этим подходом заключается в том, что при использовании основных страниц (или других элементов управления контейнера именования) сгенерированный 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 в вызов getElementByIdJavaScript. Если вы посещаете эту страницу через браузер и просматриваете 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>
Обратите внимание, как правильное значение атрибута idctl00_MainContent_Age появляется в вызове getElementById. Так как это значение вычисляется во время выполнения, оно работает независимо от последующих изменений в иерархии элементов управления страницами.
Примечание.
В этом примере JavaScript просто показано, как добавить функцию JavaScript, которая правильно ссылается на HTML-элемент, отображаемый серверным элементом управления. Чтобы использовать эту функцию, необходимо создать дополнительный JavaScript для вызова функции при загрузке документа или при выполнении определенных действий пользователя. Дополнительные сведения об этих и связанных разделах см. в статье "Работа с клиентским скриптом".
Итоги
Некоторые серверные элементы управления ASP.NET действуют как контейнеры именования, что влияет на сгенерированные id значения атрибутов их дочерних элементов управления, а также на охват элементов управления, определяемый методом FindControl. Что касается главных страниц, то как главная страница, так и элементы управления ContentPlaceHolder называют контейнеры. Следовательно, нам нужно приложить немного больше усилий, чтобы программно обращаться к элементам управления на странице контента с помощью FindControl. В этом руководстве мы рассмотрели два метода: детализацию элемента управления ContentPlaceHolder и вызов его FindControl метода, а также развертывание собственной FindControl реализации, которая рекурсивно выполняет поиск по всем контейнерам именования.
Кроме серверных проблем, которые выясняются из-за именования контейнеров, вызывающего сложности с ссылками на веб-элементы управления, существуют также клиентские проблемы. Если контейнеры именования отсутствуют, значение свойства веб-управления ID и отображаемое значение атрибута id равны. Но при добавлении контейнера именования отрисованный id атрибут включает как значения веб-элемента управления, так и ID контейнеры именования в наследственную иерархию элементов управления. Эти проблемы с именованием не представляют проблемы, если вы используете свойство веб-элемента ClientID, чтобы определить значение отрисованного атрибута id в клиентском скрипте.
Счастливое программирование!
Дополнительные материалы
Дополнительные сведения о разделах, описанных в этом руководстве, см. в следующих ресурсах:
-
ASP.NET эталонные страницы и
FindControl - Создание пользовательских интерфейсов для записи динамических данных
- Практическое руководство: Обращение к содержимому мастер-страницы ASP.NET
- Mater Pages: советы, трюки и ловушки
- Работа с клиентским скриптом
Об авторе
Скотт Митчелл, автор нескольких книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с технологиями Microsoft Web с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его последняя книга Изучите ASP.NET 3.5 за 24 часа с помощью Сэмс. С Скоттом можно связаться на mitchell@4GuysFromRolla.com или через его блог http://ScottOnWriting.NET.
Особое спасибо кому
Эта серия учебников была проверена многими полезными рецензентами. Ведущими экспертами-оценщиками этого руководства были Зак Джонс и Сучи Барнержи. Хотите просмотреть мои предстоящие статьи MSDN? Если да, напишите мне на mitchell@4GuysFromRolla.com.