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

作者 :斯科特·米切尔

下载 PDF

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

介绍

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

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

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

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

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

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

本教程的类别为产品主/细节界面,基于前面的 Master/Detail Filtering With a DropDownList 教程中介绍的概念。 如果尚未完成前面的教程,请在继续学习此教程之前执行此操作。

步骤 1:添加 Categories DropDownList 和 Products GridView

在将摘要信息添加到 GridView 页脚之前,让我们先生成主/详细信息报告。 完成第一步后,我们将了解如何包括摘要数据。

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

添加名为 CategoriesDataSource 的新 ObjectDataSource

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

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

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

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

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

图 4:分别使用CategoryNameCategoryID字段作为 Text ListItem Value s(单击可查看全尺寸图像)

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

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

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

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

添加名为 ProductsInCategoryDataSource 的新 ObjectDataSource

图 6:添加名为 /> 的新 ObjectDataSource (单击以查看全尺寸图像ProductsInCategoryDataSource

配置 ObjectDataSource,以便调用 ProductsBLLGetProductsByCategoryID(categoryID) 的方法。

让 ObjectDataSource 调用 GetProductsByCategoryID(categoryID) 方法

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

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

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

图 8:从所选类别 DropDownList 获取 categoryID 参数值(单击以查看全尺寸图像

完成向导后,GridView 将为每个产品属性提供 BoundField。 让我们清理这些 BoundField,以便仅ProductNameUnitPriceUnitsInStockUnitsOnOrder显示和 BoundFields。 可以随意将任何字段级设置添加到剩余的 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:从所选类别 DropDownList 获取 categoryID 参数值(单击以查看全尺寸图像

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

将 GridView 的 ShowFooter 属性设置为 true

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

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

GridView 现在包括页脚行

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

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

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

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

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

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

<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 页面发出此查询,而是通过在页面中创建方法 ProductsTableAdapterProductsBLL发出此查询。

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

我通常采用第二种方法,因为它保存了数据库行程,并且需要努力在数据访问层和业务逻辑层中实现摘要功能,但任一方法都足够。 在本教程中,我们将使用第二个选项,并使用事件处理程序跟踪正在运行的总数 RowDataBound

RowDataBound在设计器中选择 GridView,单击属性窗口中的闪电图标,然后双击RowDataBound事件,为 GridView 创建事件处理程序。 这将创建一个名为页面代码隐藏类的新事件处理程序ProductsInCategory_RowDataBoundSummaryDataInFooter.aspx

protected void ProductsInCategory_RowDataBound
    (object sender, GridViewRowEventArgs e)
{
}

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

  • _totalUnitPrice,类型 decimal
  • _totalNonNullUnitPriceCount,类型 int
  • _totalUnitsInStock,类型 int
  • _totalUnitsOnOrder,类型 int

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

// Class-scope, running total variables...
decimal _totalUnitPrice = 0m;
int _totalNonNullUnitPriceCount = 0;
int _totalUnitsInStock = 0;
int _totalUnitsOnOrder = 0;
protected void ProductsInCategory_RowDataBound(object sender,
  GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        // Reference the ProductsRow via the e.Row.DataItem property
        Northwind.ProductsRow product =
          (Northwind.ProductsRow)
          ((System.Data.DataRowView)e.Row.DataItem).Row;
        // Increment the running totals (if they are not NULL!)
        if (!product.IsUnitPriceNull())
        {
            _totalUnitPrice += product.UnitPrice;
            _totalNonNullUnitPriceCount++;
        }
        if (!product.IsUnitsInStockNull())
            _totalUnitsInStock += product.UnitsInStock;
        if (!product.IsUnitsOnOrderNull())
            _totalUnitsOnOrder += product.UnitsOnOrder;
    }
}

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

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

protected void ProductsInCategory_RowDataBound
    (object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
      ... Increment the running totals ...
    }
    else if (e.Row.RowType == DataControlRowType.Footer)
    {
      ... Display the summary data in the footer ...
    }
}

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

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

protected void ProductsInCategory_RowDataBound
    (object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
      ... <i>Increment the running totals</i> ...
    }
    else if (e.Row.RowType == DataControlRowType.Footer)
    {
      // Determine the average UnitPrice
      decimal avgUnitPrice = _totalUnitPrice / (decimal) _totalNonNullUnitPriceCount;
      // Display the summary data in the appropriate cells
      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();
    }
}

图 13 显示添加此代码后的报告。 请注意, ToString("c") 导致平均价格摘要信息的格式与货币类似。

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

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

总结

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

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

快乐编程!

关于作者

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