GridViewID="ProductsInCategory"runat="server"
AutoGenerateColumns="False"DataKeyNames="ProductID"
DataSourceID="ProductsInCategoryDataSource"EnableViewState="False">
BoundFieldDataField="ProductName"HeaderText="Product"
SortExpression="ProductName"/>
BoundFieldDataField="UnitPrice"DataFormatString="{0:
c}"
HeaderText="Price"
HtmlEncode="False"SortExpression="UnitPrice">
BoundField>
BoundFieldDataField="UnitsInStock"
HeaderText="UnitsInStock"SortExpression="UnitsInStock">
BoundField>
BoundFieldDataField="UnitsOnOrder"
HeaderText="UnitsOnOrder"SortExpression="UnitsOnOrder">
BoundField>
GridView>
现在,我们就拥有了一个可以根据所选类别来显示相关的产品的名称、单价、库存量以及订货量的功能齐全的主/从报表了。
图九:
现在的效果(译者注:
估计原文这里弄错了,写得跟图八的一样。
这里的原文是“Figure9:
GetthecategoryIDParameterValuefromtheSelectedCategoriesDropDownList”,图八的原文是“Figure8:
GetthecategoryIDParameterValuefromtheSelectedCategoriesDropDownList”)
第二步:
在GridView中显示页脚
GridView控件可以显示页眉和页脚行。
这些行的显示与否分别取决于ShowHeader和ShowFooter属性,默认情况下,ShowHeader的值为true,而ShowFooter的值为false。
要显示页脚行的话,我们只需简单的将ShowFooter属性设置为true就可以了。
图十:
将GridView的ShowFooter属性设置为true
GridView中所定义的每一个字段都在页脚行中有一个对应的单元格,不过这些单元格默认是空的。
花点时间到浏览器中看看我们的成果。
由于我们将GridView的ShowFooter属性设置为true了,所以GridView现在包含了一个空的页脚行。
图十一:
现在,GridView有了一个页脚行
图十一中的页脚行并不明显,因为它的背景是白色的。
让我们在Styles.css中创建一个名为FooterStyle的CSS类,用它来指定一个深红色的背景,并在DataWebControls主题中配置GridView.skin这个皮肤文件(Skinfile)以将此CSS类分配给此GridView的FooterStyle的CssClass属性。
如果你需要复习一下皮肤和主题的相关内容,请参考“使用ObjectDataSource显示数据”。
先给Styles.css添加以下的CSS类:
?
1
2
3
4
5
6
.FooterStyle
{
background-color:
#a33;
color:
White;
text-align:
right;
}
FooterStyle这个CSS类跟HeaderStyle类是一样的,只是HeaderStyle的背景色要深一点且文本是粗体显示的而已。
此外,页脚的文本是右对齐的,而页眉的文本是居中的。
然后,为了将这个CSS类关联到每一个GridView的页脚上,在DataWebControls主题中打开GridView.skin文件并设置FooterStyle的CssClass属性。
作了这个添加之后,文件的标记代码应该是这个样子:
?
1
2
3
4
5
6
7
GridViewrunat="server"CssClass="DataWebControlStyle">
GridView>
就像下面这个屏幕截图所显示的那样,这个更改使页脚清晰的显示出来了。
图十二:
GridView的页脚现在有了一个红红的背景色
第三步:
计算统计数据
在显示了GridView的页脚之后,下一个面对我们的挑战就是如何计算统计数据。
有两个计算统计信息的途径:
1.通过一个SQL查询——我们可以向数据库发出一个额外的查询来为某个特定的类别计算统计信息。
SQL包含一系列的聚合函数,并由GROUPBY子句指定应该根据什么数据来进行统计。
下面的SQL查询将会返回我们所需要的信息:
?
1
2
3
4
SELECTCategoryID,AVG(UnitPrice),SUM(UnitsInStock),SUM(UnitsOnOrder)
FROMProducts
WHERECategoryID=categoryID
GROUPBYCategoryID
当然,你也可能不喜欢直接在SummaryDataInFooter.aspx页面中直接执行这个查询,而是希望在ProductsTableAdapter和ProductsBLL中创建一个方法来干这个事情。
2.由于这些信息已经添加到GridView中了,所以可以直接计算——就像在“基于数据的自定义格式化”中讨论的那样,在GridView的数据绑定之后,GridView的RowDataBound事件处理方法会在添加每一行数据时被执行一次。
为这个事件创建了事件处理方法之后,我们就可以保持一个累积的合计值了。
在最后一行数据被绑定到DataGrid上之后,我们就有了一个合计值以及需要计算平均值的信息了。
一般来说,我还是喜欢第二种方法的,因为它节省了一次到数据库的往返,而且要达到这个效果还需要在数据访问层和业务逻辑层中实现统计功能,不过话说回来了,其实两种办法都行的。
在这本教程中,我们还是使用第二个办法吧,并使用RowDataBound事件处理方法来记录这个累积合计。
给GridView新建一个RowDataBound事件处理方法,你可以在设计器中选择GridView,然后在属性窗口中点击那个带闪电符号的图标,找到RowDataBound事件并双击它就可以了。
这样就会在SummaryDataInFooter.aspx页面的后置代码类中添加一个新的名为ProductsInCategory_RowDataBound的事件处理方法了。
?
1
2
3
4
protectedvoidProductsInCategory_RowDataBound
(objectsender,GridViewRowEventArgse)
{
}
为了可以维护一个累积合计,我们需要在这个事件处理方法的外面定义一些变量。
创建以下4个页面级的变量:
·_totalUnitPrice,类型为decimal
·_totalNonNullUnitPriceCount,类型为int
·_totalUnitsInStock,类型为int
·_totalUnitsOnOrder,类型为int
然后,在RowDataBound事件处理方法中写一些代码,使每一个数据行都可以增加这些变量的值。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//Class-scope,runningtotalvariables...
decimal_totalUnitPrice=0m;
int_totalNonNullUnitPriceCount=0;
int_totalUnitsInStock=0;
int_totalUnitsOnOrder=0;
protectedvoidProductsInCategory_RowDataBound(objectsender,
GridViewRowEventArgse)
{
if(e.Row.RowType==DataControlRowType.DataRow)
{
//ReferencetheProductsRowviathee.Row.DataItemproperty
Northwind.ProductsRowproduct=
(Northwind.ProductsRow)
((System.Data.DataRowView)e.Row.DataItem).Row;
//Incrementtherunningtotals(iftheyarenotNULL!
)
if(!
product.IsUnitPriceNull())
{
_totalUnitPrice+=product.UnitPrice;
_totalNonNullUnitPriceCount++;
}
if(!
product.IsUnitsInStockNull())
_totalUnitsInStock+=product.UnitsInStock;
if(!
product.IsUnitsOnOrderNull())
_totalUnitsOnOrder+=product.UnitsOnOrder;
}
}
在RowDataBound事件处理方法中,我们首先应该确保我们正在操作一个DataRow。
一旦确定了是这么回事,e.Row中那个刚刚绑定到GridViewRow对象的Northwind.ProductsRow实例就可以赋值给product变量了。
接着,累积合计就被当前产品的相关值(当然了,我们还是应该要确保它们不会含有一个数据库NULL值)增加了。
我们同时记录了累积的UnitPrice合计以及非空UnitPrice记录的条数,因为平均价格是这两个数的商。
第四步:
在页脚中显示统计数据
计算了统计数据之后,最后一个步骤就是在GridView的页脚上显示它了。
同样,这个任务也可以通过RowDataBound事件处理方法来完成。
回忆一下RowDataBound事件处理方法,它会在每一行绑定到GridView的时候被触发,页脚行也不例外。
因此,我们可以扩展我们的事件处理方法,让它可以通过如下的代码来在页脚行中显示数据:
?
1
2
3
4
5
6
7
8
9
10
11
12
protectedvoidProductsInCategory_RowDataBound
(objectsender,GridViewRowEventArgse)
{
if(e.Row.RowType==DataControlRowType.DataRow)
{
...Incrementtherunningtotals...
}
elseif(e.Row.RowType==DataControlRowType.Footer)
{
...Displaythesummarydatainthefooter...
}
}
因为页脚行是在所有的数据行都已经添加之后才添加到GridView中的,所以我们可以相信在将统计数据显示在页脚中之前,累积合计值都已经计算完毕了。
最后一步就是将这些值放到页脚的单元格中了。
要在页脚的特定单元格中显示文本,可以使用usee.Row.Cells[index].Text=value,单元格的索引是从0开始的。
下面的代码计算了平均价格(总的价格除以产品的数量)并将其与库存量和订货量一起显示到GridView页脚的相应单元格中。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
protectedvoidProductsInCategory_RowDataBound
(objectsender,GridViewRowEventArgse)
{
if(e.Row.RowType==DataControlRowType.DataRow)
{
...Incrementtherunningtotals...
}
elseif(e.Row.RowType==DataControlRowType.Footer)
{
//DeterminetheaverageUnitPrice
decimalavgUnitPrice=_totalUnitPrice/(decimal)_totalNonNullUnitPriceCount;
//Displaythesummarydataintheappropriatecells
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();
}
}
图十三展示了添加了这段代码之后这个报表的样子。
注意ToString("c")是如何让平均价格这个统计信息格式化成货币形式的。
图十三:
现在的效果(译者注:
估计原文这里又弄错了,写得跟图十二的一样。
这里的原文是“Figure13:
TheGridView'sFooterRowNowHasaReddishBackgroundColor”,图十二的一样。
这里的原文是“Figure13:
TheGridView'sFooterRowNowHasaReddishB的原文是“Figure12:
TheGridView'sFooterRowNowHasaReddishBackgroundColor”)
总结
显示统计信息是一个常见的报表需求,而GridView控件可以在页脚行中包含这样的信息,而且它将这个操作变得非常简单。
当GridView的ShowFooter属性被设置为true时就可以显示页脚行了,并且通过RowDataBound事件处理方法可以将这些信息显示在它的不同的单元格中。
可以通过重新查询数据库的数据,也可以在ASP.NET页面的后置代码类中通过编程的方式来计算这些统计数据。
本节教程结束了我们关于GridView、DetailsView以及FormView控件的自定义格式化的学习。
接下来的教程中,我们会开始一个使用这些控件来进行增删改操作的探索。