简介
除了能看到每件产品的价格、存货量、订单量及续订单级别外,用户可能还对汇总数据感兴趣,如平均价格、库存产品的总量等。这类小结信息通常显示在报表底部的摘要行中。GridView 控件可以将一个脚注行包含到这些单元格中,我们可以通过编程写入聚合数据。
这涉及到三个任务:
- 配置 GridView 以显示其脚注行;
- 确定摘要数据 , 即如何计算平均价格或所有库存产品的总量;
- 将摘要数据注入脚注行的相应单元格。
本教程将介绍如何实现这些任务。具体地说,我们将创建一个页面,用下拉列表列出类别,并在GridView 中显示选中类别的产品。GridView 将包含一个脚注行,显示该类别产品的平均价格、总存货量及总订单量。
图1: GridView 的脚注行中显示摘要信息
本教程的类别到产品主/ 明细界面,都基于前面 使用 DropDownList 的主 /明细筛选 教程中的概念。如果您尚未学习该教程,建议学习后再来阅读本教程。
步骤1 :添加Categories DropDownList 及Products GridView
在开始向GridView 脚注添加小结信息之前,首先创建一个主/ 明细报表。完成第一步之后,再来看看如何添加摘要数据。
首先,打开CustomFormatting 中的SummaryDataInFooter.aspx 页面。添加一个 DropDownList 控件,将其ID 赋值为 Categories 。然后单击 DropDownList 的智能标记上的 Choose Data Source 链接,选择添加一个名为 CategoriesDataSource 的新 ObjectDataSource ,它调用 CategoriesBLL 类的 GetCategories() 方法。
图2 :添加一个名为 CategoriesDataSource 的新 ObjectDataSource
图3 :使 ObjectDataSource 调用 CategoriesBLL 类的 GetCategories() 方法
配置完ObjectDataSource 后,向导 返回 DropDownList 的Data Source Configuration 向导。这里需要指定要显示的数据字段值,以及哪个数据字段与 DropDownList 的 ListItems 值相对应。设置显示 CategoryName 字段,并 使用 CategoryID 作为值。
图4 :使用 CategoryName 和 CategoryID 字段分别作为 ListItems 的文本和值
现在我们有了一个列出系统中类别的 DropDownList (Categories )。接下来需要添加一个 GridView ,列出属于选中类别的那些产品。但在开始操作之前,应花点时间勾选上 DropDownList 的智能标记上的 Enable AutoPostBack 复选框。在使用 DropDownList 的主/明细筛选 教程中讲过,若将DropDownList 的 AutoPostBack 属性赋值为 true ,则每次更改 DropDownList 值时都将回传页面。这将导致 GridView 被刷新,显示新选中类别的那些产品。如果将 AutoPostBack 属性赋值为 false (默认值),那么更改类别不会导致回传,从而也就不会更新所列出的产品。
图5 :勾选上 DropDownList 的智能标记中的 Enable AutoPostBack 复选框
向页面添加一个 GridView 控件以显示选中类别的产品。将 GridView 的 ID 赋值为 ProductsInCategory ,并将其绑定到一个名为 ProductsInCategoryDataSource 的新 ObjectDataSource 。
图6:添加一个名为 ProductsInCategoryDataSource 的新ObjectDataSource
配置ObjectDataSource ,使其调用 ProductsBLL 类的GetProductsByCategoryID(categoryID) 方法。
图7 :使 ObjectDataSource 调用GetProductsByCategoryID(categoryID) 方法
由于GetProductsByCategoryID(categoryID) 方法携带了一个输入参数,因此我们可以在向导的最后一步中指定参数值的源。要显示选中类别的那些产品,设置从 Categories DropDownList 中获取参数。
图8 :从选中的 Categories DropDownList 中获取 categoryID 参数值
向导运行完成后,GridView 将为每个产品属性提供一个 BoundField 。我们将清除这些 BoundField ,只显示ProductName 、UnitPrice 、UnitsInStock 和 UnitsOnOrder 这几个 BoundField 。为剩下的这些 BoundField 随意添加字段级设置(如将 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>
现在我们有一个全功能的主/ 明细报表,显示属于选中类别的那些产品的名称、单价、存货量及订单量等信息。
图9 :从选中的 类别 DropDownList 获取 categoryID 参数值
步骤2 :在GridView 中显示脚注
GridView 控件可以显示标题行和脚注行。这些行分别根据ShowHeader 和 ShowFooter 属性的值显示。默认状态下,ShowHeader 的值为 true , ShowFooter 的值为 false 。要在 GridView 中显示脚注,只需将其 ShowFooter 值赋值为 true 即可。
图10 : 将 GridView 的 ShowFooter 属性赋值为 true
脚注行针对GridView 中定义的每个字段有一个单元格。但这些单元格默认为空。花点时间在浏览器中查看一下进度。因为现在 ShowFooter 属性被赋值为 true ,所以 GridView 包含一个空的脚注行。
图11 :现在 GridView 包含一个脚注行
图11 中的脚注行并不突出,因为其背景是白色的。我们在 Styles.css 中创建一个 FooterStyle CSS 类,指定一个暗红色背景,然后在 DataWebControls Theme 中 配置 GridView.skin Skin 文件,将此 CSS 类指定到 GridView 的 FooterStyle 的 CssClass 属性。如果您需要复习有关 Skins 和 Themes 的内容,请参阅前面的 用 ObjectDataSource 显示数据 教程。
首先,向Styles.css 添加CSS 类如下:
.FooterStyle
{
#a33;
color: White;
text-align: right;
}
FooterStyle CSS 类在样式上与 HeaderStyle 类相似,但HeaderStyle 的背景颜色稍深一点,且文本显示为加粗字体。此外,脚注中的文本是右对齐的,而标题文本是居中显示的。
然后是将此 CSS 类与每个 GridView 的脚注相关联:打开 DataWebControls Theme 中的 GridView.skin 文件并为 FooterStyle 的 CssClass 属性赋值。完成此操作后,文件的标记应如下所示:
<asp:GridView runat="server" CssClass="DataWebControlStyle">
<AlternatingRowStyle CssClass="AlternatingRowStyle" />
<RowStyle CssClass="RowStyle" />
<HeaderStyle CssClass="HeaderStyle" />
<FooterStyle CssClass="FooterStyle" />
<SelectedRowStyle CssClass="SelectedRowStyle" />
</asp:GridView>
如下图的屏幕截图所示,完成此操作后,脚注突出显示。
图12 :GridView 的脚注行现在显示为红色背景
步骤3 :计算摘要数据
显示了GridView 的脚注后,接下来是如何计算摘要数据。有两种方法计算汇总信息:
-
- 通过 SQL 查询 : 我们可以向数据库发出一个额外查询 ,以 计算某类别的摘要数据。 SQL 提供了多个汇总功能以及一个 GROUP BY 子句,用于指定作为汇总基础的数据。下列 SQL 查询将返回所需信息:
SELECT CategoryID, AVG(UnitPrice), SUM(UnitsInStock),
SUM(UnitsOnOrder)
FROM Products
WHERE CategoryID = categoryID
GROUP BY CategoryID
- 通过 SQL 查询 : 我们可以向数据库发出一个额外查询 ,以 计算某类别的摘要数据。 SQL 提供了多个汇总功能以及一个 GROUP BY 子句,用于指定作为汇总基础的数据。下列 SQL 查询将返回所需信息:
当然,您不想直接从SummaryDataInFooter.aspx 页面发起查询,而是在 ProductsTableAdapter 和 ProductsBLL 中创建一个方法。
- 在信息添加到 GridView 时进行计算:如 基于数据设置自定义格式教程中所讨论的,GridView 的 RowDataBound Event Handler 为每个添加到 GridView 的行触发一次,触发时间是在数据绑定之后。通过为此事件创建一个 Event Handler,我们可以始终具有运行中的待汇总值的总数。最后一个数据行被绑定到 GridView 之后,我们就可以计算出总数,以及具有计算平均数所需的信息。
我通常使用第二种方法,因为此方法无需访问数据库,也无需使用数据访问层和业务逻辑层的摘要功能。当然这两种方法都可以达到目的。本教程中将使用第二种方法,并使用 RowDataBound Event Handler 记录运行中的总数。
为GridView 创建一个 RowDataBound Event Handler。方法为:在 Designer 中选中 GridView ,单击 Properties 窗口的闪电图标,然后双击 RowDataBound 事件。这将在 SummaryDataInFooter.aspx 页面的内含代码类中创建一个名为 ProductsInCategory_RowDataBound 的新 Event Handler。
protected void ProductsInCategory_RowDataBound
(object sender, GridViewRowEventArgs e)
{
}
为保持运行中的总数计算,我们需要在Event Handler 的范围外定义变量。创建如下 4 个页面级的变量:
- _totalUnitPrice, 十进制类型;
- _totalNonNullUnitPriceCount, 整数类型;
- _totalUnitsInStock, 整数类型;
- _totalUnitsOnOrder, 整数类型。
然后编写代码,以便为 RowDataBound Event Handler 中遇到的每个数据行添加这三个变量。
// 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;
}
}
确认我们正在处理 DataRow 后启动 RowDataBound Event Handler 。之后,刚被绑定到 e.Row 中的 GridViewRow 对象的 Northwind.ProductsRow 实例便被存储到变量产品中。然后,运行中的总数变量便随当前产品的相应值增加(假定它们不包含数据库 NULL 值)。我们将跟踪记录运行中的 UnitPrice 总数以及非 NULL UnitPrice 记录的数目,因为平均价格是这两个数的商。
步骤4 :在脚注中显示摘要数据
汇总了摘要信息后,最后一步就是将其显示在 GridView 的脚注行中。这一任务也可通过 RowDataBound Event Handler 以编程方式实现。回顾一下, RowDataBound Event Handler 为绑定到 GridView 的每行都要触发一次,包括脚注行。因此,我们可以通过如下代码增加Event Handler,以在脚注行中显示数据:
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,其中Cells 索引从 0 开始。下列代码将计 算平均价格(总价格除以产品数量),将其与总存货量、订单量一起显示在 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") 是如何将平均价格摘要信息变为类似于货币的格式的。
图13 :GridView 的脚注行现在显示为红色背景
小结
显示摘要数据是常见的报表需求。通过 GridView 控件可以轻松地在其脚注行中显示这些信息。当 GridView 的 ShowFooter 属性被赋值为 true 时显示脚注行。通过 RowDataBound Event Handler ,可以编程方式为其单元格中的文本赋值。计算摘要数据可以通过重复查询数据库实现,也可在 ASP.NET 页面的内含代码类中使用代码,以编程方式实现。
本教程学习了如何设置 GridView 、DetailsView 和FormView 控件的自定义格式。下一篇教程将介绍使用这些控件来插入、更新和删除数据。
快乐编程!