在 GridView 的页脚中显示摘要信息 (VB)

作者 :Scott Mitchell

下载 PDF

摘要信息通常显示在报表底部的摘要行中。 GridView 控件可以包含一个页脚行,我们可以以编程方式注入聚合数据的单元格。 在本教程中,我们将了解如何在此页脚行中显示聚合数据。

简介

除了查看每个产品的价格、库存单位、订单单位和重新订购级别外,用户还可能对汇总信息感兴趣,例如平均价格、库存总单位数等。 此类摘要信息通常显示在报表底部的摘要行中。 GridView 控件可以包含一个页脚行,我们可以以编程方式注入聚合数据的单元格。

此任务向我们提出了三个挑战:

  1. 配置 GridView 以显示其页脚行
  2. 确定摘要数据;也就是说,我们如何计算股票中的平均价格或总单位数?
  3. 将摘要数据注入页脚行的相应单元格

在本教程中,我们将了解如何克服这些挑战。 具体而言,我们将创建一个页面,用于列出下拉列表中的类别,其中所选类别的产品显示在 GridView 中。 GridView 将包含一个页脚行,该行显示该类别中产品的平均价格和库存单位总数和订单数量。

摘要信息显示在 GridView 的页脚行中

图 1:摘要信息显示在 GridView 的页脚行 (单击以查看全尺寸图像)

本教程的类别为产品母版/详细信息接口,它基于前面的 使用 DropDownList 筛选母版/详细信息筛选 教程中介绍的概念。 如果尚未完成前面的教程,请先完成此操作,然后再继续学习此教程。

步骤 1:添加类别 DropDownList 和 Products GridView

在将摘要信息添加到 GridView 的页脚之前,让我们先简单地生成大纲/详细信息报表。 完成第一步后,我们将了解如何包含摘要数据。

首先打开 SummaryDataInFooter.aspx 文件夹中的页面 CustomFormatting 。 添加 DropDownList 控件并将其设置为 IDCategories。 接下来,单击 DropDownList 的智能标记中的“选择数据源”链接,并选择添加一个名为 CategoriesDataSource 的新 ObjectDataSource,用于调用 CategoriesBLL 类的 GetCategories() 方法。

添加名为 CategoriesDataSource 的新 ObjectDataSource

图 2:添加名为 CategoriesDataSource 的新对象DataSource (单击以查看全尺寸图像)

让 ObjectDataSource 调用 CategoriesBLL 类的 GetCategories () 方法

图 3:让 ObjectDataSource 调用 CategoriesBLL 类的方法 GetCategories() (单击以查看全尺寸图像)

配置 ObjectDataSource 后,向导会将我们返回到 DropDownList 的数据源配置向导,我们需要从该向导中指定应显示哪些数据字段值,以及哪个值应与 DropDownList 的值 ListItem 相对应。 CategoryName显示字段并使用 CategoryID 作为值。

将 CategoryName 和 CategoryID 字段分别用作 ListItems 的文本和值

图 4:将 CategoryNameCategoryID 字段分别用作 TextValueListItem ,分别 (单击以查看全尺寸图像)

此时,我们有一个 DropDownList (Categories) ,用于列出系统中的类别。 我们现在需要添加一个 GridView,用于列出属于所选类别的产品。 不过,在此之前,请花点时间检查 DropDownList 的智能标记中的“启用 AutoPostBack”复选框。 如 使用 DropDownList 筛选母版/详细信息筛选 教程中所述,通过将 DropDownList 的 AutoPostBack 属性设置为 True 页面,每次更改 DropDownList 值时,都会回发该页。 这将导致 GridView 刷新,显示新所选类别的这些产品。 如果 属性 AutoPostBack 设置为 False (默认) ,则更改类别不会导致回发,因此不会更新列出的产品。

选中 DropDownList 的智能标记中的“启用 AutoPostBack”复选框

图 5:选中 DropDownList 的智能标记中的“启用 AutoPostBack”复选框 (单击以查看全尺寸图像)

向页面添加 GridView 控件,以显示所选类别的产品。 将 GridView 的 ID 设置为 ProductsInCategory ,并将其绑定到名为 ProductsInCategoryDataSource的新 ObjectDataSource。

添加名为 ProductsInCategoryDataSource 的新 ObjectDataSource

图 6:添加名为 ProductsInCategoryDataSource 的新对象DataSource (单击以查看全尺寸图像)

配置 ObjectDataSource,以便调用 ProductsBLL 类的 GetProductsByCategoryID(categoryID) 方法。

让 ObjectDataSource 调用 GetProductsByCategoryID (categoryID) 方法

图 7:让 ObjectDataSource 调用 GetProductsByCategoryID(categoryID) 方法 (单击以查看全尺寸图像)

GetProductsByCategoryID(categoryID)由于 该方法采用输入参数,因此在向导的最后一步中,我们可以指定参数值的源。 若要显示所选类别中的这些产品,请从 Categories DropDownList 中提取 参数。

显示“配置数据源”窗口的屏幕截图,其中选择了 categoryID 参数值。

图 8:从“所选类别”下拉列表中获取 categoryID 参数值 (单击以查看全尺寸图像)

完成向导后,GridView 将具有每个产品属性的 BoundField。 让我们清理这些 BoundField,以便仅 ProductName显示 、 UnitPriceUnitsInStockUnitsOnOrder BoundField。 可以随意将任何字段级设置添加到剩余的 BoundFields (,例如将 UnitPrice 设置为货币) 。 进行这些更改后,GridView 的声明性标记应如下所示:

<asp:GridView ID="ProductsInCategory" runat="server" AutoGenerateColumns="False"
    DataKeyNames="ProductID" DataSourceID="ProductsInCategoryDataSource"
    EnableViewState="False">
    <Columns>
        <asp:BoundField DataField="ProductName" HeaderText="Product"
          SortExpression="ProductName" />
        <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}"
          HeaderText="Price"
            HtmlEncode="False" SortExpression="UnitPrice">
            <ItemStyle HorizontalAlign="Right" />
        </asp:BoundField>
        <asp:BoundField DataField="UnitsInStock"
          HeaderText="Units In Stock" SortExpression="UnitsInStock">
            <ItemStyle HorizontalAlign="Right" />
        </asp:BoundField>
        <asp:BoundField DataField="UnitsOnOrder"
          HeaderText="Units On Order" SortExpression="UnitsOnOrder">
            <ItemStyle HorizontalAlign="Right" />
        </asp:BoundField>
    </Columns>
</asp:GridView>

此时,我们有一个功能齐全的主/详细信息报表,其中显示了属于所选类别的产品的名称、单价、库存单位和订单单位。

显示属于“饮料”类别的产品的 GridView 报表的屏幕截图。

图 9:从所选类别下拉列表中获取 categoryID 参数值 (单击以查看全尺寸图像)

GridView 控件可以同时显示页眉和页脚行。 这些行根据 和 属性的值ShowHeader分别显示,ShowHeader默认为 TrueShowFooterFalseShowFooter 若要在 GridView 中包含页脚,只需将其 ShowFooter 属性设置为 True

将 GridView 的 ShowFooter 属性设置为 True

图 10:将 GridView 的 ShowFooter 属性设置为 True (单击以查看全尺寸图像)

页脚行包含 GridView 中定义的每个字段的单元格;但是,这些单元格默认为空。 花点时间在浏览器中查看进度。 由于 属性 ShowFooter 现在设置为 True,GridView 将包含一个空的页脚行。

GridView 现在包括页脚行

图 11:GridView 现在包含页脚行 (单击以查看全尺寸图像)

图 11 中的页脚行没有突出,因为它的背景为白色。 让我们在 中创建一个 FooterStyle CSS 类,该类指定深红色背景,然后在 Theme 中DataWebControls配置 GridView.skin Skin 文件,以将此 CSS 类分配给 GridView 的 FooterStyleCssClass 的 属性。Styles.css 如果需要对外观和主题进行刷写,请参阅 使用 ObjectDataSource 显示数据 教程。

首先将以下 CSS 类添加到 Styles.css

.FooterStyle
{
    background-color: #a33;
    color: White;
    text-align: right;
}

FooterStyle CSS 类的样式与 HeaderStyle 类相似,但 HeaderStyle的背景色较深,其文本以粗体字体显示。 此外,页脚中的文本右对齐,而页眉的文本居中。

接下来,若要将此 CSS 类与每个 GridView 的页脚相关联,请在 Theme 中DataWebControls打开 GridView.skin 文件并设置 FooterStyleCssClass 属性。 添加此内容后,文件的标记应如下所示:

<asp:GridView runat="server" CssClass="DataWebControlStyle">
   <AlternatingRowStyle CssClass="AlternatingRowStyle" />
   <RowStyle CssClass="RowStyle" />
   <HeaderStyle CssClass="HeaderStyle" />
   <FooterStyle CssClass="FooterStyle" />
   <SelectedRowStyle CssClass="SelectedRowStyle" />
</asp:GridView>

如下面的屏幕截图所示,此更改使页脚更加清晰。

显示 GridView 的页脚行中采用新背景色格式的摘要数据的屏幕截图。

图 12:GridView 的页脚行现在具有红色背景色 (单击以查看全尺寸图像)

步骤 3:计算摘要数据

显示 GridView 的页脚后,我们面临的下一个挑战是如何计算摘要数据。 有两种方法可以计算此聚合信息:

  1. 通过 SQL 查询,我们可以向数据库发出额外的查询,以计算特定类别的摘要数据。 SQL 包含许多聚合函数和 子 GROUP BY 句,用于指定应对其汇总数据的数据。 以下 SQL 查询将返回所需的信息:

    SELECT CategoryID, AVG(UnitPrice), SUM(UnitsInStock),
    SUM(UnitsOnOrder)
    FROM Products
    WHERE CategoryID = categoryID
    GROUP BY CategoryID
    

    当然,你不希望直接从SummaryDataInFooter.aspx页面发出此查询,而是通过在 和 ProductsBLL中创建方法ProductsTableAdapter发出此查询。

  2. 按照 基于数据的自定义格式 设置教程中所述,在添加到 GridView 时计算此信息,GridView 的 RowDataBound 事件处理程序对数据绑定后添加到 GridView 的每一行触发一次。 通过为此事件创建事件处理程序,我们可以保持要聚合的值的运行总数。 将最后一个数据行绑定到 GridView 后,我们将获得计算平均值所需的总计和信息。

我通常采用第二种方法,因为它可以节省数据库行程,以及实现数据访问层和业务逻辑层中的摘要功能所需的工作量,但两种方法都已足够。 在本教程中,让我们使用第二个选项,并使用 事件处理程序跟踪正在运行的 RowDataBound 总计。

RowDataBound通过选择Designer中的 GridView,单击属性窗口中的闪电图标,然后双击RowDataBound事件,为 GridView 创建事件处理程序。 或者,可以从 ASP.NET 代码隐藏类文件顶部的下拉列表中选择 GridView 及其 RowDataBound 事件。 这将在页面的代码隐藏类中创建SummaryDataInFooter.aspx名为 ProductsInCategory_RowDataBound 的新事件处理程序。

Protected Sub ProductsInCategory_RowDataBound _
    (sender As Object, e As GridViewRowEventArgs) _
        Handles ProductsInCategory.RowDataBound
End Sub

为了保持正在运行的总计,我们需要在事件处理程序的范围之外定义变量。 创建以下四个页面级变量:

  • _totalUnitPrice,类型为 Decimal
  • _totalNonNullUnitPriceCount,类型为 Integer
  • _totalUnitsInStock,类型为 Integer
  • _totalUnitsOnOrder,类型为 Integer

接下来,编写代码以递增事件处理程序中遇到的每个数据行的这三个 RowDataBound 变量。

Dim _totalUnitPrice As Decimal = 0
Dim _totalNonNullUnitPriceCount As Integer = 0
Dim _totalUnitsInStock As Integer = 0
Dim _totalUnitsOnOrder As Integer = 0
Protected Sub ProductsInCategory_RowDataBound _
    (sender As Object, e As GridViewRowEventArgs) _
        Handles ProductsInCategory.RowDataBound
    If e.Row.RowType = DataControlRowType.DataRow Then
        Dim product As Northwind.ProductsRow = _
            CType(CType(e.Row.DataItem, DataRowView).Row, Northwind.ProductsRow)
        If Not product.IsUnitPriceNull() Then
            _totalUnitPrice += product.UnitPrice
            _totalNonNullUnitPriceCount += 1
        End If
        If Not product.IsUnitsInStockNull() Then
            _totalUnitsInStock += product.UnitsInStock
        End If
        If Not product.IsUnitsOnOrderNull() Then
            _totalUnitsOnOrder += product.UnitsOnOrder
        End If
    ElseIf e.Row.RowType = DataControlRowType.Footer Then
        Dim avgUnitPrice As Decimal = _
            _totalUnitPrice / CType(_totalNonNullUnitPriceCount, Decimal)
        e.Row.Cells(1).Text = "Avg.: " & avgUnitPrice.ToString("c")
        e.Row.Cells(2).Text = "Total: " & _totalUnitsInStock.ToString()
        e.Row.Cells(3).Text = "Total: " & _totalUnitsOnOrder.ToString()
    End If
End Sub

事件处理程序 RowDataBound 首先确保处理 DataRow。 建立该实例后, Northwind.ProductsRow 刚刚绑定到 GridViewRow 中的 e.Row 对象的实例将存储在 变量 product中。 接下来,运行的总变量按当前产品的相应值递增, (假设它们不包含数据库 NULL 值) 。 我们跟踪运行 UnitPrice 总数和非记录数,NULLUnitPrice 因为平均价格是这两个数字的商。

汇总汇总了汇总数据后,最后一步是将其显示在 GridView 的页脚行中。 也可以通过 RowDataBound 事件处理程序以编程方式完成此任务。 回想一下, RowDataBound 事件处理程序会针对绑定到 GridView 的每 一行(包括页脚行)触发。 因此,我们可以使用以下代码扩充事件处理程序,以在页脚行中显示数据:

Protected Sub ProductsInCategory_RowDataBound _
    (sender As Object, e As GridViewRowEventArgs) _
        Handles ProductsInCategory.RowDataBound
    If e.Row.RowType = DataControlRowType.DataRow Then
      ... Increment the running totals ...
    ElseIf e.Row.RowType = DataControlRowType.Footer
      ... Display the summary data in the footer ...
    End If
End Sub

由于在添加所有数据行后,页脚行已添加到 GridView,因此我们可以确信,当我们准备好在页脚中显示摘要数据时,运行的总计算将已完成。 最后一步是在页脚的单元格中设置这些值。

若要显示特定页脚单元格中的文本,请使用 e.Row.Cells(index).Text = value,其中 Cells 索引从 0 开始。 以下代码计算 (总价格除以) 产品数的平均价格,并在 GridView 的适当页脚单元格中显示其库存单位总数和订单单位总数。

Protected Sub ProductsInCategory_RowDataBound _
    (sender As Object, e As GridViewRowEventArgs) _
        Handles ProductsInCategory.RowDataBound
    If e.Row.RowType = DataControlRowType.DataRow Then
      ... <i>Increment the running totals</i> ...
    ElseIf e.Row.RowType = DataControlRowType.Footer
      Dim avgUnitPrice As Decimal = _
        _totalUnitPrice / CType(_totalNonNullUnitPriceCount, Decimal)
      e.Row.Cells(1).Text = "Avg.: " & avgUnitPrice.ToString("c")
      e.Row.Cells(2).Text = "Total: " & _totalUnitsInStock.ToString()
      e.Row.Cells(3).Text = "Total: " & _totalUnitsOnOrder.ToString()
    End If
End Sub

图 13 显示了添加此代码后的报告。 请注意 如何 ToString("c") 使平均价格摘要信息的格式类似于货币。

显示 GridView 页脚行中格式为货币的摘要数据的屏幕截图。

图 13:GridView 的页脚行现在具有红色背景色 (单击以查看全尺寸图像)

总结

显示摘要数据是一种常见的报表要求,GridView 控件可以轻松地将此类信息包含在其页脚行中。 当 GridView ShowFooter 的 属性设置为 True 时,将显示页脚行,并且可以通过事件处理程序以编程方式 RowDataBound 设置其单元格中的文本。 可以通过重新查询数据库或使用 ASP.NET 页的代码隐藏类中的代码以编程方式计算摘要数据来计算摘要数据。

本教程结束对 GridView、DetailsView 和 FormView 控件的自定义格式设置的检查。 下一篇教程将开始探索如何使用这些相同的控件插入、更新和删除数据。

编程快乐!

关于作者

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