通过详细信息 DataList 使用母版记录项目符号列表的母版/详细信息 (VB)

作者 :斯科特·米切尔

下载 PDF

在本教程中,我们将上一教程的双页母版/详细信息报表压缩为单个页面,其中显示了屏幕左侧的类别名称项目符号列表和屏幕右侧所选类别的产品。

介绍

前面的教程 中,我们介绍了如何跨两页分隔主报表/详细信息报表。 在母版页中,我们使用 Repeater 控件呈现类别的项目符号列表。 每个类别名称都是一个超链接,单击后,会将用户带到详细信息页,其中两列 DataList 显示属于所选类别的产品。

在本教程中,我们将两页教程压缩为单个页面,其中显示屏幕左侧类别名称的项目符号列表,其中每个类别名称都呈现为 LinkButton。 单击某个类别名称 LinkButtons 会引发回发,并将所选类别的产品绑定到屏幕右侧的两列 DataList。 除了显示每个类别的名称外,左侧的 Repeater 还显示给定类别的总产品数(请参阅图 1)。

类别名称和产品总数显示在左侧

图 1:类别的名称和产品总数显示在左侧(单击以查看全尺寸图像

步骤 1:在屏幕左侧部分显示重复程序

在本教程中,我们需要将类别的项目符号列表显示在所选类别产品左侧。 可以使用标准 HTML 元素段落标记、非中断空格、 <table> s 等方式或通过级联样式表(CSS)技术定位网页中的内容。 到目前为止,我们所有的教程都使用 CSS 技术进行定位。 在母版页和网站导航教程的母版页中生成导航用户界面时,我们使用绝对定位,指示导航列表和主内容的精确像素偏移量。 或者,CSS 可用于通过 浮动将一个元素放在另一个元素的右侧或左侧。 通过将 Repeater 浮动到 DataList 左侧,我们可以将类别的项目符号列表显示在所选类别产品左侧

CategoriesAndProducts.aspxDataListRepeaterFiltering文件夹中打开页面,并添加到页面一个 Repeater 和 DataList。 将 Repeater s ID 设置为 Categories ,并将 DataList 设置为 CategoryProducts。 转到“源”视图,并将 Repeater 和 DataList 控件置于其自己的 <div> 元素中。 也就是说,先将 Repeater 括在元素 <div> 内,然后在 Repeater 后面直接将 DataList 括在自己的 <div> 元素中。 此时的标记应如下所示:

<div>
    <asp:Repeater ID="Categories" runat="server">
    </asp:Repeater>
</div>
<div>
    <asp:DataList ID="CategoryProducts" runat="server">
    </asp:DataList>
</div>

若要将 Repeater 浮动到 DataList 的左侧,我们需要使用 float CSS 样式属性,如下所示:

<div>
    Repeater
</div>
<div>
    DataList
</div>

float: left; 第二个元素左侧的第一 <div> 个元素浮动。 和widthpadding-right设置指示第一<div>个 s width 以及元素内容与其右边距之间的<div>填充量。 有关 CSS 中浮动元素的详细信息,请查看 Floatutorial

让我们改为在命名FloatLeftStyles.css创建新的 CSS 类,而不是直接通过第一<p>个元素属性style指定样式设置:

.FloatLeft
{
    float: left;
    width: 33%;
    padding-right: 10px;
}

然后,我们可以将其替换为 <div> <div class="FloatLeft">

添加 CSS 类并在页面中配置标记 CategoriesAndProducts.aspx 后,转到设计器。 应会看到 DataList 左侧浮动的 Repeater(尽管现在两者都只是显示为灰色框,因为我们尚未配置其数据源或模板)。

Repeater 浮动到 DataList 的左侧

图 2:重复器浮动到 DataList 左侧(单击以查看全尺寸图像

步骤 2:确定每个类别的产品数

完成 Repeater 和 DataList 周围的标记后,我们便可以将类别数据绑定到 Repeater 控件。 但是,如图 1 所示的类别项目符号列表,除了每个类别的名称外,还需要显示与类别关联的产品数。 若要访问此信息,我们可以:

  • 从 ASP.NET 页的代码隐藏类中确定此信息。 鉴于特定 categoryID ,我们可以通过调用 ProductsBLL 类的方法 GetProductsByCategoryID(categoryID) 来确定关联的产品数。 此方法返回一个 ProductsDataTable 对象,该 Count 对象的属性指示存在多少 ProductsRow 个 s,即指定 categoryID产品的数量。 我们可以为 Repeater 创建 ItemDataBound 事件处理程序,对于绑定到 Repeater 的每个类别,调用 ProductsBLL 类的方法 GetProductsByCategoryID(categoryID) 并在输出中包含其计数。
  • 更新 CategoriesDataTable 类型化数据集中的列以包含列 NumberOfProducts 然后,我们可以更新GetCategories()方法以CategoriesDataTable包含此信息,或者,保留原样,GetCategories()并创建一GetCategoriesAndNumberOfProducts()个名为的新CategoriesDataTable方法。

让我们探讨这两种技术。 第一种方法更易于实现,因为我们不需要更新数据访问层;但是,它需要更多的与数据库的通信。 对事件处理程序中ItemDataBound类方法的GetProductsByCategoryID(categoryID)调用ProductsBLL为 Repeater 中显示的每个类别添加额外的数据库调用。 使用此方法时,有 N + 1 个数据库调用,其中 N 是 Repeater 中显示的类别数。 第二种方法返回产品计数,其中包含类 (GetCategories()GetCategoriesAndNumberOfProducts()) 方法中每个类别CategoriesBLL的相关信息,从而只导致对数据库进行一次访问。

确定 ItemDataBound 事件处理程序中的产品数

确定 Repeater ItemDataBound 事件处理程序中每个类别的产品数量不需要对现有数据访问层进行任何修改。 所有修改都可以直接在 CategoriesAndProducts.aspx 页面中进行。 首先,添加一个新的 ObjectDataSource,该对象通过 Repeater 的智能标记命名 CategoriesDataSource 。 接下来,配置 CategoriesDataSource ObjectDataSource,以便从 CategoriesBLL 类 s GetCategories() 方法检索其数据。

将 ObjectDataSource 配置为使用 CategoriesBLL 类 s GetCategories() 方法

图 3:将 ObjectDataSource 配置为使用 CategoriesBLL 类 s GetCategories() 方法(单击可查看全尺寸图像

Repeater 中的每个 Categories 项都需要可单击,单击后,DataList 将显示 CategoryProducts 所选类别的这些产品。 这可以通过将每个类别设置为超链接,链接回同一页(CategoriesAndProducts.aspx),但传递 CategoryID 查询字符串,就像我们在上一教程中看到的一样。 此方法的优点是,显示特定类别产品的页面可由搜索引擎为书签和索引。

或者,我们可以将每个类别设为 LinkButton,这是我们将用于本教程的方法。 LinkButton 在用户的浏览器中呈现为超链接,但单击时会引发回发;在回发时,需要刷新 DataList s ObjectDataSource 以显示属于所选类别的产品。 在本教程中,使用超链接比使用 LinkButton 更有意义;但是,使用 LinkButton 的其他方案可能更有利。 虽然超链接方法非常适合此示例,但让我们改用 LinkButton 进行探索。 如我们所看到的,使用 LinkButton 会引入一些不会导致超链接出现的挑战。 因此,在本教程中使用 LinkButton 将突出显示这些挑战,并帮助为可能想要使用 LinkButton 而不是超链接的方案提供解决方案。

注意

建议使用 HyperLink 控件或元素代替 <a> LinkButton 重复本教程。

以下标记显示了 Repeater 和 ObjectDataSource 的声明性语法。 请注意,Repeater 的模板将项目符号列表呈现为 LinkButton:

<asp:Repeater ID="Categories" runat="server" DataSourceID="CategoriesDataSource">
    <HeaderTemplate>
        <ul>
    </HeaderTemplate>
    <ItemTemplate>
        <li><asp:LinkButton runat="server" ID="ViewCategory"></asp:LinkButton></li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

注意

在本教程中,Repeater 必须启用其视图状态(请注意 Repeater 声明性语法中的 EnableViewState="False" 省略)。 在步骤 3 中,我们将为 Repeater 事件 ItemCommand 创建事件处理程序,我们将在其中更新 DataList s ObjectDataSource 集合 SelectParameters 。 但是,如果禁用视图状态,Repeater 不会 ItemCommand触发。

具有属性值的 ViewCategory LinkButton ID 没有其Text属性集。 如果只想显示类别名称,我们将通过数据绑定语法以声明方式设置 Text 属性,如下所示:

<asp:LinkButton runat="server" ID="ViewCategory"
    Text='<%# Eval("CategoryName") %>' />

但是,我们希望同时显示类别名称和属于该类别的产品数。 可以通过调用ProductBLL类的方法ItemDataBoundGetCategoriesByProductID(categoryID)并确定结果ProductsDataTable中返回的记录数,从 Repeater 事件处理程序中检索此信息,如以下代码所示:

Protected Sub Categories_ItemDataBound(sender As Object, e As RepeaterItemEventArgs)
    ' Make sure we're working with a data item...
    If e.Item.ItemType = ListItemType.Item OrElse _
        e.Item.ItemType = ListItemType.AlternatingItem Then
        ' Reference the CategoriesRow instance bound to this RepeaterItem
        Dim category As Northwind.CategoriesRow = _
            CType(CType(e.Item.DataItem, System.Data.DataRowView).Row, _
                Northwind.CategoriesRow)
        ' Determine how many products are in this category
        Dim productsAPI As New NorthwindTableAdapters.ProductsTableAdapter
        Dim productCount As Integer = _
            productsAPI.GetProductsByCategoryID(category.CategoryID).Count
        ' Reference the ViewCategory LinkButton and set its Text property
        Dim ViewCategory As LinkButton = _
            CType(e.Item.FindControl("ViewCategory"), LinkButton)
        ViewCategory.Text = _
            String.Format("{0} ({1:N0})", category.CategoryName, productCount)
    End If
End Sub

首先,确保使用数据项(即数据 ItemType Item AlternatingItem项),然后引用 CategoriesRow 刚刚绑定到当前 RepeaterItem实例的实例。 接下来,我们通过创建类的 ProductsBLL 实例、调用其 GetCategoriesByProductID(categoryID) 方法以及确定使用该 Count 属性返回的记录数来确定此类别的产品数。 最后, ViewCategory ItemTemplate 中的 LinkButton 是引用,其 Text 属性设置为 CategoryNameNumberOfProductsInCategory),其中 NumberOfProductsInCategory 的格式为带零小数位数的数字。

注意

或者,我们可以将格式化函数添加到 ASP.NET 页的代码隐藏类中,该类接受类别和CategoryIDCategoryName值,并返回CategoryName与类别中的产品数(由调用GetCategoriesByProductID(categoryID)该方法确定)串联的函数。 此类格式设置函数的结果可以声明方式分配给 LinkButton s Text 属性,以替换事件处理程序的需求 ItemDataBound 。 有关 使用格式设置函数的详细信息,请参阅 GridView 控件 中的 Using TemplateFields 或 设置 DataList 和 Repeater Based Data 教程。

添加此事件处理程序后,花点时间通过浏览器测试页面。 请注意每个类别在项目符号列表中如何列出,显示类别名称和与类别关联的产品数(请参阅图 4)。

将显示每个类别的名称和产品数

图 4:显示每个类别的名称和产品数(单击以查看全尺寸图像

更新CategoriesDataTableCategoriesTableAdapter包括每个类别的产品数

我们可以通过在数据访问层中调整 CategoriesDataTableCategoriesTableAdapter 在数据访问层中以本机方式包含此信息来简化此过程,而不是确定每个类别的产品数量。 若要实现此目的,必须添加一个新列来 CategoriesDataTable 保存关联的产品数。 若要向 DataTable 添加新列,请打开类型化数据集(App_Code\DAL\Northwind.xsd),右键单击要修改的 DataTable,然后选择“添加/列”。 向 (请参阅图 5) 添加新列 CategoriesDataTable

向 CategoriesDataSource 添加新列

图 5:向 添加新列 CategoriesDataSource单击以查看全尺寸图像

这将添加一个名为 Column1的新列,只需键入其他名称即可更改该列。 将此新列重命名为 NumberOfProducts。 接下来,我们需要配置此列的属性。 单击新列并转到属性窗口。 更改列DataType的属性System.String并将其System.Int32设置为ReadOnlyTrue,如图 6 所示。

设置新列的 DataType 和 ReadOnly 属性

图 6:设置DataTypeReadOnly新列的属性

CategoriesDataTable虽然现在有一个NumberOfProducts列,但它的值不是由任何对应的 TableAdapter 查询设置的。 如果我们希望每次检索类别信息时返回此类信息,我们可以更新 GetCategories() 该方法以返回此信息。 但是,如果只需要获取极少数实例中类别的关联产品数(如本教程所示),则可以按原样保留 GetCategories() ,并创建返回此信息的新方法。 让我们使用后一种方法,创建名为 GetCategoriesAndNumberOfProducts() 的新方法。

若要添加新 GetCategoriesAndNumberOfProducts() 方法,请 CategoriesTableAdapter 右键单击并选择“新建查询”。 这会显示 TableAdapter 查询配置向导,我们在前面的教程中多次使用它。 对于此方法,请通过指示查询使用返回行的即席 SQL 语句来启动向导。

使用即席 SQL 语句创建方法

图 7:使用即席 SQL 语句创建方法(单击以查看全尺寸图像

SQL 语句返回行

图 8:SQL 语句返回行(单击可查看全尺寸图像

下一个向导屏幕提示我们查询使用。 若要返回每个类别、CategoryIDCategoryName字段以及Description与类别关联的产品数,请使用以下SELECT语句:

SELECT CategoryID, CategoryName, Description,
       (SELECT COUNT(*) FROM Products p WHERE p.CategoryID = c.CategoryID)
            as NumberOfProducts
FROM Categories c

指定要使用的查询

图 9:指定要使用的查询(单击以查看全尺寸图像

请注意,计算与类别关联的产品数的子查询别名为 NumberOfProducts。 此命名匹配会导致此子查询返回的值与 CategoriesDataTable s NumberOfProducts 列相关联。

输入此查询后,最后一步是选择新方法的名称。 GetCategoriesAndNumberOfProducts分别用于FillWithNumberOfProducts填充 DataTable 和返回 DataTable 模式。

将 New TableAdapter s 方法命名为 FillWithNumberOfProducts 和 GetCategoriesAndNumberOfProducts

图 10:命名新的 TableAdapter 方法FillWithNumberOfProductsGetCategoriesAndNumberOfProducts单击以查看全尺寸图像

此时,数据访问层已扩展,以包含每个类别的产品数。 由于我们的所有表示层都通过单独的业务逻辑层路由对 DAL 的所有调用,因此我们需要向类添加相应的 GetCategoriesAndNumberOfProducts 方法 CategoriesBLL

<System.ComponentModel.DataObjectMethodAttribute _
    (System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetCategoriesAndNumberOfProducts() As Northwind.CategoriesDataTable
    Return Adapter.GetCategoriesAndNumberOfProducts()
End Function

完成 DAL 和 BLL 后,我们便可以将此数据Categories绑定到 Repeater!CategoriesAndProducts.aspx 如果已从“确定事件处理程序”部分中的“确定产品ItemDataBound数”为 Repeater 创建了 ObjectDataSource,请删除此 ObjectDataSource 并删除 Repeater s DataSourceID 属性设置;此外,通过删除 Handles Categories.OnItemDataBound ASP.NET 代码隐藏类中的语法,将 Repeater 事件ItemDataBound从事件处理程序中取消连接。

将 Repeater 重新回到其原始状态后,添加一个新的 ObjectDataSource,该对象通过 Repeater 的智能标记命名 CategoriesDataSource 。 将 ObjectDataSource 配置为使用 CategoriesBLL 类,但不要使用 GetCategories() 该方法,而是改 GetCategoriesAndNumberOfProducts() 用它(请参阅图 11)。

将 ObjectDataSource 配置为使用 GetCategoriesAndNumberOfProducts 方法

图 11:将 ObjectDataSource 配置为使用 GetCategoriesAndNumberOfProducts 方法(单击以查看全尺寸图像

接下来,更新 ItemTemplate 该属性,以便使用数据绑定语法以声明方式分配 LinkButton 属性 Text ,并包括 CategoryName 数据和 NumberOfProducts 数据字段。 Repeater 和 CategoriesDataSource ObjectDataSource 的完整声明性标记如下:

<asp:Repeater ID="Categories" runat="server" DataSourceID="CategoriesDataSource">
    <HeaderTemplate>
        <ul>
    </HeaderTemplate>
    <ItemTemplate>
        <li><asp:LinkButton runat="server" ID="ViewCategory"
                Text='<%# String.Format("{0} ({1:N0})", _
                    Eval("CategoryName"), Eval("NumberOfProducts")) %>' />
        </li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategoriesAndNumberOfProducts" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

更新 DAL 以包含 NumberOfProducts 列所呈现的输出与使用 ItemDataBound 事件处理程序方法相同(请参阅图 4 以查看显示类别名称和产品数量的重复器屏幕截图)。

步骤 3:显示所选类别的产品

此时,我们让 Categories Repeater 显示类别列表以及每个类别中的产品数。 Repeater 对单击的每个类别使用 LinkButton,这会导致回发,此时我们需要在 DataList 中 CategoryProducts 显示所选类别的这些产品。

我们面临的一个挑战是,如何让 DataList 仅显示所选类别的这些产品。 在 Master/Detail 中使用具有 Details DetailsView 的可选主网格视图 教程中,我们了解了如何生成可选择其行的 GridView,并在同一页上的 DetailsView 中显示所选行的详细信息。 GridView s ObjectDataSource 使用 ProductsBLL s GetProducts() 方法返回了有关所有产品的信息,而 DetailsView s ObjectDataSource 使用 GetProductsByProductID(productID) 该方法检索了有关所选产品的信息。 通过将 productID 参数值与 GridView 属性 SelectedValue 的值相关联,以声明方式提供参数值。 遗憾的是,Repeater 没有 SelectedValue 属性,不能用作参数源。

注意

这是在 Repeater 中使用 LinkButton 时出现的其中一项挑战。 如果我们使用超链接来传入 CategoryID 查询字符串,则可使用该 QueryString 字段作为参数值的源。

不过,在担心 Repeater 缺少 SelectedValue 属性之前,让我们先将 DataList 绑定到 ObjectDataSource 并指定它 ItemTemplate

从 DataList 的智能标记中,选择添加新的 ObjectDataSource, CategoryProductsDataSource 并将其配置为使用 ProductsBLL 类 s GetProductsByCategoryID(categoryID) 方法。 由于本教程中的 DataList 提供只读界面,因此可以随意将 INSERT、UPDATE 和 DELETE 选项卡中的下拉列表设置为“无”。

将 ObjectDataSource 配置为使用 ProductsBLL 类 s GetProductsByCategoryID(categoryID) 方法

图 12:将 ObjectDataSource 配置为使用 ProductsBLL 类 s GetProductsByCategoryID(categoryID) 方法(单击以查看全尺寸图像

GetProductsByCategoryID(categoryID)由于该方法需要输入参数(categoryID),因此“配置数据源”向导允许我们指定参数的源。 如果类别列在 GridView 或 DataList 中,我们将参数源下拉列表设置为 Control,并将 ControlID 设置为 ID 数据 Web 控件。 但是,由于 Repeater 缺少属性 SelectedValue ,因此不能将其用作参数源。 如果选中,你会发现 ControlID 下拉列表仅包含一个控件 ID``CategoryProducts,即 ID DataList。

目前,将“参数源”下拉列表设置为“无”。 在 Repeater 中单击某个类别 LinkButton 时,我们将以编程方式分配此参数值。

请勿为 categoryID 参数指定参数源

图 13:请勿为参数指定参数源 categoryID单击以查看全尺寸图像

完成“配置数据源”向导后,Visual Studio 将自动生成 DataList。ItemTemplate 将此默认值 ItemTemplate 替换为我们在前面的教程中使用的模板;此外,请将 DataList 属性 RepeatColumns 设置为 2。 进行这些更改后,DataList 及其关联的 ObjectDataSource 的声明性标记应如下所示:

<asp:DataList ID="CategoryProducts" runat="server" DataKeyField="ProductID"
    DataSourceID="CategoryProductsDataSource" RepeatColumns="2"
    EnableViewState="False">
    <ItemTemplate>
        <h5><%# Eval("ProductName") %></h5>
        <p>
            Supplied by <%# Eval("SupplierName") %><br />
            <%# Eval("UnitPrice", "{0:C}") %>
        </p>
    </ItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="CategoryProductsDataSource"
    OldValuesParameterFormatString="original_{0}"  runat="server"
    SelectMethod="GetProductsByCategoryID" TypeName="ProductsBLL">
    <SelectParameters>
        <asp:Parameter Name="categoryID" Type="Int32" />
    </SelectParameters>
</asp:ObjectDataSource>

目前, CategoryProductsDataSource 永远不会设置 ObjectDataSource s categoryID 参数,因此在查看页面时不会显示任何产品。 我们需要执行的操作是基于 Repeater 中单击的类别设置 CategoryID 此参数值。 这引入了两个挑战:第一,如何确定何时单击 Repeater s ItemTemplate 中的 LinkButton;其次,如何确定 CategoryID 单击了 LinkButton 的相应类别?

LinkButton(如 Button 和 ImageButton 控件)具有事件 ClickCommand 事件。 该 Click 事件旨在仅注意已单击 LinkButton。 但是,除了指出已单击 LinkButton 之外,我们还需要向事件处理程序传递一些额外的信息。 如果是这种情况,则可以为 LinkButton 和CommandNameCommandArgument属性分配此额外信息。 然后,单击 LinkButton 时,其 Command 事件将触发(而不是事件 Click ),事件处理程序将 CommandName 传递这些值和 CommandArgument 属性。

Command当从 Repeater 的模板内引发事件时,Repeater 的事件ItemCommand将触发并传递CommandNameCommandArgument单击的 LinkButton(或 Button 或 ImageButton)的值。 因此,若要确定在 Repeater 中单击某个类别 LinkButton 时,需要执行以下操作:

  1. CommandName Repeater s ItemTemplate 中的 LinkButton 属性设置为一些值(我使用了 ListProducts)。 通过设置此值 CommandName ,单击 LinkButton 时,LinkButton 的事件 Command 将触发。
  2. 将 LinkButton s CommandArgument 属性设置为当前项的值 CategoryID
  3. 为 Repeater s ItemCommand 事件创建事件处理程序。 在事件处理程序中,将 CategoryProductsDataSource ObjectDataSource s CategoryID 参数设置为传入 CommandArgument的值。

Categories Repeater 的以下 ItemTemplate 标记实现了步骤 1 和 2。 请注意如何使用 CommandArgument 数据绑定语法为数据项 CategoryID 分配值:

<ItemTemplate>
    <li>
        <asp:LinkButton CommandName="ListProducts"  runat="server"
            CommandArgument='<%# Eval("CategoryID") %>' ID="ViewCategory"
            Text='<%# string.Format("{0} ({1:N0})", _
                Eval("CategoryName"), Eval("NumberOfProducts")) %>'>
        </asp:LinkButton>
    </li>
</ItemTemplate>

每当创建ItemCommand事件处理程序时,最好始终先检查传入CommandName值,因为重复程序中任何 Button、LinkButton 或 ImageButton 引发的任何Command事件都会导致ItemCommand事件触发。 虽然我们现在只有一个这样的 LinkButton,但将来我们(或团队上的另一位开发人员)可能会向 Repeater 添加其他按钮 Web 控件,单击时,会引发相同的 ItemCommand 事件处理程序。 因此,最好始终确保检查 CommandName 属性,并仅在与预期值匹配时继续编程逻辑。

确保传入CommandName值等于 ListProducts 后,事件处理程序会将 ObjectDataSource s CategoryID 参数分配给CategoryProductsDataSource传入CommandArgument的值。 对 ObjectDataSource 的 SelectParameters 这种修改会自动使 DataList 重新绑定到数据源,从而显示新所选类别的产品。

Protected Sub Categories_ItemCommand(source As Object, e As RepeaterCommandEventArgs) _
    Handles Categories.ItemCommand
    ' If it's the "ListProducts" command that has been issued...
    If String.Compare(e.CommandName, "ListProducts", True) = 0 Then
        ' Set the CategoryProductsDataSource ObjectDataSource's CategoryID parameter
        ' to the CategoryID of the category that was just clicked (e.CommandArgument)...
        CategoryProductsDataSource.SelectParameters("CategoryID").DefaultValue = _
            e.CommandArgument.ToString()
    End If
End Sub

通过这些新增功能,我们的教程已完成! 花点时间在浏览器中测试一下。 图 14 显示首次访问页面时的屏幕。 由于尚未选择某个类别,因此不会显示任何产品。 单击类别(如“生产”)在两列视图中的“产品”类别中显示这些产品(请参阅图 15)。

首次访问页面时不显示任何产品

图 14:首次访问页面时不显示任何产品(单击以查看全尺寸图像

单击“生成类别”将匹配的产品列在右侧

图 15:单击“生成类别”,将匹配的产品列在右侧(单击以查看全尺寸图像

总结

正如我们在本教程和前面的教程中看到的那样,主/详细信息报表可以分散在两个页面中,也可以合并在一个页面上。 但是,在单个页面上显示母版/详细信息报表,对如何最好地布局页面上的主控形状和详细信息记录带来了一些挑战。 在 Master/Detail 中使用具有 Details DetailsView 的可选择主网格视图 教程中,我们有详细信息记录显示在主记录上方;在本教程中,我们使用 CSS 技术将主记录浮动到详细信息的左侧。

除了显示主/详细信息报表外,我们还有机会探索如何检索与每个类别关联的产品数,以及如何在 Repeater 内单击 LinkButton(或 Button 或 ImageButton)时执行服务器端逻辑。

本教程使用 DataList 和 Repeater 完成对大纲/详细信息报表的检查。 下一组教程将演示如何向 DataList 控件添加编辑和删除功能。

快乐编程!

深入阅读

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

关于作者

斯科特·米切尔,七本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com创始人,自1998年以来一直在与Microsoft Web 技术合作。 斯科特担任独立顾问、教练和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 2.0。 他可以通过他的博客联系到mitchell@4GuysFromRolla.com他,可以在该博客中找到http://ScottOnWriting.NET

特别感谢

本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是 Zack Jones。 有兴趣查看即将发布的 MSDN 文章? 如果是这样,请把我扔一条线。mitchell@4GuysFromRolla.com