内容页中的控件 ID 命名 (VB)
作者 :斯科特·米切尔
说明 ContentPlaceHolder 控件如何充当命名容器,从而使以编程方式处理控件变得困难(通过 FindControl)。 查看此问题和解决方法。 还讨论了如何以编程方式访问生成的 ClientID 值。
简介
所有 ASP.NET 服务器控件都包含一个唯一 ID
标识控件的属性,并且是控件在代码隐藏类中以编程方式访问的方法。 同样,HTML 文档中的元素可能包含唯一 id
标识元素的属性;这些 id
值通常用于客户端脚本以编程方式引用特定 HTML 元素。 鉴于此,可以假定当 ASP.NET 服务器控件呈现到 HTML 中时,其 ID
值将用作 id
呈现的 HTML 元素的值。 这不一定是这种情况,因为在某些情况下,具有单个值的单个 ID
控件可能会在呈现的标记中多次出现。 请考虑一个 GridView 控件,该控件包含一个具有值为ProductName
标签 Web 控件ID
的 TemplateField。 当 GridView 在运行时绑定到其数据源时,每个 GridView 行都会重复此标签一次。 每个呈现的标签都需要唯一 id
值。
为了处理此类方案,ASP.NET 允许某些控件表示为命名容器。 命名容器用作新 ID
命名空间。 命名容器中显示的任何服务器控件的呈现 id
值都以 ID
命名容器控件为前缀。 例如, GridView
和 GridViewRow
类都是命名容器。 因此,在 GridView TemplateField ID
ProductName
中定义的标签控件的呈现 id
值为 GridViewID_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
母版页。
图 01:将内容页 IDIssues.aspx
添加到根文件夹
Visual Studio 会自动为母版页的四个 ContentPlaceHolders 创建内容控件。 如多 ContentPlaceHolders 和默认内容教程中所述,如果内容控件不存在母版页的默认 ContentPlaceHolder 内容,则会改为发出。 由于和 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>
并将其和url
属性分别设置为title
“控件 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
立即反映在左侧列的“课程”部分中。
图 02:“课程”部分现在包含指向“控件 ID 命名问题”的链接
步骤 2:检查呈现的ID
更改
为了更好地了解 ASP.NET 引擎对服务器控件的 id
呈现值所做的修改,让我们向页面添加一些 Web 控件 IDIssues.aspx
,然后查看发送到浏览器的呈现标记。 具体而言,键入文本“请输入你的年龄:”,后跟一个 TextBox Web 控件。 在页面上进一步添加按钮 Web 控件和标签 Web 控件。 将 TextBox 的 ID
和 Columns
属性分别设置为 Age
3 和 3。 将按钮 Text
和 ID
属性设置为“提交”和 SubmitButton
。 清除 Label 的属性 Text
并将其设置为 ID
Results
。
此时,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 设计器查看的页面。
图 03:页面包含三个 Web 控件:文本框、按钮和标签(单击以查看全尺寸图像)
通过浏览器访问页面,然后查看 HTML 源。 如以下标记所示,id
TextBox、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
属性值,由 ID
母版页、ContentPlaceHolder 控件和 TextBox 本身的值组成。
图 4 说明了此行为。 若要确定 TextBox 的Age
呈现id
,请从 ID
TextBox 控件的值开始。 Age
接下来,按照控制层次结构的方式进行操作。 在每个命名容器(具有桃色的节点)处,以命名容器的id
当前呈现id
前缀。
图 04:呈现 id
的属性基于 ID
命名容器的值
注意
正如我们所讨论的, ctl00
呈现 id
的属性部分构成了 ID
母版页的值,但你可能想知道此值 ID
是如何实现的。 我们没有在母版或内容页中的任何位置指定它。 ASP.NET 页中的大多数服务器控件通过页面的声明性标记显式添加。 MainContent
ContentPlaceHolder 控件在标记Site.master
中显式指定;Age
TextBox 已定义的IDIssues.aspx
标记。 我们可以通过属性窗口或声明性语法指定ID
这些类型的控件的值。 声明性标记中未定义其他控件,如母版页本身。 因此,必须为我们自动生成其 ID
值。 ASP.NET 引擎在运行时为未显式设置 ID 的控件设置 ID
值。 它使用命名模式 ctlXX
,其中 XX 是一个按顺序递增的整数值。
由于母版页本身充当命名容器,因此母版页中定义的 Web 控件也更改了呈现的 id
属性值。 例如,DisplayDate
我们在“使用母版页创建网站范围布局”教程中添加到母版页的标签具有以下呈现标记:
<span id="ctl00_DateDisplay">current date</span>
请注意,该 id
属性包括母版页 ID
的值(ctl00
)和 ID
标签 Web 控件的值(DateDisplay
)。
步骤 3:通过编程方式引用 Web 控件FindControl
每个 ASP.NET 服务器控件都包含一个 FindControl("controlID")
方法,该方法在控件的后代中搜索名为 controlID 的控件。 如果找到此类控件,则返回该控件;如果未找到匹配控件, FindControl
则 Nothing
返回 。
FindControl
在需要访问控件但没有直接引用控件的情况下,非常有用。 例如,处理 GridView 等数据 Web 控件时,GridView 字段中的控件在声明性语法中定义一次,但在运行时,会为每个 GridView 行创建控件实例。 因此,运行时生成的控件存在,但我们没有代码隐藏类提供的直接引用。 因此,我们需要用于 FindControl
以编程方式处理 GridView 字段中的特定控件。 (有关用于FindControl
访问数据 Web 控件模板中的控件的详细信息,请参阅基于数据的自定义格式设置。将 Web 控件动态添加到 Web 窗体时,会出现这种情况,这是创建动态数据输入用户界面中讨论的主题。
为了说明如何使用 FindControl
该方法搜索内容页中的控件,请为 SubmitButton
该事件 Click
创建事件处理程序。 在事件处理程序中,添加以下代码,该代码使用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)。
图 05:引发 A NullReferenceException
(单击以查看全尺寸图像)
如果在事件处理程序中SubmitButton_Click
设置断点,将看到两个要返回Nothing
的FindControl
调用。 当我们尝试访问 Age
TextBox Text
的属性时,将引发此NullReferenceException
事件。
问题是 Control.FindControl
,仅搜索 位于同一命名容器中的 Control 后代。 由于母版页构成新的命名容器,因此对 Page.FindControl("controlID")
从不渗透母版页对象的 ctl00
调用。 (请参阅图 4 以查看控件层次结构,该层次结构将对象显示为Page
母版页对象的父级ctl00
。)因此,Results
找不到 Label 和 Age
TextBox,并且ResultsLabel
AgeTextBox
已分配值 Nothing
。
此挑战有两种解决方法:我们可以向下钻取一个命名容器,一次一个命名容器,以适当的控制:或者,我们可以创建 FindControl
一个渗透到命名容器的方法。 让我们检查其中每个选项。
钻取到适当的命名容器
若要用于FindControl
引用 Label 或 Age
TextBox,我们需要从同一命名容器中的上级控件调用FindControl
Results
。 如图 4 所示,MainContent
ContentPlaceHolder 控件是同一命名容器中Results
的唯一上级。Age
换句话说,从控件调用FindControl
方法,如下面的代码片段所示,正确返回对或Age
控件的Results
MainContent
引用。
Dim ResultsLabel As Label = CType(MainContent.FindControl("Results"), Label)
Dim AgeTextBox As TextBox = CType(MainContent.FindControl("Age"), TextBox)
但是,由于 ContentPlaceHolder 是在母版页中定义的,因此无法使用上述语法从内容页的代码隐藏类中使用 MainContent
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
如果通过浏览器访问页面,请输入年龄,然后单击“提交”按钮,将引发 a NullReferenceException
。 如果在事件处理程序中 SubmitButton_Click
设置断点,则会在尝试调用 MainContent
对象的 FindControl
方法时看到此异常发生。 对象 MainContent
等于 Nothing
,因为 FindControl
该方法找不到名为“MainContent”的对象。 根本原因与 Results
Label 和 Age
TextBox 控件相同: FindControl
从控件层次结构顶部启动其搜索,并且不会渗透命名容器,但 MainContent
ContentPlaceHolder 位于母版页中,这是一个命名容器。
在可用于 FindControl
获取对母版页控件的引用 MainContent
之前,首先需要对母版页控件进行引用。 获得对母版页的引用后,可以通过该母版页获取对 MainContent
ContentPlaceHolder FindControl
的引用,并从那里获取对 Results
Label 和 Age
TextBox 的引用(再次通过使用 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
。 对自动生成的值做出假设从来不是个好主意。
幸运的是,可通过 Page
类的属性访问对母版页的 Master
引用。 因此,为了访问 MainContent
ContentPlaceHolder,我们不必用来FindControl("ctl00")
获取母版页的引用,而是可以使用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
引用MainContent
Control.FindControl
了 Label 和 Age
TextBox 控件的原因是该方法仅在控件的命名容器中进行搜索。 FindControl
在大多数情况下,在命名容器中保持命名容器是有意义的,因为两个不同的命名容器中的两个控件可能具有相同ID
的值。 请考虑 GridView 的情况,该网格视图定义在其模板字段之一内命名 ProductName
的标签 Web 控件。 当数据在运行时绑定到 GridView 时,会为每个 GridView 行创建一个 ProductName
标签。 如果 FindControl
搜索所有命名容器并调用 Page.FindControl("ProductName")
,应 FindControl
返回哪些 Label 实例? 第 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
名为 <FindControlRecursive
扩展方法,该方法用作名为 <String
/> 的参数的输入。 若要使扩展方法正常工作,必须将类标记为 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
将与其他类方法一起 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
注意
由于扩展方法不熟悉 C# 3.0 和 Visual Basic 9,因此如果使用 Visual Studio 2005,则无法使用扩展方法。 相反,你需要在帮助程序类中实现 FindControlRecursive
该方法。 里克·斯特拉尔 在他的博客文章 ASP.NET 马瑟·页面和 FindControl
。
步骤 4:在客户端脚本中使用正确的id
属性值
如本教程简介中所述,Web 控件的呈现 id
属性通常在客户端脚本中使用,以编程方式引用特定 HTML 元素。 例如,以下 JavaScript 按 HTML 元素引用 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")
访问。
此方法的问题是,使用母版页(或其他命名容器控件)时,呈现的 ID
HTML id
与 Web 控件的属性不同义。 你的第一个倾向可能是通过浏览器访问页面并查看源以确定实际 id
属性。 知道呈现 id
的值后,即可将其粘贴到调用中,以 getElementById
访问需要通过客户端脚本处理的 HTML 元素。 此方法不太理想,因为对页面控件层次结构的某些更改或更改 ID
命名控件的属性将更改生成的 id
属性,从而破坏 JavaScript 代码。
好消息是 id
,呈现的 ClientID
属性值可通过 Web 控件的属性在服务器端代码中访问。 应使用此属性来确定 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
注入到 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>
请注意, id
正确的属性值 ctl00_MainContent_Age
在调用 getElementById
中如何显示。 由于此值是在运行时计算的,因此无论以后对页面控件层次结构的更改如何,它都会起作用。
注意
此 JavaScript 示例仅演示如何添加一个 JavaScript 函数,该函数正确引用服务器控件呈现的 HTML 元素。 若要使用此函数,需要在文档加载时或某些特定的用户操作转译时创作其他 JavaScript 来调用该函数。 有关这些和相关主题的详细信息,请阅读 “使用客户端脚本”。
总结
某些 ASP.NET 服务器控件充当命名容器,这会影响其后代控件的 id
呈现属性值以及方法画布的 FindControl
控件的范围。 对于母版页,母版页本身及其 ContentPlaceHolder 控件都是命名容器。 因此,我们需要进一步提出一些工作,以编程方式引用内容页面中的控件。FindControl
在本教程中,我们研究了两种技术:钻取 ContentPlaceHolder 控件并调用其 FindControl
方法;并滚动我们自己的 FindControl
实现,以递归方式搜索所有命名容器。
除了命名容器与引用 Web 控件相关的服务器端问题外,还存在客户端问题。 如果没有命名容器,则 Web 控件的 ID
属性值和呈现 id
的属性值相同。 但是,随着命名容器的添加,呈现 id
的属性在其 ID
控件层次结构的祖先中包含 Web 控件的值和命名容器(s)。 只要使用 Web 控件的属性来确定客户端脚本中呈现id
的ClientID
属性值,这些命名问题就不是问题。
快乐编程!
深入阅读
有关本教程中讨论的主题的详细信息,请参阅以下资源:
关于作者
斯科特·米切尔是多个 ASP/ASP.NET 书籍的作者,4GuysFromRolla.com 的创始人,自1998年以来一直在与Microsoft Web 技术合作。 斯科特担任独立顾问、教练和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 3.5。 斯科特可以在他的博客上 mitchell@4GuysFromRolla.com 或通过他的博客联系 http://ScottOnWriting.NET。
特别感谢
本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是 Zack Jones 和 Suchi Barnerjee。 有兴趣查看即将发布的 MSDN 文章? 如果是这样,请把我扔一条线。mitchell@4GuysFromRolla.com