本文档是 Visual Basic 教程 (切换到 Visual C# 教程)
本教程对主/明细关系进行扩展,新增一个第三层。使用两个 DropDownList 控件选择所需的父记录和祖父级记录。
简介上一篇教程中,我们详细介绍了如何通过一个类别的 DropDownList 显示简单的主/明细报表,以及如何在 GridView 中显示属于所选类别的那些产品。该报表模式在显示存在一对多关系的记录时没有问题,可以轻松扩展,在包含多个一对多关系的场景下使用。例如,一个订单输入系统应包含多个对应客户、订单和订单行项目的表。一个特定客户可能有多个订单,每个订单又包含多个项目。通过两个 DropDownLists 和一个 GridView,就可以向用户展示这些数据。第一个 DropDownList 包含数据库中每个客户的一个列表项。第二个 DropDownList 的内容则为选中客户下的订单GridView 则显示选中订单的行项目。 尽管 Northwind 数据库在其Customers、Orders 和 Order Details 表中包含标准的客户/订单/订单详细信息,但在我们的架构中并不能获取这些表。不过,我们仍可以使用两个相互相关的 DropDownList 来说明。第一个 DropDownList 将列出类别,第二个则列出属于选中类别的产品。然后将有一个 DetailsView 列出选中产品的详细信息。 步骤 1:创建和填充类别 DropDownList我们的第一个目标是添加列出类别的 DropDownList。前一教程中对这些步骤进行了详细讲解,但此处将进行归纳以求完整性。 打开 Filtering 文件夹中的 MasterDetailsDetails.aspx 页面,向页面添加一个 DropDownList,将其 ID 属性设为 Categories,然后单击其智能标记中的 Configure Data Source 链接。在 Data Source Configuration Wizard 中选择添加一个新的数据源。 图1: 为 DropDownList 增加一个新的数据源 当然新数据源应为 ObjectDataSource。将新的 ObjectDataSource 命名为 CategoriesDataSource,使其调用 CategoriesBLL 对象的 GetCategories() 方法。 图2: 选择使用 CategoriesBLL 类 图3: 配置 ObjectDataSource 以使用 GetCategories() 方法 Method 完成对 ObjectDataSource 的配置后,还需要指定要在 Categories DropDownList 中显示哪个数据源字段,以及要将哪个字段配置为列表项的值。将 CategoryName 字段设置为显示,CategoryID 作为每个列表项的值。 图4: 使 DropDownList 显示 CategoryName 字段,使用 CategoryID 作为值 现在我们有一个 DropDownList 控件 (Categories),显示来自 Categories 表的记录。用户从 DropDownList 中选择一个新的类别时,我们希望发生一次回传,以便刷新我们将在步骤2中创建的产品 DropDownList。因此,勾选类别 DropDownList 的智能标记上的 Enable AutoPostBack 选项。 图5: 启用类别 DropDownList 的 AutoPostBack 步骤 2:在第二个 DropDownList 中显示选中类别的产品完成 Categories DropDownList 后,下一步是显示属于选中类别产品的 DropDownList。操作方法为:向页面添加另一个名为 ProductsByCategory 的 DropDownList。与 Categories DropDownList 一样,为 ProductsByCategory DropDownList 新建一个名为 ProductsByCategoryDataSource 的 ObjectDataSource。 图6:为 ProductsByCategory DropDownList 添加一个新的数据源 图7: 新建一个名为 ProductsByCategoryDataSource 的新 ObjectDataSource 由于 ProductsByCategory DropDownList 只需显示属于选中类别的那些产品,所以需要使 ObjectDataSource 调用 ProductsBLL 对象的 GetProductsByCategoryID(categoryID) 方法。 图8: 选择使用 ProductsBLL 类 图9: 配置 ObjectDataSource 以使用 GetProductsByCategoryID(categoryID) 方法 本向导的最后一步是为 categoryID 参数赋值。将此参数赋值为 Categories DropDownList 的选中项。 图10: 从 Categories DropDownList 为 categoryID 参数赋值 配置完 ObjectDataSource 后,剩下的就是指定哪些数据源字段用于显示,以及为 DropDownList 项赋值。显示 ProductName 字段,使用 ProductID 字段赋值。 图11: 用数据源字段为 DropDownList 的 ListItems 的 Text 和 Value 属性赋值 配置好 ObjectDataSource 和 ProductsByCategory DropDownList 后,我们的页面将显示两个 DropDownList:第一个 DropDownList 将列出所有类别,第二个则列出属于选中类别的那些产品。用户从第一个 DropDownList 中选中一个新类别时,将引发一个回传,第二个 DropDownList 将被重新绑定,显示属于新选中类别的那些产品。图 12 和图 13 显示了通过浏览器浏览的运行中的 MasterDetailsDetails.aspx 页面。 图12: 首次访问该页面时显示选中的饮料类别 图13: 选择其他类别将显示新类别的产品 更改当前的 productsByCategory DropDownList 时,不会造成回传。但我们希望在添加 DetailsView 显示产品详细信息(步骤 3)时引发回传。因此,勾选 productsByCategory DropDownList 智能标记上的 Enable AutoPostBack 复选框。 图14: 为 productsByCategory DropDownList 启用 AutoPostBack 特性 步骤 3:使用 DetailsView 显示选中产品的详细信息最后一步是在 DetailsView 中显示选中产品的详细信息。操作方法为:向页面添加一个 DetailsView,将其 ID 属性设为 ProductDetails,并为其创建一个新的 ObjectDataSource。配置该 ObjectDataSource,使其从 ProductsBLL 类的 GetProductByProductID(productID) 方法中获取数据。使用 ProductsByCategory DropDownList 中选中的值为 productID 参数赋值。 图15: 选择使用 ProductsBLL 类 图16: 配置 ObjectDataSource 以使用 GetProductByProductID(productID) 方法 图17: 从 ProductsByCategory DropDownList 为 productID 参数赋值 您可以选择在 ProductDetails DetailsView 中显示任何可用字段。本例中,我选择移除 ProductID、SupplierID 和 CategoryID 字段,并对其余字段进行重新排序和格式化。此外,我还清除了 DetailsView 的 Height 和 Width 属性,使 DetailsView 能根据需要扩展,便于以最佳方式显示数据,而不是受限于特定尺寸。完整的标记如下所示:
花点时间试着在浏览器中打开 MasterDetailsDetails.aspx 页面。第一眼看上去整个页面似乎运行良好,但实际上有个小问题。选择一个新的类别后,ProductsByCategory DropDownList 被更新以包含选中类别的那些产品。但 ProductDetails DetailsView 显示的还是原来的产品信息。只有当选择了选中类别的其他产品时,DetailsView 才会被更新。而且,如果进行充分测试,就会发现如果持续选择新类别(如先是 Categories DropDownList 中的 Beverages,然后是 Condiments,然后是 Confections),那么每次选择都将导致 ProductDetails DetailsView 被刷新。 为了具体了解此问题,我们来看一个实例。首次访问页面时,Beverages 类别被选中,且相关产品被加载到 ProductsByCategory DropDownList。选中的产品为 Chai,其详细信息显示在 ProductDetails DetailsView 中,如图 18 所示。 图18: 选中产品的详细信息显示在 DetailsView 中 如果将选中类别由 Beverages 改为 Condiments,将引发一个回传,且 ProductsByCategory DropDownList 将做相应更新。但 DetailsView 仍显示 Chai 的详细信息。 图19: 仍显示之前选中的产品的详细信息 从列表中选择另一个产品,如我们所预期的那样刷新 DetailsView。如果更改产品后选择一个新类别,则不会再次刷新 DetailsView。但是,如果不选择新产品,而是选择新类别,将刷新 DetailsView。这到底是怎么回事呢? 症结在于页面生命周期中的计时问题。无论何时请求一个页面,它都将处理所显示的多个步骤。其中一个步骤中,ObjectDataSource 控件要查看其 SelectParameters 值是否有变更。如果有,那么绑定到 ObjectDataSource 的数据 Web 控件知道其需要刷新其显示。例如,当选中一个新类别时,ProductsByCategoryDataSource ObjectDataSource 将检测到其参数值已被更改,且 ProductsByCategory DropDownList 将重新绑定自己,以获取选中类别的产品。 由此产生的问题是,页面生命周期中 ObjectDataSources 检查被更改参数的时间发生在相关联数据 Web 控件重新绑定之前。因此,选择新类别时,ProductsByCategoryDataSource ObjectDataSource 将检测到其参数值发生更改。然而,ProductDetails DetailsView 使用的 ObjectDataSource 并未注意到这些更改。这是因为 ProductsByCategory DropDownList 尚未被重新绑定。在生命周期内的稍后时间里,ProductsByCategory DropDownList 将重新绑定到其 ObjectDataSource,以便获取新选中类别的产品。尽管 ProductsByCategory DropDownList 的值发生了更改,但 ProductDetails DetailsView 的 ObjectDataSource 已经结束了对其参数值的检查。因此,DetailsView显示的是原来的结果。图 20 对此进行了描述。 图20: ProductsByCategory DropDownList 值的更改发生在 ProductDetails DetailsView 的 ObjectDataSource更改检查之后 为了解决这一问题,我们需要在 ProductsByCategory DropDownList 被绑定之后,明确地对 ProductDetails DetailsView 进行重新绑定。通过在 ProductsByCategory DropDownList 的 DataBound 事件触发时调用 ProductDetails DetailsView 的 DataBind() 方法,就可以实现这一点。向 MasterDetailsDetails.aspx 页面的代码文件类添加下列 Event Handler 代码(关于如何添加 Event Handler,请参见 "通过编程为 ObjectDataSource 的参数赋值" :
完成添加对 ProductDetails DetailsView 的 DataBind() 方法的明确调用后,本教程就可按预期的那样进行了。图 21 着重说明了这一方法如何解决我们前面遇到的问题。 图21: ProductsByCategory DropDownList 的 DataBound 事件触发时,ProductDetails DetailsView 被明确刷新 小结对于主记录和明细记录之间存在一对多关系的主/明细报表而言,DropDownList 是理想的用户界面元素。前面教程中,我们学习了如何使用一个 DropDownList 来筛选选中类别显示的产品。本教程中我们用一个 DropDownList 取代了产品的 GridView,并使用一个 DetailsView 来显示选中产品的详细信息。本教程中讲到的概念可以轻松扩展至涉及多个一对多关系的数据模型,例如客户、订单及订单项等。总之,你可以始终为一对多关系中的每“一个”实体添加 DropDownList。 快乐编程!
|