内容页中的控件 ID 命名 (VB)

作者 :Scott Mitchell

下载 PDF

说明 ContentPlaceHolder 控件如何充当命名容器,从而使得难以通过 FindControl) 以编程方式使用控件 (。 查看此问题和解决方法。 还讨论了如何以编程方式访问生成的 ClientID 值。

简介

所有 ASP.NET 服务器控件都包含一个唯一 ID 标识控件的属性,它是在代码隐藏类中以编程方式访问控件的方式。 同样,HTML 文档中的元素可能包含唯一 id 标识元素的属性;这些 id 值通常用于客户端脚本以编程方式引用特定的 HTML 元素。 鉴于此,可以假定当 ASP.NET 服务器控件呈现为 HTML 时,其 ID 值将用作 id 呈现的 HTML 元素的值。 情况不一定如此,因为在某些情况下,具有单个值的单个 ID 控件可能会在呈现的标记中多次出现。 考虑一个 GridView 控件,该控件包含具有值为 的 Label Web 控件 IDProductNameTemplateField。 当 GridView 在运行时绑定到其数据源时,此标签将针对每个 GridView 行重复一次。 每个呈现的 Label 都需要一个唯 id 一值。

为了处理此类方案,ASP.NET 允许将某些控件表示为命名容器。 命名容器用作新的 ID 命名空间。 命名容器内出现的任何服务器控件的呈现 id 值都带有 ID 命名容器控件的 前缀。 例如, GridViewGridViewRow 类都是命名容器。 因此,在 GridView TemplateField 中定义的 Label 控件的 IDProductName 呈现 idGridViewID_GridViewRowID_ProductName为 。 由于 GridViewRowID 对于每个 GridView 行都是唯一的,因此生成的 id 值是唯一的。

注意

接口INamingContainer用于指示特定的 ASP.NET 服务器控件应充当命名容器。 接口 INamingContainer 不说明服务器控件必须实现的任何方法;而是将其用作标记。 在生成呈现标记时,如果控件实现此接口,则 ASP.NET 引擎会自动将其 ID 值作为其后代呈现 id 的属性值的前缀。 步骤 2 中更详细地讨论了此过程。

命名容器不仅会更改呈现 id 的属性值,还影响如何以编程方式从 ASP.NET 页的代码隐藏类引用控件。 方法 FindControl("controlID") 通常用于以编程方式引用 Web 控件。 但是, FindControl 不会渗透到命名容器中。 因此,不能直接使用 Page.FindControl 方法引用 GridView 或其他命名容器中的控件。

正如你推测的那样,母版页和 ContentPlaceHolders 都是作为命名容器实现的。 本教程介绍母版页如何影响 HTML 元素 id 值,以及使用 FindControl以编程方式引用内容页中的 Web 控件的方法。

步骤 1:添加新 ASP.NET 页面

为了演示本教程中讨论的概念,让我们向网站添加新 ASP.NET 页面。 在根文件夹中创建名为 IDIssues.aspx 的新内容页,并将其绑定到 Site.master 母版页。

将内容页IDIssues.aspx添加到根文件夹

图 01:将内容页 IDIssues.aspx 添加到根文件夹

Visual Studio 自动为母版页的四个 ContentPlaceHolders 创建一个 Content 控件。 如 多个 ContentPlaceHolders 和默认内容 教程中所述,如果不存在 Content 控件,则会改为发出母版页的默认 ContentPlaceHolder 内容。 QuickLoginUI由于 和 LeftColumnContent ContentPlaceHolders 包含适合此页的默认标记,因此请继续从 IDIssues.aspx中删除相应的 Content 控件。 此时,内容页的声明性标记应如下所示:

<%@ 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> ,并将其 titleurl 属性分别设置为“控件 ID 命名问题”和 ~/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 新站点地图条目立即反映在左列中的“课程”部分。

课程部分现在包含指向“控件 ID 命名问题”的链接

图 02:课程部分现在包含指向“控件 ID 命名问题”的链接

步骤 2:检查呈现的ID更改

为了更好地了解 ASP.NET 引擎对服务器控件的呈现 id 值所做的修改,让我们向页面添加一些 Web 控件 IDIssues.aspx ,然后查看发送到浏览器的呈现标记。 具体来说,键入文本“请输入你的年龄:”,然后键入 TextBox Web 控件。 在页面上的下一步添加按钮 Web 控件和标签 Web 控件。 将 TextBox 的 IDColumns 属性分别设置为 Age 和 3。 将按钮的 TextID 属性设置为“提交”和 SubmitButton。 清除 Label 的 Text 属性并将其设置为 IDResults

此时,Content 控件的声明性标记应如下所示:

<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 设计器查看时的页面。

页面包含三个 Web 控件:TextBox、Button 和 Label

图 03:页面包含三个 Web 控件:TextBox、按钮和标签 (单击以查看全尺寸图像)

通过浏览器访问页面,然后查看 HTML 源。 如以下标记所示,idTextBox、Button 和 Label Web 控件的 HTML 元素的值是 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>

如本教程前面所述,母版页及其 ContentPlaceHolders 都充当命名容器。 因此,两者都贡献其嵌套控件的呈现 ID 值。 以 TextBox 的属性 id 为例: ctl00_MainContent_Age。 回想一下,TextBox 控件的 ID 值为 Age。 此前缀为 ContentPlaceHolder 控件 ID 的值 MainContent。 此外,此值的前缀为母版页 ID 的值 ctl00。 净效果是一个 id 属性值,由母版页的值、ContentPlaceHolder 控件和 TextBox 本身组成 ID

图 4 演示了此行为。 若要确定 TextBox 的Age呈现id,请从 ID TextBox 控件 Age的值开始。 接下来,在控件层次结构中向上工作。 在每个命名容器 (具有桃色) 的节点处,以当前呈现 id 的命名容器为 id前缀。

呈现的 ID 属性基于命名容器的 ID 值

图 04:呈现 id 的属性基于 ID 命名容器的值

注意

正如我们所讨论的, ctl00 呈现 id 的属性部分构成了 ID 母版页的值,但你可能想知道此值 ID 是如何生成的。 我们没有在母版或内容页中的任何位置指定它。 ASP.NET 页中的大多数服务器控件都是通过页面的声明性标记显式添加的。 ContentPlaceHolder MainContent 控件在 的 Site.master标记中显式指定; Age TextBox 已定义 IDIssues.aspx标记。 可以通过属性窗口或声明性语法为这些类型的控件指定ID值。 其他控件(如母版页本身)未在声明性标记中定义。 因此,必须为我们自动生成其 ID 值。 ASP.NET 引擎 ID 在运行时为未显式设置 ID 的控件设置值。 它使用命名模式 ctlXX,其中 XX 是按顺序递增的整数值。

由于母版页本身充当命名容器,因此在母版页中定义的 Web 控件也更改了呈现的 id 属性值。 例如,在 DisplayDate “使用母版页创建 Site-Wide 布局”教程中添加到 母版页 的标签具有以下呈现标记:

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

请注意, id 属性包括母版页 ID 的值 () ctl00ID 标签 Web 控件的值 () DateDisplay

步骤 3:通过 以编程方式引用 Web 控件FindControl

每个 ASP.NET 服务器控件都包含一个 FindControl("controlID") 方法,用于在控件的后代中搜索名为 controlID 的控件。 如果找到此类控件,则返回该控件;如果未找到匹配的控件, FindControlNothing返回 。

FindControl 在需要访问控件但未直接引用控件的情况下非常有用。 例如,使用 GridView 等数据 Web 控件时,GridView 字段内的控件在声明性语法中定义一次,但在运行时,会为每个 GridView 行创建控件的实例。 因此,运行时生成的控件存在,但我们没有代码隐藏类提供的直接引用。 因此,我们需要使用 FindControl 以编程方式处理 GridView 字段中的特定控件。 (有关使用 FindControl 访问数据 Web 控件模板中的控件的详细信息,请参阅 基于数据的自定义格式设置。) 将 Web 控件动态添加到 Web 窗体时,会发生相同的方案,该主题在 创建动态数据输入用户界面中进行了讨论。

为了说明如何使用 FindControl 方法在内容页中搜索控件,请为 SubmitButtonClick 事件创建事件处理程序。 在事件处理程序中,添加以下代码,这些代码使用 FindControl 方法以编程方式引用 Age TextBox 和 Results Label,然后根据用户的输入在 中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 是引发 (单击以查看全尺寸图像)

如果在事件处理程序中设置断点, SubmitButton_Click 将看到两个调用都返回 FindControlNothingNullReferenceException当我们尝试访问 TextBox 的 Text 属性时,Age将引发 。

问题是仅 Control.FindControl 搜索位于同一命名容器中的 Control 的后代。 由于母版页构成一个新的命名容器,因此对 Page.FindControl("controlID") 的调用永远不会渗透到母版页对象 ctl00中。 (请参阅图 4 查看控件层次结构,该层次结构将对象显示为Page母版页对象的父级 ctl00。) 因此,Results找不到 Label 和 Age TextBox,并且ResultsLabelAgeTextBox分配了 值Nothing

此挑战有两种解决方法:我们可以向下钻取(一次一个命名容器)到适当的控件:或者,我们可以创建我们自己的 FindControl 方法来渗透命名容器。 让我们检查其中每个选项。

钻取到相应的命名容器

若要使用 FindControl 引用 Label 或 Age TextBox,需要从同一命名容器中的上级控件调用 FindControlResults 。 如图 4 所示,MainContentContentPlaceHolder 控件是 或 Age 在同一命名容器中的唯一上级Results。 换句话说,从 控件调用 FindControl 方法(如下面的代码片段所示)正确返回对 或 Age 控件的ResultsMainContent引用。

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的引用之前,首先需要对母版页控件的引用。 一旦有了对母版页的引用,就可以通过使用 FindControl) 从那里再次 FindControl (Label 和 Age TextBox 引用,获取MainContent对 ContentPlaceHolder 的引用Results。 但是,如何获取对母版页的引用? 通过检查 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。 对自动生成的值进行假设从来不是一个好主意。

幸运的是,可通过 Page 类的 属性访问对母版页的 Master 引用。 因此,我们可以改Page.Master.FindControl("MainContent")用 来访问 ContentPlaceHolder,而不必使用 FindControl("ctl00") 获取母版页的引用来访问 MainContent ContentPlaceHolder。 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:“用户年龄”显示在“标签 (单击以查看全尺寸图像)

递归搜索命名容器

上一个代码示例从母版页引用 ContentPlaceHolder 控件,然后ResultsMainContent引用 MainContent Label 和 Age TextBox 控件的原因是方法Control.FindControl仅在 Control 的命名容器中搜索。 FindControl在大多数情况下,保留命名容器是有意义的,因为两个不同命名容器中的两个控件可能具有相同ID的值。 假设 GridView 在其 TemplateFields 之一中定义一个名为 ProductName 的标签 Web 控件。 当数据在运行时绑定到 GridView 时,将为每个 GridView 行创建一个 ProductName 标签。 如果 FindControl 通过所有命名容器进行搜索并调用 Page.FindControl("ProductName"),应 FindControl 返回哪个 Label 实例? 第 ProductName 一个 GridView 行中的标签? 最后一行中的那个?

因此,在大多数情况下, Control.FindControl 仅搜索 Control 的命名容器是有意义的。 但也有其他情况,例如我们面临的情况,在所有命名容器中都有一个唯 ID 一的,并且希望避免必须仔细引用控件层次结构中的每个命名容器来访问控件。 具有 FindControl 以递归方式搜索所有命名容器的变体也有意义。 遗憾的是,.NET Framework不包括此类方法。

好消息是,我们可以创建自己的 FindControl 方法,以递归方式搜索所有命名容器。 事实上,使用扩展方法,我们可以将 方法Control粘在 类上FindControlRecursive,以附带其现有FindControl方法。

注意

扩展方法是 C# 3.0 和 Visual Basic 9 的新增功能,它们是 .NET Framework 版本 3.5 和 Visual Studio 2008 附带的语言。 简言之,扩展方法允许开发人员通过特殊语法为现有类类型创建新方法。 有关此有用功能的详细信息,请参阅我的文章 :使用扩展方法扩展基类型功能

若要创建扩展方法,请将新文件添加到 App_Code 名为 PageExtensionMethods.vb的文件夹。 添加名为 FindControlRecursive 的扩展方法,该方法采用名为 的参数controlID作为输入String。 若要使扩展方法正常工作,必须将类标记为 , 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 Drop-Downs (单击以查看全尺寸图像)

在事件处理程序中 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 马瑟·Pages 和 FindControl

步骤 4:在 Client-Side 脚本中使用正确的id属性值

如本教程的简介中所述,Web 控件的呈现 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 属性与 Web 控件的 ID 属性值相同。 因此,将属性值中的 id 硬代码硬编码为 JavaScript 代码很诱人。 也就是说,如果知道要通过客户端脚本访问 Age TextBox Web 控件,请通过调用 document.getElementById("Age")来执行此操作。

此方法的问题在于,使用母版页 (或其他命名容器控件) 时,呈现的 HTML id 与 Web 控件的 ID 属性不同义。 你的第一个倾向可能是通过浏览器访问页面,并查看源以确定实际 id 属性。 知道呈现 id 的值后,可以将其粘贴到 的调用 getElementById 中,以通过客户端脚本访问需要使用的 HTML 元素。 此方法不太理想,因为对页面控件层次结构的某些更改或对命名控件的属性的更改 ID 会更改生成的 id 属性,从而破坏 JavaScript 代码。

好消息是,呈现的 id 属性值可通过 Web 控件的 ClientID 属性在服务器端代码中访问。 应使用此属性来确定 id 客户端脚本中使用的属性值。 例如,若要将 JavaScript 函数添加到调用时在模式消息框中显示 TextBox 值 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)

上述代码将 TextBox ClientID 属性的值Age注入到 对 getElementById的 JavaScript 调用中。 如果通过浏览器访问此页面并查看 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>

请注意正确的 id 属性值 ctl00_MainContent_Age在 对 getElementById的调用中如何显示。 由于此值是在运行时计算的,因此无论以后对页面控件层次结构的更改如何,它都有效。

注意

此 JavaScript 示例仅演示如何添加 JavaScript 函数,该函数正确引用由服务器控件呈现的 HTML 元素。 若要使用此函数,需要创作其他 JavaScript,以在文档加载或某些特定用户操作发生时调用函数。 有关这些主题和相关主题的详细信息,请阅读 使用 Client-Side 脚本

总结

某些 ASP.NET 服务器控件充当命名容器,这会影响其子代控件的呈现 id 属性值以及方法 FindControl 所绘制的控件的范围。 对于母版页,母版页本身及其 ContentPlaceHolder 控件都是命名容器。 因此,我们需要投入更多工作,以便使用 FindControl以编程方式引用内容页中的控件。 在本教程中,我们研究了两种技术:钻取到 ContentPlaceHolder 控件并调用其 FindControl 方法;以及滚动我们自己的 FindControl 实现,以递归方式搜索所有命名容器。

除了在引用 Web 控件方面引入的命名容器的服务器端问题外,还存在客户端问题。 在没有命名容器的情况下,Web 控件的 ID 属性值和呈现的 id 属性值相同。 但是,通过添加命名容器,呈现 id 的属性包括 ID Web 控件的值和命名容器 (其控件层次结构的祖先中的) 。 只要使用 Web 控件的 ClientID 属性来确定客户端脚本中呈现 id 的属性值,这些命名问题就不是问题。

编程快乐!

深入阅读

有关本教程中讨论的主题的详细信息,请参阅以下资源:

关于作者

Scott Mitchell 是多本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com 的创始人,自 1998 年以来一直从事 Microsoft Web 技术工作。 Scott 担任独立顾问、培训师和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 3.5。 可以在 上联系 mitchell@4GuysFromRolla.com 斯科特,也可以通过他的博客在 联系 http://ScottOnWriting.NET

特别感谢

本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是 Zack Jones 和 Suchi Barnerjee。 有兴趣查看我即将发布的 MSDN 文章? 如果是,请在 处 mitchell@4GuysFromRolla.com放置一行。