本文英文原版及代码下载:
http://aspnet.4guysfromrolla.com/articles/021308-1.aspx
利用ListView和DataPager控件来对数据分页
导言:
GridView, DetailsView,FormView控件等都支持分页功能. 当配置为支持分页时,这些控件都呈现为包含LinkButtons, Buttons,或ImageButtons的分页界面.我们通过设置相关的属性来定制分页界面,比如使用Next/Previous,数字分页等.虽然这些配置都很好,但实现用户自定义的余地很小.比如,配置选项允许你指定分页接口出现在控件的顶部或者底部(或上下都出现),但是如果你希望分页接口出现在页面的其它地方,与控件分离,那就没办法了.
为此,ASP.NET开发团队用ListView控件来解决,将该控件的分页支持剥离出来,用另一个控件DataPager来实现.DataPager控件的唯一目的就是呈现一个分页接口,并与相应的ListView控件关联起来.ListView 和 DataPager的这种剥离关系,可以允许进行更大程度的分页界面定制.
Paging Basics
DataPager控件使用如下3方面的信息来进行分页:
PagedControlID -与DataPager相关的ListView的ID值
StartRowIndex -当前页面要显示的记录的第一条记录的index索引值
MaximumRows - 每页最多显示的记录数
比如,要显示第一页的记录,且每页显示10条记录.那么StartRowIndex 和 MaximumRows的值就应分别为0 和 10,如果是第二页的记录,则为10 和 10. 第三页的为20 和 10.另外还有一个只读的TotalRowCount属性, 用于返回总记录条数.
DataPager包含一系列的DataPagerFields,每个DataPagerField展示一种分页接口. 在.NET Framework 3.5里有3个DataPagerFields: NextPreviousPagerField, NumericPagerField, 以及TemplatePagerField.其中,NextPreviousPagerField展示的是First/Previous/Next/Last按钮;而NumericPagerField展示的是page numbers;TemplatePagerField则是通过模板来创建分页接口,不过我们要自己创建该模板.
当用户使用该分页接口时,比如点击"Next"按钮时,接着产生页面回传,DataPagerField意识到这是由分页接口上的某个元素引起的. DataPagerField 就确定好StartRowIndex 和 MaximumRows的值,并传给DataPager类的SetPageProperties方法. 接着,DataPager调用其对应的ListView控件的SetPageProperties方法,该方法将促使ListView重新绑定数据,并展示我们期望的记录.
默认分页VS自定义分页
ListView 和 DataPager控件提供了2种分页模式:默认分页和自定义分页.这2种方式在性能和配置、使用等易操作性方面做出一种权衡.SqlDataSource控件使用的是默认分页、ObjectDataSource默认使用默认分页,不过它也可以通过一个很方便的机制来实现自定义分页.务必谨记的是,ListView仅仅是展示数据,从数据库检索数据实际上是它的数据源控件来完成的.
默认分页时,每次显示一页新数据时,都会检索所有的记录。一旦返回所有的记录后,ListView再基于StartRowIndex 和 MaximumRows值从所有记录中挑选出所需的记录.
自定义分页时,我们要多做一点工作.我们要配置数据源控件仅仅检索某个页面所需要的那些记录,而不是盲目地检索所有的记录.
关于默认分页和自定义分页,以及在性能和易操作性如何权衡的问题,请参阅文章《 Custom Paging in ASP.NET with SQL Server 2005》.
创建DataPager及定义其DataPagerFields
用ListView 和 DataPager控件贯彻分页虽不像点选一个checkbox那么简单,但也不会太困难.本文下载代码里包含2个DataPager案例,它们都基于《Displaying Data with the ListView》那篇文章里的SimpleListView.aspx,我们接下来将探讨.
首先,添加并配置ListView控件,使其按我们期望的格式显示数据.然后,添加一个或多个DataPager控件到页面,任意你期望的地方.DataPager可以放在ListView控件的LayoutTemplate模板,或页面上ListView控件之外的任何地方.
添加完DataPager后,在Visual Studio设计视图里它看起来是一个灰色的box.要定制DataPagerFields,点击DataPager智能标签里的"Edit Pager Fields"链接.这将打开Fields对话框(如下图).在这里,我们可以指定添加何种DataPagerFields,设置其相关属性.在这里我添加NextPreviousPagerField,并将ButtonType属性设置为Button(这样,它将在分页接口里呈现Button控件,而不是默认的LinkButtons控件),同时我将ShowFirstPageButton 和 ShowLastPageButton属性设置为True.这样它们就和Next 和Previous按钮一起出现在分页接口上.
图1
点击OK关闭Fields对话框后,DataPager的声明代码里就包含了DataPagerField信息,此外,在设计器里分页接口呈现为First/Previous/Next/Last按钮.
在测试页面之前,我们还要设置2个属性.首先将DataPager的PagedControlID属性设置为页面上相关的ListView的ID值.然后,设置DataPager的PageSize属性,指定相关联的 ListView每页展示记录的条数 (默认值为10).完成设置后,代码应和下面的类似:
<asp:DataPager ID="ProductListPagerSimple" runat="server"
PagedControlID="ProductList" PageSize="5">
<Fields>
<asp:NextPreviousPagerField ButtonType="Button" ShowFirstPageButton="True"
ShowLastPageButton="True" />
</Fields>
</asp:DataPager>
完成后在浏览器里测试,如下面的截屏所示,ListView控件每页只显示了5条记录(因为我将DataPager的PageSize属性设置为5),此外First 和 Previous按钮不可用,因为我们查看的是第一页的数据.
图2
上面截屏里的分页接口位于ListView的顶部,但务必牢记:一个ListView可用有多个DataPager控件.我们可以在ListView的底部再增加一个DataPager,将其PagedControlID 和 PageSize属性像上面那个DataPager一样进行设置.
在DataPager里指定多个DataPagerFields
前面的例子我们在DataPager里只使用了一个DataPagerField(也就是一个NextPreviousPagerField),不过我们可以在DataPager里使用多个DataPagerField.这种灵活性可以允许我们创建混合分页接口,比如包含numeric pages和First and Last buttons的分页接口.
为创建这样的一个接口,向页面添加一个DataPager,并设置好其PagedControlID 和 PageSize属性.然后打开Fields对话框,并添加3个DataPagerFields:一个NextPreviousPagerField,紧接着是一个NumericPagerField,最后一个还是NextPreviousPagerField.设置第一个NextPreviousPagerField的属性,使其只显示First按钮,同样的,设置最后那个NextPreviousPagerField,使其只显示Last按钮.放心大胆的随意设置First 和 Last按钮显示的文本——我将First按钮设置为<<,而将Last按钮的文本设置为>>,如下:
<asp:DataPager ID="ProductListPagerCombo" runat="server"
PagedControlID="ProductList" PageSize="5">
<Fields>
<asp:NextPreviousPagerField FirstPageText="<<" ShowFirstPageButton="True"
ShowNextPageButton="False" ShowPreviousPageButton="False" />
<asp:NumericPagerField />
<asp:NextPreviousPagerField LastPageText=">>" ShowLastPageButton="True"
ShowNextPageButton="False" ShowPreviousPageButton="False" />
</Fields>
</asp:DataPager>
最终结果是一个混合分页接口,如下图所示.注意页面上有2个DataPagers.顶部的为First/Previous/Next/Last形式的接口,而底部的为混合接口.
图3
用TemplatedPagerField创建一个用户定制分页接口
如果NumericPagerField 和 NextPreviousPagerField都不能满足你的分页接口要求,你可以利用TemplatedPagerField来创建你自定义的分页接口.使用它,你,作为一个page developer,就要自己来创建分页接口、探测用户什么时候点击了分页接口,并更新DataPager 和 ListView作为回应.
我们创建一个分页接口,它枚举所有的pages,而这些pages是一个DropDownList控件的ListItems,它允许用户在下拉列表里选择某个页面,直接跳到那个页面.为此,向页面添加一个DataPager控件,设置其使用TemplatedPagerField,我们可以直接在声明代码里实现,也可以在DataPager的智能标签里通过“Edit Templates”选项来实现.不管以哪种方式,向模板里添加一个DropDownList控件,设置其ID为PageJump,并将AutoPostBack属性设置为True.
接下来我们要将这些pages对DropDownList控件赋值.由于每次ListView绑定到数据源时都会发生,因此我们为ListView控件的DataBound事件创建一个事件处理器,添加如下的代码:
Protected Sub ProductList_DataBound(ByVal sender As Object, ByVal e As System.EventArgs) Handles ProductList.DataBound
Dim currentPage As Integer = (DataPagerID.StartRowIndex / DataPagerID.PageSize) + 1
Dim totalPages As Integer = DataPagerID.TotalRowCount / DataPagerID.PageSize
'Populate the DropDownList if needed
Dim ddl As DropDownList = CType(DataPagerID.Controls(0).FindControl("PageJump"), DropDownList)
If ddl.Items.Count = 0 Then
'Add a list item for each page
For i As Integer = 1 To totalPages
ddl.Items.Add(i.ToString())
Next
'Set the DDL to the appropriate page value
ddl.Items.FindByValue(currentPage.ToString()).Selected = True
End If
End Sub
代码首先处理当前要访问的页面,以及总页数.这都要用到DataPager控件的 StartRowIndex, PageSize,以及TotalRowCount属性.
然后编程引用id为PageJump的DropDownList控件.注意,为了引用TemplatePagerField里的控件,我们使用了稍微复杂点的代码:DataPagerID.Controls(0).FindControl("controlID").
遍历page numbers(从1到totalPages),为每一页添加一个ListItem.最后将当前页面对应的DropDownList item选中.
如果你现在登录该页面,你将看到在DataPager里包含了一个DropDownList控件,它的下拉列表里包含了所有的页面.此外,如果在下拉列表里选择另一个页面将触发页面回传,但显示的数据记录却并没有发生改变,这是因为我们还没有处理DropDownList控件的SelectedIndexChanged event事件——在该事件里我们需要调用DataPager的SetPageProperties方法,传入新的StartRowIndex 和 MaximumRows的值,这样才能更新ListView控件里展示的数据记录.
要给模板里的一个控件创建一个事务处理器,需要在在该控件的声明代码里添加OnEventName=EventHandler的语法构造.我们可以通过手动输入也可以在双击该DropDownList控件来实现.不管用哪种方式,创建好该事件处理器后添加如下的代码:
Protected Sub PageJump_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)
Dim PageJumpDDL As DropDownList = CType(sender, DropDownList)
Dim pageNo As Integer = Convert.ToInt32(PageJumpDDL.SelectedValue)
Dim startRowIndex As Integer = (pageNo - 1) * ProductListPager.PageSize
DataPagerID.SetPageProperties(startRowIndex, DataPagerID.PageSize, True)
End Sub
上述代码首先编程引用该id为PageJump 的DropDownList控件,以得到其SelectedValue值。被请求的page number用于计算StartRowIndex值,再将该值连同DataPager控件的PageSize值一起传递给DataPager的 SetPageProperties方法.
最终结果是DropDownList列出了所有的page number,当选择某页时将触发页面回传,并将该页的数据显示出来.
图4
结语:
与以前的控件不同,ListView并没有直接的支持分页,相反其分页接口由另一个Web control——DataPager来实现.而DataPager的界面又由其DataPagerFields来呈现..NET Framework有3个内置的DataPagerFields:也即NextPreviousPagerField, NumericPagerField,以及 TemplatePagerField.在本文我们探讨了如何使用这3种fields.
祝编程愉快!
ASP.NET 3.5's ListView and DataPager—Part4:利用ListView和DataPager控件来对数据分页