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

作者 :Scott Mitchell

下载 PDF

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

简介

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

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

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

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

步骤 1:在屏幕左侧部分显示中继器

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

CategoriesAndProducts.aspx从 文件夹中打开页面,DataListRepeaterFiltering向页面添加 Repeater 和 DataList。 将 Repeater 设置为 IDCategories ,将 DataList 设置为 CategoryProducts。 转到“源”视图,将 Repeater 和 DataList 控件放在它们自己的 <div> 元素中。 也就是说,先将 Repeater 括在元素 <div> 内,然后将 DataList 直接括在中继器后面的其自身 <div> 元素中。 此时的标记应如下所示:

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

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

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

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

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

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

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

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

中继器浮动到 DataList 的左侧

图 2:将中继器浮动到 DataList 的左侧 (单击以查看全尺寸图像)

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

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

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

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

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

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

配置 ObjectDataSource 以使用 CategoriesBLL 类 getCategories () 方法

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

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

或者,我们可以将每个类别设为 LinkButton,这是本教程将使用的方法。 LinkButton 在用户浏览器中呈现为超链接,但单击时会引发回发;回发时,需要刷新 DataList 的 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" /></li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

注意

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

属性值为 的 IDViewCategory LinkButton 没有设置其 Text 属性。 如果只是想显示类别名称,可以通过数据绑定语法以声明方式设置 Text 属性,如下所示:

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

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

protected void Categories_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    // Make sure we're working with a data item...
    if (e.Item.ItemType == ListItemType.Item ||
        e.Item.ItemType == ListItemType.AlternatingItem)
    {
        // Reference the CategoriesRow instance bound to this RepeaterItem
        Northwind.CategoriesRow category =
            (Northwind.CategoriesRow) ((System.Data.DataRowView) e.Item.DataItem).Row;
        // Determine how many products are in this category
        NorthwindTableAdapters.ProductsTableAdapter productsAPI =
            new NorthwindTableAdapters.ProductsTableAdapter();
        int productCount =
            productsAPI.GetProductsByCategoryID(category.CategoryID).Count;
        // Reference the ViewCategory LinkButton and set its Text property
        LinkButton ViewCategory = (LinkButton)e.Item.FindControl("ViewCategory");
        ViewCategory.Text =
            string.Format("{0} ({1:N0})", category.CategoryName, productCount);
    }
}

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

注意

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

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

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

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

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

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

向 CategoriesDataSource 添加新列

图 5:将新列添加到 CategoriesDataSource (单击以查看全尺寸图像)

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

设置新列的 DataType 和 ReadOnly 属性

图 6:设置 DataType 新列的 和 ReadOnly 属性

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

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

使用临时 SQL 语句创建方法

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

SQL 语句返回行

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

下一个向导屏幕会提示我们使用查询。 若要返回每个类别的 CategoryIDCategoryNameDescription 字段,以及与该类别关联的产品数,请使用以下 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 列相关联。

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

将 New TableAdapter 命名为 Methods FillWithNumberOfProducts 和 GetCategoriesAndNumberOfProducts

图 10:将 New TableAdapter 命名为“方法 FillWithNumberOfProducts ”, (GetCategoriesAndNumberOfProducts单击以查看全尺寸图像)

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

[System.ComponentModel.DataObjectMethodAttribute
    (System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.CategoriesDataTable GetCategoriesAndNumberOfProducts()
{
    return Adapter.GetCategoriesAndNumberOfProducts();
}

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

当 Repeater 恢复其原始状态时,通过 Repeater 的智能标记添加名为 CategoriesDataSource 的新 ObjectDataSource。 将 ObjectDataSource 配置为使用 CategoriesBLL 类,但不要让它使用 GetCategories() 方法,而是让它改用 GetCategoriesAndNumberOfProducts() (请参阅图 11) 。

将 ObjectDataSource 配置为使用 GetCategoriesAndNumberOfProducts 方法

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

接下来,更新 ItemTemplate ,以便使用数据绑定语法以声明方式分配 LinkButton 属性 Text ,并包括 CategoryNameNumberOfProducts 数据字段。 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 以查看显示) 类别名称和产品数量的 Repeater 的屏幕截图。

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

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

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

注意

这是在中继器中使用 LinkButton 时出现的挑战之一。 如果改用超链接通过 querystring 传入 CategoryID ,则可使用该 QueryString 字段作为参数值的源。

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

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

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

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

GetProductsByCategoryID(categoryID)由于 方法需要输入参数 (categoryID) ,因此“配置数据源”向导允许我们指定参数的 源。 如果类别已在 GridView 或 DataList 中列出,我们将参数源下拉列表设置为 Control,将 ControlID 设置为 ID 数据 Web 控件的 。 但是,由于 Repeater 缺少 SelectedValue 属性,因此不能将其用作参数源。 如果检查,你会发现 ControlID 下拉列表仅包含一个控件 ID``CategoryProductsID即 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 参数 categoryID ,因此查看页面时不会显示任何产品。 我们需要做的是基于在 Repeater 中单击的类别的 设置 CategoryID 此参数值。 这带来了两个挑战:第一,我们如何确定何时单击了中继器 中的 ItemTemplate LinkButton;第二,如何确定 CategoryID 单击了 LinkButton 的相应类别的 ?

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

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

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

类别 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 参数CategoryID分配给CategoryProductsDataSource传入CommandArgument的值。 对 ObjectDataSource 的 SelectParameters 此修改会自动导致 DataList 将自身重新绑定到数据源,并显示新选择的类别的产品。

protected void Categories_ItemCommand(object source, RepeaterCommandEventArgs e)
{
    // If it's the "ListProducts" command that has been issued...
    if (string.Compare(e.CommandName, "ListProducts", true) == 0)
    {
        // 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();
    }
}

添加这些内容后,我们的教程就完成了! 花点时间在浏览器中进行测试。 图 14 显示了首次访问页面时的屏幕。 由于尚未选择某个类别,因此不会显示任何产品。 单击某个类别(如“生产”)在两列视图中的“产品”类别中显示这些产品 (请参阅图 15) 。

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

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

单击“产品类别”Lists右侧的“匹配产品”

图 15:单击“产品类别”Lists右侧的“匹配产品” (单击以查看全尺寸图像)

总结

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

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

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

编程愉快!

深入阅读

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

  • 使用 CSS 浮动 CSS 元素的浮动教程
  • CSS 定位 有关使用 CSS 定位元素的详细信息
  • 使用 和用于定位的其他 HTML 元素对内容进行<table>布局

关于作者

Scott Mitchell 是七本 ASP/ASP.NET 书籍的作者, 4GuysFromRolla.com 的创始人,自 1998 年以来一直从事 Microsoft Web 技术工作。 Scott 担任独立顾问、培训师和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 2.0自学。 可以在 上联系 mitchell@4GuysFromRolla.com他, 也可以通过他的博客联系到他,该博客可在 http://ScottOnWriting.NET中找到。

特别感谢

本教程系列由许多有用的审阅者查看。 本教程的首席审阅者是 Zack Jones。 有兴趣查看我即将发布的 MSDN 文章? 如果是,请在 处放置一行 mitchell@4GuysFromRolla.com。