DataGrid的客户端分页
void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { BindDataGrid (); MyDataGrid.ClientCurrentPageIndex = 1; } }当ClientPageDataGrid所在页面的请求发送到服务器后,页面返回后,ClientCurrentPageIndex属性就被更新,来指示显示哪一页。服务器并不知道用户在看哪一页。 ![]() ClientPageDataGrid输出标页数的HTML表格从使用Render方法开始,如Figure 7所示。你可以通过本文上面的链接下载完整的源代码。Render是一个从System.Web.UI.Control继承来的一个虚方法。在Microsoft? .NET Framework中,当包括该控件的页面被请求时,Render方法被用来将控件加入到HTML中去。 ClientPageDataGrid重写了DataGrid的方法并且多次调用基类的实现,而不是一次。特别说明的是,ClientPageDataGrid在输出每一页时调用DataGrid.Render,同时通过对RenderBeginTag和RenderEndTag的调用将输出加入到<DIV>模块中。在调用基类的Render方法前,ClientPageDataGrid.Render调用一个本地的ShowItems方法来隐藏那些在页面上不显示的记录,方法是设置那些行的Visible属性为否(见Figure 8)。 当ClientPageDataGrid被设置为每页显示16条记录,也就意味着第1个<DIV>包括该表格的前16条记录,第2个<DIV>包括该表格紧接着的16条记录,以此类推。除1个之外所有的<DIV>模块通过设置样式 style=”display:none”来隐藏。只有显示页的索引等于ClientCurrentPageIndex时,<DIV>模块才被显示,它的样式被设置为style=”display:block”。 ClientPageDataGrid中的每一页包括一个分页器,该分页器存在于DataGrid当中,因为ClientPageDataGrid重写了DataGrid的DataBind方法,并且通过设置AllowPaging为真、PageSize设置为每页显示条数来调用。ClientPageDataGrid.cs中大部分代码就是来使页面正常工作,确保支持Previous/next-style分页器(<PagerStyle Mode="NextPrev">)和数字分页器(<PagerStyle Mode="NumericPages">)。 传统的包括LinkButtons的DataGrid分页器,发送页面请求到服务器,然后产生PageIndexChanged事件。ClientPageDataGrid用指向客户端的JavaScript函数的链接取代了这些LinkButtons。UpdatePager方法(如Figure 9所示),在Render调用基类的Render方法之前被取代。UpdatePager通过在DataGrid中搜索类型为ListItemType.Pager的行,来找到要显示的页面,接着就删除页面中的控件,加入需要的控件。 下面是一个传统的DataGrid,触发分页器时输出HTML的例子: <tr> <td colspan="3"> <a href= "javascript:__doPostBack(''MyDataGrid$_ctl20$_ctl0'','''')"> <</a> <a href="javascript:__doPostBack(''MyDataGrid$_ctl20$_ctl1'','''')"> ></a> </td> </tr>下面是由ClientPageDataGrid控件实现的同一个页面: <tr> <td colspan="3"> <a href="javascript:__onPrevPage (''MyDataGrid'');"><</a> <a href="javascript:__onNextPage (''MyDataGrid'');">></a> </td> </tr>当ClientPageDataGrid.OnPreRender调用Page.RegisterClientScriptBlock时,导航标记所指向的JavaScript函数就被打上标记。调用RegisterClientScriptBlock确保包括这些函数的脚本块仅仅被输出一次,甚至当页面包括ClientPageDataGrid的多个实例时也是这样。 虽然ClientPageDataGrid并没有包括任何自己的服务器请求,但是页面的其他控件仍然会导致向服务器的请求。这产生了两个问题。第一,当请求发生时,ClientCurrentPageIndex应当进行更新,因此服务器端代码能决定哪一页被显示。第二,当页面从服务器反馈回后,ClientPageDataGrid应当能被当前页保存。用另外的话说就是,当第3页显示时向服务器请求时,ClientPageDataGrid的代码应当加入一个样式style="display: block"给包括第3页的<DIV>模块,而其他<DIV>模块没有。 为解决这些问题,ClientPageDataGrid注册了一个隐含字段controlid__ PAGENUM: Page.RegisterHiddenField (ClientID + "__PAGENUM", ClientCurrentPageIndex.ToString ());当页面更新时,在客户端中通过输出页面进行分页的JavaScript函数来更新隐含的字段。当页面请求发给服务器后,隐含字段的值被加入到服务器返回数据中。ClientPageDataGrid的LoadViewState方法从返回的数据中读取这个值,并且通过RestoreCurrentPageIndex方法设置ClientCurrentPageIndex的值,代码如下: string page = Page.Request[ClientID + "__PAGENUM"]; ... ClientCurrentPageIndex = Convert.ToInt32 (page);请求时间处理代码能通过ClientCurrentPageIndex属性的值判断用户要读取哪一页。同时因为ClientPageDataGrid.Render使用ClientCurrentPageIndex来判断哪一个<DIV>被显示,因此当前显示页直到页面数据请求发生后才隐藏。 在LoadViewState带来的反馈数据中包括当前页索引非常有用。首先,当前页的索引不会被设置直到控件被组装,存在LoadViewState中。此外,由于LoadViewState在主页的装入事件触发前被调用,Page_Load方法能通过ClientCurrentPageIndex属性值来判断用户的当前页的索引。那样是非常重要的,因为当前页的索引会带来其他的变化。 RestoreCurrentPageIndex同样被ClientPageDataGrid的DataBind方法调用,而且仅仅是,如果不是被LoadViewState调用的话。为什么呢?因为如果视图状态被禁用了(即EnableViewState=false),则LoadViewState就不能被ASP.NET运行库调用。如果视图状态被禁用,页面就会调用DataBind重新装载控件,因此调用DataBind方法就是根据返回数据恢复当前页的索引就很自然了。 或许你注意到了ClientPageDataGrid的DataBind方法调用绑定数据并不是一次,而是两次(见Figure 10)。第1次调用是基类的DataBind方法给ClientPageDataGrid提供记录条数,产生一个包括所有数据的DataGrid。DataGrid放在ClientPageDataGrid下面,为描述数据源提供服务。 ClientPageDataGrid的DataBing方法另一个特别的地方是它将DataReaders转变为DataSets.。有两个理由这么做。首先,DataReader只能被绑定一次,因为它是只能向前读取的数据源。其次,通常的DataGrid并不支持DataReaders作为数据源,如果AllowPaging值为true的话,直到AllowCustomPaging也为true时才行。在程序内部巧妙地将DataReaders转换为DataSets就解决了这些问题,并且确保ClientPageDataGrid工作起来像使用DataReaders一样,而实际上是在使用DataSets。 ![]() 难道在客户端分页就没有缺点吗?你讲对了。数据量越大,页面装入速度就会越慢,因为要装入全部的数据,而不只是显示页的数据。相对较小的记录集,1000条记录或更少,你就可以不关注这个问题了。对于非常大的记录集,装入时间就将会是难以接受的长。 另外一个要考虑的因素是带宽,大的DataGrid会在两方面增加下载时间。第一会增大HTML表格,第二会增加视图状态的大小。因为ASP.NET通过一个隐含字段保持视图状态,DataGrid的内容本来就是在HTTP请求时显示一次,在HTTP响应时显示两次。这就是要保证数据量少,大的数据量还是最好用服务器端分页。当有大量的记录时你还要使用ClientPageDataGrid,最好设置控件的EnableViewState属性为false,并且在每一个页面请求时绑定数据。这样会减少相应大小约2/3。 对ClientPageDataGrid的最后一个思考是,你大概会设置ClientPageSize为一个偶数,如果你使用AlternatingItemStyle来呈现奇数项来区别于偶数项。第一页将在顶端拥有一个不交互的项。第二页将有一个交互的项,并且其他也这样。这样会导致用户考虑为什么表格格式变来变去,每次都不同于前面的页面。 ![]() DataGrid是ASP.NET中最重要的功能之一,归功于它在重用类中隐含了复杂的构造和非凡的逻辑。ClientPageDataGrid 将给服务器端的控件带来一个适应未来步伐的思想,证明了不仅仅要考虑如何修改嵌入控件来使用,而且还要考虑如何扩展客户端的功能。服务器控件依赖于客户端脚本,工作将更有效率,这是一个非常好的主意。希望所有控件的作者同意这个看法。 有什么问题或建议,请发邮件到:wicked@microsoft.com。 |
![]() Jeff Prosise是MSDN杂志的资深编辑,和一些书的作者,例如《Programming Microsoft .NET》(微软出版社,2002),同时他还是Wintellect网站(http://www.wintellect.com)的共同创办人,该网站是一个专门提供Microsoft .NET的咨询和教育公司。 |
本文出自 MSDN Magazine,2004年2月号。你可以从附近的报摊获得,更好的方法是 订阅。 |
