ASP.NET 2.0 和数据绑定控件:新的角度,新的做法(一)

翻译|其它|编辑:郝浩|2006-04-05 13:24:00.000|阅读 1868 次

概述:

# 界面/图表报表/文档/IDE等千款热门软控件火热销售中 >>


适用于
Microsoft ASP.NET 1.x
Microsoft ASP.NET 2.0

摘要:了解 ASP.NET 2.0 中的用于生成自定义数据绑定控件的工具是如何演变的。

本页内容

什么需要新的数据源模型

数据绑定是开发人员在 ASP.NET 1.x 中发现的最令人愉快的意外功能之一。与 Active Server Pages 对数据访问的支持相比,数据绑定是简单性和有效性的非凡结合。然而,如果根据真正开发人员的需要进行衡量,则它还不够完美。其局限不在于总体功能方面,而在于开发人员必须编写大量代码来处理甚至非常简单和常见的操作(例如,分页、排序或删除)。为了弥补这一缺陷,ASP.NET 2.0 添加了一种新的数据源模型(请参阅我的文章:More Load, Less Code with the Data Enhancements of ASP.NET 2.0)。它包括很多不带 UI 的新控件,这些控件将数据绑定控件的可视部分和数据容器联系起来。开发人员需要在 ASP.NET 1.x 中编写的绝大部分代码经过适当的分解和创作,现在基本上都被嵌入到一系列新的控件中:数据源组件。

使用数据源组件有很多好处 — 首先,可以得到完全声明性的数据绑定模型。新模型减少了以内联方式插入到 ASPX 资源中或者分散在代码隐藏类中的松散代码。新的数据绑定体系结构强制开发人员遵守严格的规则。此外,它还从本质上改变了代码的质量。附加到事件的较长代码块通常会消失,而被只是插入到现有框架中的组件所取代。这些数据源组件派生自抽象类,实现了已知的接口,并且总体而言意味着更高级别的可重用性。

Nikhil Kothari 的有关控件开发的优秀著作 — Developing Microsoft ASP.NET Server Controls and Components — 帮助成千上万的开发人员生成自定义控件,并且说明了设计和实现的最佳做法。但是,一本书 — 无论它有多么伟大 — 都永远无法取代一个更好的系统框架。借助于 ASP.NET 2.0,您还获得了一个完全重新设计的类图 — 当您沿着类树从基础类向叶子类滚动时,它能够添加更具体的数据绑定功能。通过新的数据绑定控件层次结构,所有开发人员都可以更容易地选取正确的类来加以继承,以便生成他们自己的自定义数据绑定控件。

在本文中,您将提前了解 ASP.NET 2.0 数据绑定模型中的能够对自定义控件产生影响的更改。在此过程中,您将了解可用的新基类以及新的高质量自定义控件的新要求。

ASP.NET 2.0 中的数据绑定控件

ASP.NET 2.0 数据源模型并未要求必须使用新的控件(例如,GridViewFormView);它仍然能够与旧样式的控件(例如,DataGridCheckBoxList)协同工作。这对于控件开发人员而言意味着什么呢?有两个截然不同类型的数据源需要处理 — 传统的基于 IEnumerable 的数据容器(例如,DataView 和集合)以及数据源控件(例如,SqlDataSourceObjectDataSource)。最后,无论数据源是 ADO.NET 对象、自定义集合还是数据源组件,ASP.NET 2.0 数据绑定控件都必须能够将传入的任何数据规格化为可枚举的集合。

在 ASP.NET 1.x 中,文档在某种程度上领先于框架。文档正确地标识和讨论了三个类型的数据绑定控件 — 标准控件、列表控件和复合控件。任何只是提供 DataBind 方法和 DataSource 属性的非空实现的控件都属于第一个类别。列表控件是下列两者的有趣结合:高级布局属性(例如,RepeatColumnsRepeatLayout),以及为绑定的每个数据元素重复的固定的嵌入式项模板。最后,复合控件负责通过组合一个或多个现有控件来设计最终的用户界面。文档准确地阐述了与创建上述类型的控件相关的任何问题;然而,ASP.NET 框架却并未提供很多基类来简化开发人员的任务。图 1 显示了 ASP.NET 2.0 中的新的数据绑定控件层次结构。请注意显示为黄色的基类以及它们在整个类树中的分布。


图 1. ASP.NET 2.0 中的数据绑定控件的层次结构
对图 1 中呈现的基类进行一番观察是一件有趣的事情。它们在表 1 中列出并进行了详细说明。

说明

BaseDataBoundControl

数据绑定控件的根类。执行数据绑定并验证任何绑定数据。

DataBoundControl

包含用于与数据源控件和数据容器进行通信的逻辑。可以从该类继承以生成标准的数据绑定控件。

ListControl

列表控件的基类,提供 Items 集合和高级布局呈现功能。

CompositeDataBoundControl

实现复合控件所必需的典型代码,包括在进行回发之后根据视图状态还原控件树的代码。

HierarchicalDataBoundControl

基于树的分层控件的根类。

表 1. ASP.NET 2.0 中的基本数据绑定类

对于任何曾经花费巨大精力来创建能够管理自己的数据集合并且能够正确地从视图状态还原的、具有丰富功能的数据绑定控件的人而言,这些类特别受欢迎。您需要能够对此加以阐明的示例吗?请继续阅读。

分析要点

ASP.NET 开发人员中心在过去几个月中发布了几篇有关下列 ASP.NET 1.1 数据绑定控件的文章:RssFeedDetailsView 控件(这两篇文章分别为 Building DataBound Templated Custom ASP.NET Server ControlsA DetailsView Control for ASP.NET 1.x)。如果您深入分析这两个控件的代码,则您会看到它们在源代码中的各个位置利用了特殊的技术。例如,它们在向页进行回发(即不在该控件管辖范围内的回发)之后根据视图状态重新生成控件的树;它们通过一个自定义集合类来公开项集合;它们允许赋予项样式;它们支持很多个类型的输入源。在 ASP.NET 1.1 中,对于上述每一种功能,您都必须编写代码,并且更重要的是,您必须按照特定的顺序编写代码,重写特定的基础方法,并且认真遵守文档和前面提到的优秀著作 Developing Microsoft ASP.NET Server Controls and Components 中的指导和建议。

在 ASP.NET 2.0 中,两个示例控件中使用的大部分管线代码都被硬编码到表 1 中列出的基类中。为了对比 ASP.NET 1.1 和 2.0 中的数据绑定控件,我将重点讨论下列要点:

    •总体数据绑定机制和不同的数据源类型
    •集合和视图状态管理

该列表很可能是不完备的,但是它肯定足以使您对控件开发有一个大致的了解。您将既愉快又吃惊地看到,开发功能丰富的自定义控件所需的代码是如此之少。

数据绑定机制

要在 ASP.NET 2.0 中生成新的数据绑定控件,首先需要确定哪个类能够更好地适合您的要求。然而,您的选择并不局限于比较空的类,如 ControlWebControl 甚至 ListControl。让我们探索一下那些深藏于幕后的类。BaseDataBoundControl 是所有数据绑定控件类的根。它定义了 DataSourceDataSourceID 属性,并且验证它们被分配的内容。DataSource 接受按照 ASP.NET 1.x 的方式获得和分配的可枚举对象。

Mycontrol1.DataSource = dataSet;
Mycontrol1.DataBind();

DataSourceID 是一个字符串,并且是指绑定数据源组件的 ID。一旦将控件绑定到数据源,则二者之间的任何进一步的交互(无论是读还是写)都将脱离您的控制范围,并且不可见。这一点既有好的一面,也有坏的一面。好(更确切地说是伟大)的一面在于可以消除大量代码。ASP.NET 框架能够保证正确的代码得以执行,并且按照公认的最佳做法编写代码。您的工作效率会更高,因为您可以完全确信在工作过程中不会出现令人难以捉摸的错误,从而可以更快地创作页。如果您不喜欢这种情况(好像很多 ASP.NET 1.x 开发人员都抱怨这种情况),则您可以继续使用通过 DataSource 属性和 DataBind 方法完成的旧样式的编程。而且,在这种情况下,基类使您不必完成一些常见的工作,即使这种效果在代码中体现得不是那么明显。

DataBoundControl 类用于与现有控件没有多少共同点的标准的自定义数据绑定控件。如果您必须处理自己的数据项集合,管理视图状态和样式,创建简单但量身定制的用户界面,则该类可以提供一个良好的起点。最为有趣的是,DataBoundControl 类将控件连接到数据源组件,并且在 API 级别隐藏了可枚举数据源和特别组件之间的任何差异。简而言之,当您从该类继承时,您只需要重写一个接收数据集合(无论数据源是 DataSet 对象还是较新的数据源组件)的方法。

让我们详细阐述这一点(它代表体系结构中的重大更改)。

BaseDataBoundControl 重写了 DataBind 方法(原来在 Control 上定义),并且使它调用 PerformSelect 方法(该方法被标记为受保护的和抽象的)。正如其名称所暗示的那样,PerformSelect 能够检索有效的数据集合以使绑定发生。该方法是受保护的,因为它包含实现细节;它是抽象的(用 Visual Basic 行话说就是 MustInherit),因为它的行为只能由派生类(如 DataBoundControl)确定。

那么,DataBoundControl 完成哪些工作以重写 PerformSelect 呢?

它连接到数据源对象并获得默认视图。数据源对象(例如,像 SqlDataSourceObjectDataSource 之类的控件)执行它的选择命令并返回得到的集合。操作数据检索的受保护方法(名为 GetData)还足够聪明,以便检查 DataSource 属性。如果 DataSource 非空,则将绑定对象包装到一个动态创建的数据源视图对象中,并且将其返回。

下一个步骤需要您以控件开发人员的身份参与。迄今为止,基类已经以一种完全自动的方式从 ADO.NET 对象或数据源组件中检索数据。下一个步骤取决于您期望该控件完成哪些任务。这里正好用到可重写的 PerformDataBinding 方法。以下代码片段显示了 DataBoundControl 中对该方法的实现。请注意,由框架传递给该方法的 IEnumerable 参数只包含要绑定的数据(不管它们的来源如何)。

protected virtual void PerformDataBinding(IEnumerable data)
{
}

在自定义数据绑定控件中,您只需要重写该方法,并且填充任何特定于控件的集合,如包含很多个列表控件的 Items 集合(例如,CheckBoxList)。控件的用户界面的呈现发生在 Render 方法或 CreateChildControls 中,具体取决于该控件的性质。Render 适用于列表控件;而 CreateChildControls 则非常适合于复合控件。

有一件事情尚未解释:由谁启动数据绑定过程?在 ASP.NET 1.x 中,数据绑定需要显式调用 DataBind 方法才能开始工作。在 ASP.NET 2.0 中,如果您使用 DataSource 属性将数据绑定到控件,则仍然需要这样做。如果您改而通过 DataSourceID 属性使用数据源组件,则应当避免这样做。数据绑定过程由 DataBoundControl 中定义的内部 OnLoad 事件处理程序自动触发,如下面的伪代码所示。

protected override void OnLoad(EventArgs e)
{
   this.ConnectToDataSourceView();
   if (!Page.IsPostBack)
       base.RequiresDataBinding = true;
   base.OnLoad(e);
}

每当该控件被加载到页中的时候(回发或首次加载),都会检索和绑定数据。需要由数据源决定是再次运行查询还是使用一些缓存数据。

如果该页是首次显示,则还会启用 RequiresDataBinding 属性以要求绑定数据。当分配的值为 true 时,该属性的设置程序会在内部调用 DataBind。下面的伪代码显示了 RequiresDataBinding 设置程序的内部实现。

protected void set_RequiresDataBinding(bool value)
{
   if (value && (DataSourceID.Length > 0))
      DataBind();
   else
      _requiresDataBinding = value;
}

正如您可以看到的那样,为了向后兼容,仅当 DataSourceID 不为空(即您绑定到 ASP.NET 2.0 数据源控件)时,才会发生对 DataBind 的自动调用。有鉴于此,如果您还显式调用 DataBind,则会导致双重数据绑定。

请注意,您无法同时设置 DataSourceDataSourceID。当发生这种情况时,将引发无效操作异常。

最后,稍微提一下 EnsureDataBound 这一受保护的方法。该方法是在 BaseDataBoundControl 类上定义的,它能够确保控件已经被正确地绑定到必需的数据。如果 RequiresDataBinding 为 true,则该方法调用 DataBind,如下面的代码片段所示。

protected void EnsureDataBound()
{
  if (RequiresDataBinding && (DataSourceID.Length > 0))
      DataBind();
}

如果您已经编写了复杂且完善的数据绑定控件,则您很可能已经知道我的意思。在 ASP.NET 1.x 中,在下列两种情况下,通常会将数据绑定控件设计为生成它自己的用户界面:该控件具有对数据源的完全访问权限,或者该控件基于视图状态。当该控件需要管理它自己的回发事件时(例如,假设该控件是支持分页的 DataGrid),则前面提到的两个选择似乎是两种极端的情况。在 ASP.NET 1.x 中,这些控件(同样,请考虑 DataGrid)只有一种解决办法:向要刷新的主页引发事件。该方法导致 ASP.NET 1.x 页中存在多余代码这一众所周知的问题 — 这也正是调用数据源组件来加以修复的问题。

在 ASP.NET 2.0 中,每当在控件的生存期中发生要求绑定数据的事情时,都需要将 RequiresDataBinding 设置为 true。设置该属性会触发相应的数据绑定机制,从而重新创建该控件的内部基础结构的更新版本。内置的 OnLoad 事件处理程序还会将该控件连接到数据源。为了确实有效,该技术必须依赖于能够将它们的数据缓存在某个位置的智能数据源控件。例如,SqlDataSource 控件支持很多属性,以便在给定期限内将任何绑定结果集存储到 ASP.NET 缓存中。

列表控件

数据绑定控件通常为列表控件。列表控件通过为它的主框架边界内的每个绑定数据项重复固定的模板,生成它自己的用户界面。例如,CheckBoxList 控件只是为每个绑定数据项重复 CheckBox 控件。同样,DropDownList 控件遍历它的数据源,并且在 <select> 父标记内创建新的 <option> 元素。除了列表控件以外,ASP.NET 还提供了迭代控件。它们有什么不同?

列表控件和迭代控件的不同之处在于被应用于每个数据项的可重复模板允许具有的自定义级别。像 CheckBoxList 控件一样,Repeater 控件遍历绑定数据项并应用用户定义的模板。Repeater(以及更完善的 DataList 控件)极为灵活,但是在使代码保持模块化和分层化方面不能提供多少帮助。要使用 Repeater,您需要在该页(或外部用户控件)中定义模板,并使用 ASPX 源中的数据绑定属性。它是快速、有效的,有时还是必要的,但肯定不是整洁和优雅的。

在 ASP.NET 1.x 中,所有列表控件都从 ListControl(它是表 1 中唯一一个已经在 1.x 中定义的类)继承。让我们进入编码猴子模式,并且开始练习使用 ASP.NET 2.0 中的数据绑定控件。我将首先生成一个 HeadlineList 控件,以便为每个数据项呈现两行数据绑定文本。此外,该控件还将具备一些布局功能,例如,垂直或水平呈现。
标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@evget.com


为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP