您的位置:首页 > 编程语言 > ASP

以编程方式访问 ASP.NET 控件_开发模板化控件

2008-08-25 19:53 1061 查看
Web 窗体控件标识
ASP.NET网页上的每一个控件都必须是可唯一标识的。通常,为控件的 ID 属性分配一个值来唯一标识该控件。该值将成为控件的实例名称(即,在代码中引用控件所依据的名称)。例如,如果将 TextBox 控件的 ID 属性设置为 Text1,则可以在代码中使用 Text1 来引用该控件。

许多数据绑定控件(包括 DataList、Repeater、GridView、FormView 和 DetailsView 控件)可作为其他(子)控件的容器。当这些控件运行时,它们会生成子控件的多个实例。例如,如果创建一个包含 Label 控件的 DataList 模板,则当页运行时,DataList 控件的数据源中包含多少个记录,就会在页中生成多少个该 Label 控件的实例。

注意
使用模板的控件(例如 DataList 和 Repeater 控件)承载模板对象。例如,当 DataList 控件运行时,它会创建 DataListItem 类的多个实例。而这些模板对象又包含单个控件,例如,标签、文本框、按钮,等等。

由于控件可在同一页上多次实例化,而且您可以在不同的页上重复使用控件名称,所以,ASP.NET 页框架提供了确保页上和应用程序中控件具有唯一标识符的机制。它还为您提供了找到这些单个控件的方法,以便您可以在自己的代码中操作它们。

命名容器
可作为其他控件的容器的控件会为其子控件生成“命名容器”或 ID 命名空间。通过提供此命名容器,控件可以保证其子控件的 ID 属性在整个应用程序内是唯一的。(控件通过实现 INamingContainer 接口来生成命名容器。)如果在运行时创建了子控件,命名容器将与子控件的 ID 属性进行组合,以创建每个子控件的 UniqueID 属性值。因此,UniqueID 属性会成为控件的完全限定标识符,引用其命名容器以及控件的各个 ID 值。

在上面的示例中,在父级 DataList 控件的命名容器(即命名空间)内创建 Label 控件的多个实例。每个 Label 控件的 UniqueID 属性将反映此命名空间,其格式类似于 DataList1:_ctl:MyLabel、DataList1:_ct2:MyLabel,依此类推。

注意
请不要编写使用生成的 UniqueID 属性的值引用控件的代码。可以将 UniqueID 属性视为一个句柄(例如,通过将它传递到进程),但不应指望它拥有特定结构。

每个容器控件都会为其子控件提供命名容器,此外,页本身也会为容器控件的所有子控件提供命名容器。这样,就可以在应用程序内为该页上的所有控件创建唯一的命名空间。

使用 NamingContainer 属性
子控件可以通过 NamingContainer 属性引用其命名容器。此属性会返回一个 Control 类型的对象,您可以将该对象强制转换为相应的 DataList 控件、DataListItem 对象等。

在需要从子控件访问容器控件的属性时,引用命名容器是很有用的。例如,在子控件的 DataBinding 事件的处理程序中,可以通过从命名容器获取 DataItem 对象来访问该对象。

注意
NamingContainer 属性和 Parent 属性引用的控件不必相同。例如,在 Repeater 控件中,可能有一个包含表的项模板,而该表又包含 Label 控件。标签的父级控件是表单元格(例如,HtmlTableCell 对象),但其命名容器是 DataListItem 对象,因为它是定义 Label 控件的命名空间的 DataListItem,而不是该表。

引用控件
如果页中包含在运行时生成的控件,例如,位于 DataList、Repeater 或 GridView 控件的模板中的控件,就不能直接按 ID 来引用这些控件,因为 ID 不是唯一的。但是,有多种方式可以找到页中的各个控件。

Web 窗体控件 ID 解析
当您声明 Web 服务器控件的 ID 属性以通过编程方式访问该控件时,ASP.NET 页框架将自动确保声明的 ID 在整个 ASP.NET Web 应用程序中是唯一的。

命名容器
ASP.NET 页框架通过 INamingContainer 接口为应用程序提供自动控件 ID 解决方案,该接口为实现它的每个类生成一个“命名容器”。命名容器在 ASP.NET 网页控件层次结构中定义一个新的 ID 命名空间。这样,命名容器便允许页框架为在该命名空间内生成的每个 Control 对象的 UniqueID 属性生成一个值。UniqueID 属性不同于您声明的 ID 属性,因为它是控件的完全限定标识符。

实现 INamingContainer 的类包括:Page、DataList、GridView、DataListItem、DataGridItem 和 Repeater。通常,可以创建子控件的那些控件都会动态实现 INamingContainer。

Page 类充当该页的控件层次结构中的顶级命名容器。

数据绑定方案中的名称解析
页框架提供的自动名称解决方案在数据绑定方案中很重要。请考虑下面的示例,该示例演示页中声明的控件。

当 Label 控件绑定到数据源,而 Repeater 控件循环访问该数据源中的项时,该页必须能够以编程的方式区分 Label 控件的不同实例,即使您只为每个实例指定了 ID MyLabel 也是如此。页框架通过对每个控件使用完全限定的 UniqueID 属性来实现这一点。例如,下面的代码生成了 Label 控件的三个版本,并将它们的 UniqueID 属性值写入该页。

当请求此页时,它将下面的内容写入该页:

名为 MyDataList 的 Repeater 控件的命名容器。此命名容器取决于指定给 .aspx 文件的名称。

注意
如果此示例的 .aspx 文件是 MySample1.aspx,则命名容器的类是 ASP.mysample1_aspx,但命名容器是 Page。

充当命名容器的下一个控件的实例,即 Repeater 控件。此容器的名称是随同它的整个命名空间限定符一起显示的。

Repeater 控件内每个 Label 控件的 UniqueID 属性。

注意
请不要编写使用生成的 UniqueID 属性的值引用控件的代码。可以将 UniqueID 属性视为一个句柄(例如,通过将它传递到进程),但不应指望它拥有特定结构。

如何在 ASP.NET 网页中按 ID 定位子控件
可以用一个方法来获取对特定控件的引用,该方法按控件 ID 搜索其命名容器。
按 ID 定位控件
调用命名容器的 FindControl 方法,向该方法传递包含要使用的控件的 ID 的字符串。该方法会返回一个类型为 Control 的对象,可以将该类型强制转换为适当的类型。
下面的代码示例演示如何定位特定的控件。该示例是 GridView 控件中某按钮的 Click 事件的处理程序。单击该按钮时,代码在当前的 GridView 项(它是 Label 控件的命名容器)中搜索名为 Label1 的控件。如果找到该控件,其文本便会显示在页面其他位置上第二个名为 LabelText 的 Label 控件中。
C#
protected void GridView1_ItemCommand(object source,
GridViewCommandEventArgs e)
{
Label l;
l = (Label) e.Item.FindControl("Label1");

if(!(l == null) ){
LabelText.Text = l.Text;
}
}
在 ASP.NET 网页中使用控件集合
Control 类及其派生类(包括 Page 类)会公开一个返回 ControlCollection 的 Controls 属性。通过这种层次结构,可以通过编程方式对控件树进行遍历以搜索某页上的特定控件,并检查集合中的控件类型,以便访问其属性。下面的代码示例演示如何通过遍历该页的控件层次结构来查找 <asp:TextBox> 控件的实例(只有一个)。
如何通过遍历控件集合定位页上的 Web 窗体控件
在 Controls 集合中定位控件
依次通过容器控件的 Controls 集合。该集合的类型为 ControlCollection,它返回 Control 类型的对象。
下面的示例说明如何浏览 Controls 集合。该示例假定 ASP.NET 网页上至少有一个 TextBox 控件,还包含一个 Label 控件和一个 Button 控件。该代码会获取 Page 对象的所有子控件。由于这样只会产生几个高级别子控件(包括 HtmlForm 对象),所以该代码还要浏览每个单独的子控件的 Controls 集合。该代码会通过比较每个控件的类型来查找文本框。当它找到文本框时,它会获取该文本框的值并将该值串联成字符串,该字符串会显示在 Label 控件的末尾。
此示例只查找 Page 对象中包含的控件以及作为该页的直接子级的控件。如果文本框是控件的子级,而该控件又是页的子级,则该示例不查找此文本框。例如,如果您向该页中添加了 Panel 控件,则 Panel 控件将是包含在 Page 中的 HtmlForm 控件的子级,在此示例中可以找到该控件。然而,如果您之后将某个 TextBox 控件添加到 Panel 控件中,则此示例将不显示该 TextBox 控件的文本,因为该控件既不是页的子级,也不是页的子控件的子级。以这种方式浏览控件有一种更实用的方法:即创建递归方法。一遇到某个控件,就可调用该方法来浏览该控件的 Controls 集合。然而,为清楚起见,没有将下面的示例创建为递归函数。
使用 NamingContainer 属性确定控件的命名容器

通过 NamingContainer 属性,可以遍历某页中的控件树。与只在内联代码(即位于<%# 和 %> 标记中)中可用的 Container 关键字相比,NamingContainer 属性在该类或派生类的任何实例代码中可用。
下面的代码示例阐释了如何遍历 ASP.NET 网页中的控件树。按钮的 ChangeBtn_Click 方法处理程序首先使用 FindControl 方法搜索控件,然后确定该控件的 NamingContainer 对象。此后,它会确定首次调用 NamingContainer 属性时返回的控件的命名容器,然后会遍历控件树,直到找到不包含命名容器的控件为止。(请注意,WalkContainers 方法还将在最低级别添加控件的类型,该类型本身不是命名容器。)
如何访问 Web 服务器控件的命名容器的成员
有时,您需要访问控件的命名容器的属性或方法。例如,在数据绑定期间,命名容器可以提供包含控件所绑定的数据的 DataItem 属性。您可以根据上下文,采用不同方式来访问该包含控件。
从数据绑定表达式访问命名容器
在数据绑定表达式中,使用 Container 关键字。该关键字可以返回指向容器的引用。然后,可以访问容器的属性或方法。
在 Eval 方法中,经常使用此关键字获取命名容器的 DataItem 对象值;不过,也可以在该方法之外使用此关键字。下面的示例演示 Label 控件。该控件可能位于 DataList、Repeater 或 GridView 控件的模板中。该控件显示当前项的编号。
下面是一个类似的示例,不同之处在于从命名容器的 DataItem 对象中获取值:
注意
NamingContainer 属性和 Parent 属性引用的控件不必相同。例如,在 Repeater 控件中,可能有一个包含表的项模板,而该表又包含 Label 控件。标签的父级控件是表单元格(例如,HtmlTableCell 对象),但其命名容器是 DataListItem 对象,因为它是定义 Label 控件的命名空间的 DataListItem,而不是该表。

从代码访问命名容器
获取控件的 NamingContainer 属性,并将其强制转换为容器的类类型,如 GridViewRow。

有关使用 NamingContainer 属性获取各个控件命名容器方面的信息的示例,请参见使用 NamingContainer 属性确定控件的命名容器。
Conctrol,Controls,DataItem这三者之间的联系与区分要重点学习。
基于对.net的控件访问的理解还不是很透彻,现将MSDN中部分内容找出,希望对自己的学习有帮助。学习了一遍,对有些地方还不是很明白,以后慢慢研读吧。

开发模板化控件

ASP.NET 提供了一种称为模板的通用功能,允许将控制数据与其表示分开。模板化控件本身不提供用户界面 (UI)。该控件的 UI 由页面开发人员通过内联模板提供,该模板允许页面开发人员自定义该控件的 UI。如果您不熟悉 ASP.NET 中的模板,请参见 ASP.NET 快速入门 —> ASP.NET Web 窗体 —> 数据访问和自定义 —>“模板化控件介绍”中的示例。

开发模板化控件

实现 System.Web.UI.INamingContainer 接口。这是没有任何方法的标记接口。它可以在您的控件下创建新的命名范围,这样子控件就在名称树中有了唯一的标识符。
[C#]
public class TemplatedFirstControl : Control,INamingContainer
{...}

ParseChildrenAttribute 应用到您的控件,并传递 true 作为参数。在 ASP.NET 页上声明性地使用控件时,这样可指示页分析器如何分析模板属性标记。步骤 3 说明如何定义一个模板属性。
[b]注意 [/b]如果您的控件是从 WebControl 派生的,则不需要应用 ParseChildrenAttribute,因为 WebControl 已经用该属性作了标记。
[C#]
[ ParseChildren(ChildrenAsProperties = true)]
public class TemplatedFirstControl : Control, INamingContainer {...}

有关 ParseChildrenAttribute 的更多信息,请参见使用 ParseChildrenAttribute

定义 System.Web.UI.ITemplate 类型的一个或多个属性。ITemplate 有一个方法 InstantiateIn,该方法可使用页上内联提供的模板创建控件。您不必实现 InstantiateIn 方法;ASP.NET 页框架可提供这种实现。ITemplate 属性必须有 System.Web.UI.TemplateContainerAttribute 类型的元数据属性,它指出哪种 INamingContainer 控件将拥有实例化模板。这在步骤 4 中作了说明。以下代码片段定义了一个模板属性。
[C#]
[TemplateContainer(typeof(FirstTemplateContainer))]
public ITemplate FirstTemplate {...}

TemplateContainerAttribute 的参数是您想在其中实例化模板的容器控件类型。容器控件独立于正在创作的模板化控件。具有逻辑容器的原因是:模板化控件通常有一个模板,该模板需要使用不同数据重复实例化。拥有与根模板化控件不同的容器控件,使拥有多个此类示例成为可能。逻辑容器是该模板内子控件的即时 INamingContainer。在开发模板化数据绑定控件中更详细地介绍了这种关系。
[b]注意 [/b]容器控件本身必须实现 INamingContainer,因为它有需要在页上唯一命名的子控件。
[C#]
public class FirstTemplateContainer : Control, INamingContainer {...}

重写 CreateChildControls 方法以便在模板中创建子控件。这是通过三个步骤来完成的。

实例化模板容器。
调用模板属性的 InstantiateIn 方法并将该容器作为参数传递给它。InstantiateIn 方法(在 ITemplate 接口中声明)实例化该模板的元素,作为该模板容器的子控件。您不必实现 InstantiateIn 方法;ASP.NET 页框架可提供这种实现。
将模板容器的示例添加到您的模板化控件的 Controls 集合。

以下代码片段说明了 CreateChildControls 的实现。
[C#]
private Control myTemplateContainer;

protected override void CreateChildControls ()
{
if (FirstTemplate != null)
{
myTemplateContainer = new FirstTemplateContainer(this);
FirstTemplate.InstantiateIn(myTemplateContainer);
Controls.Add(myTemplateContainer);
}
else
{
Controls.Add(new LiteralControl(Text + " " + DateTime));
}
}

重写从 Control 继承的 OnDataBinding 方法以调用 EnsureChildControls 方法。这样可保证在页框架尝试计算模板内任何数据绑定表达式之前,创建模板中的子控件。您还必须调用基类的 OnDataBinding 方法以确保调用已注册的事件处理程序。
[C#]
protected override void OnDataBinding(EventArgs e) {
EnsureChildControls();
base.OnDataBinding(e);
}

在步骤 5 中,在 CreateChildControls 方法内重复该逻辑,以便为控件的每个模板属性实例化一个模板。

有关模板化控件、模板的相关容器控件和使用该控件的页的示例,请参见模板化控件示例

请参见

模板化控件示例
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: