项目使用Asp.net MVC有一段时间了,刚开始的时候,非常不适应,很多在webform中非常习惯的用法和技巧,在这里都做不出来,显得束手束脚,不过写起来倒是找回了一些写jsp代码的感觉。 - -
好吧,闲话不多说,进入正题,讲下我使用的MVC分页控件吧!
首先,我自己在写分页控件之前,找过一些资料,排名比较靠前的,就有以下这篇博客:asp.net mvc强大的分页控件MvcPager,它整理了一些封装得较为完善的分页控件的链接,其中,杨涛的博客的示例列举比较多,杨涛个人主页:www.webdiyer.com。
我研究了他里面列出来的ajax分页控件的示例,并引入我的项目中。
赞美!这个分页控件使用起来非常非常之方便,封装比较完美了,只要将需要引用的动态链接库文件全部引用进来,并且为分页控件指定正确的参数,即可使用,分页控件各按钮的Url定义,页码的计算,以及取记录的操作全部封装好,无须我们另外定义。为了偷懒,我还向我老大强力推荐这款“懒惰”的分页控件,不过因为一个原因,这个分页控件最终在项目中被排除了。
它在设计上有一个令人可恨的特性——它的分页操作在客户端缓存中进行(这也是它使用起来如此简单的一个原因之一)。当数据量庞大时,它的性能将令人难以忍受。于是,它被果断抛弃。
我只得自己写了一个较简陋的分页控件,简陋,意味着你需要为它写更多的代码。几乎没有BUG,而且在设计上,也没有太多的诟病,当然,它也有一个缺憾:当页面包含较多的过滤条件时,需要你针对这些查询条件做一些特别处理。换句话说,由于分页控件的“首页、下一页、上一页、末页、跳转_页”使用超链接的方式传递参数,导致,在查询条件较多的时候,需要你自行拼接查询条件,例如:“王#2010-10-01#1#4”(某些人也许做过同样的事情,应该看得懂这个)。
接下来,介绍一下这个分页控件的原理吧!
1. 这个分页控件完全与展示数据的控件分离,它们之间没有任何耦合,并且它只负责显示一些分页信息(总页数、记录条数、每页记录条数),和与翻页相关的按钮的跳转。
2. 你需要为它写一个action,所有的翻页查询数据、包括第一次进入页面查询数据都通过该action来完成。
3. 需要准备一个获取分页数据的存储过程。
然后,我再列出我的一些代码吧!
[分页控件]
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <style type="text/css"> .Statistics { height: 35px; vertical-align: middle; width: 100%; line-height: 22px; text-align: left; padding-left: 11px; font-family: Tahoma; text-align: right; padding-bottom: 3px; padding-top: 3px; padding-right: 10px; } .StatisticsNumeric { margin-left: 4px; margin-right: 4px; font-weight: Bold; color: #690; font-size: 14px; } .NavigationArea { border-bottom: #ddd 1px solid; text-align: left; padding-bottom: 10px; margin: 5px 3px 5px 0px; padding-left: 3px; padding-right: 3px; font-family: Tahoma; display: block; clear: left; font-size: 14px; padding-top: 10px; } .NavigationArea a { text-decoration: none; padding-bottom: 5px; padding-left: 8px; padding-right: 8px; padding-top: 5px; } .NavigationArea a:hover { border-bottom: #690 2px solid; color: #690; text-decoration: none; } .page_a_NumericButton { color: #000000; } .page_a_disabled { color: #999999; } .page_a_current { border-bottom: #690 2px solid; color: #690; font-weight: 700; } .button_go { border: none; background: #FFFFFF; font-family: Tahoma; border: solid 1px #CCCCCC; } .textbox_pageIndex { font-family: Tahoma; width: 45px; border: solid 1px #CCCCCC; margin-left: 5px; margin-right: 5px; } .page_span_text { font-family: Tahoma; font-size: 13px; } .textNumber { font-weight: Bold; color: #690; font-size: 12px; } .currentSpan { border: solid 1px #f34500; color: #fff; background: #f34500; padding: 2px 5px; font-weight: bold; margin: 2px; } .Span { border: #aaaadd 1px solid; padding: 2px 5px; text-decoration: none; color: #000; cursor: pointer; } </style> <script type="text/javascript"> function setPageSize() { var pz = $('#pageSizeList').val(); var url = window.location.href; if (url.toUpperCase().indexOf('PAGESIZE=') < 0 || url.toUpperCase().indexOf('PAGEINDEX=') < 0) { window.location.href = url + '?PageSize=' + pz + '&PageIndex=1'; return; } // “PageSize=”后面的部分 var url_part = url.substring(url.toUpperCase().indexOf('PAGESIZE=') + 9, url.length); var ps = getNumber(url_part); url = url.replace("PageSize=" + ps, "PageSize=" + pz); window.location.href = url; } function setPageIndex(pageIndex) { var url = window.location.href; var pz = $('#pageSizeList').val(); if (url.toUpperCase().indexOf('PAGESIZE=') < 0 || url.toUpperCase().indexOf('PAGEINDEX=') < 0) { window.location.href = url + '?PageSize=' + pz +'&PageIndex=' + pageIndex; return; } var url_page = url.substring(url.toUpperCase().indexOf('PAGEINDEX=') + 10, url.length); var pi = getNumber(url_page); url = url.replace("PageIndex=" + pi, "PageIndex=" + pageIndex); window.location.href = url; } function gotoPage(pageCount) { var gotopageindex = $('#gotoPage').val(); if (Number(gotopageindex) > Number(pageCount)) { gotopageindex = pageCount; } else if (Number(gotopageindex) < 1) { gotopageindex = '1'; } var pz = $('#pageSizeList').val(); var url = window.location.href; var url_part = url.substring(url.toUpperCase().indexOf('PAGESIZE=') + 9, url.length); var ps = getNumber(url_part); url = url.replace("PageSize=" + ps, "PageSize=" + pz); url_page = url.substring(url.toUpperCase().indexOf('PAGEINDEX=') + 10, url.length); var pi = getNumber(url_page); url = url.replace("PageIndex=" + pi, "PageIndex=" + gotopageindex); window.location.href = url; } function getNumber(part) { var str = "0123456789"; var rst = ''; for (var i = 0; i < part.length; i++) { if (str.indexOf(part.charAt(i)) < 0) break; rst += part.charAt(i); } return rst; } </script> <% var data = ViewData["PageData"]; if (data != null) { int pageIndex = Convert.ToInt32(ViewData["PageIndex"] ?? "0"); // 页码 int pageSize = Convert.ToInt32(ViewData["PageSize"] ?? "0"); // 每页显示记录条数 int pageCount = Convert.ToInt32(ViewData["PageCount"] ?? "0"); // 总页数 int recordCount = Convert.ToInt32(ViewData["RecordCount"] ?? "0"); // 总记录数 int lastPageIndex = (pageIndex - 1 < 1) ? 1 : pageIndex - 1; int nextPageIndex = (pageIndex + 1 > pageCount) ? pageCount : pageIndex + 1; %> <table class="Statistics"> <tr> <td align="left" style="padding-left: 20px" id="perPage"> 每页显示数量<select id="pageSizeList" class="PzSelect" onchange="setPageSize()"> <option value="10">10</option> <option value="20">20</option> <option value="30">30</option> </select> <input type="hidden" id="fPageSize" value="<%= pageSize %>" /> <input type="hidden" id="fPageIndex" value="<%= pageIndex %>" /> </td> <td align="right"> 共<span class="StatisticsNumeric"><%= recordCount %></span>条记录 当前页:<span class="StatisticsNumeric"><%= pageCount==0?0:pageIndex%></span>/<span class="StatisticsNumeric"><%= pageCount%></span> <% if (pageIndex == 1) { if (pageCount == 1 || pageCount == 0) { %> <a style="color: #999999">首页</a> <a style="color: #999999">上一页</a> <a style="color: #999999"> 下一页</a> <a style="color: #999999">末页</a> <% } else { %> <a style="color: #999999">首页</a> <a style="color: #999999">上一页</a> <a href="javascript:setPageIndex('<%= nextPageIndex %>');"> 下一页</a> <a href="javascript:setPageIndex('<%= pageCount %>');"> 末页</a> <% } } else if (pageIndex == pageCount) { %> <a href="javascript:setPageIndex('1');"> 首页</a> <a href="javascript:setPageIndex('<%= lastPageIndex %>');"> 上一页</a> <a style="color: #999999">下一页</a> <a style="color: #999999">末页</a> <% } else { %> <a href="javascript:setPageIndex('1');"> 首页</a> <a href="javascript:setPageIndex('<%= lastPageIndex %>');"> 上一页</a> <a href="javascript:setPageIndex('<%= nextPageIndex %>');"> 下一页</a> <a href="javascript:setPageIndex('<%= pageCount %>');"> 末页</a> <% } %> 转到第 <input type="text" class="textNumber" style="width: 30px" id="gotoPage" /> 页<a href="javascript:gotoPage('<%= pageCount%>');">跳转</a> </td> </tr> </table> <script type="text/javascript"> $(function () { // 设置分页Size $("#pageSizeList").val('<%= pageSize %>'); }); </script> <% } %>
[PageEn.cs]
using System; using System.Collections.Generic; using System.Collections; namespace ATA.Toeic.Models { [Serializable] public class PageEn { private int? _pageIndex; // 页码 private int? _pageSize; // 每页记录条数 private int? _recordCount; // 总记录条数 private int? _pageCount; // 页数 private string _condition; // 条件 private int? _goToPageIndex; // 跳转到 private bool _isShow; // 是否显示 private IList _pagedata; // 分页数据 private string _pageurl; // 翻页链接 public string PageURL { set { _pageurl = value; } get { return _pageurl; } } public IList PageData { set { _pagedata = value; } get { return _pagedata; } } public int? PageIndex { set { _pageIndex = value; } get { return _pageIndex; } } public int? PageSize { set { _pageSize = value; } get { return _pageSize; } } public int? RecordCount { set { _recordCount = value; } get { return _recordCount; } } public int? PageCount { set { _pageCount = value; } get { return _pageCount; } } public int? GoToPageIndex { set { _goToPageIndex = value; } get { return _goToPageIndex; } } public string Condition { set { _condition = value; } get { return _condition; } } public bool IsShow { set { _isShow = value; } get { return _isShow; } } } }
[分页action]
/// <summary> /// 获取分页数据 /// </summary> /// <param name="PageIndex">页码</param> /// <param name="Condition">查询条件拼接串</param> /// <returns></returns> public ActionResult ViewExamServiceList(int? PageIndex, string Condition) { // 获取分页数据的方法,页码不提供时,默认为第一页,Condition条件可以在数据访问层,拼接成查询条件,PAGE_SIZE可以在web.config中配置 PageEn pen = new ExamServiceBLL().GetExamServiceByPage(PageIndex ?? 1, PAGE_SIZE, this.UserId, Condition); pen.Condition = Condition; pen.PageURL = Url.Action("ViewExamServiceList", "ExamService"); // ExamServiceList这个视图包含MVCPage分页控件和展示数据的控件 return View("ExamServiceList", pen); }
[ViewExamServiceList.aspx]
<%Html.RenderPartial("ExamServicePage", Model); %><%Html.RenderPartial("MvcPage", Model); %>
[获取分页数据的存储过程]
-- ============================================= -- Author: -- Create date: -- Description: 分页SP -- ============================================= CREATE procedure [dbo].[SP_GetPage] @tblName varchar(1000), -- 表名 @strGetFields varchar(1000) = '*', -- 需要返回的列 @strOrder varchar(100)='', -- 排序的字段名 @PageSize int = 10, -- 页尺寸 @PageIndex int = 1, -- 页码 @strWhere varchar(1500) = '', -- 查询条件 (注意: 不要加 where) @sort varchar(5) ='asc', @RowCount int output AS begin declare @strSQL varchar(5000) declare @DynamicSQL nvarchar(4000) if @strWhere !='' begin set @strWhere=' where '+@strWhere end SET @DynamicSQL = N'select @RowCount = count(*) from '+@tblName+@strWhere exec sp_executesql @DynamicSQL, N'@RowCount int OUTPUT',@RowCount OUTPUT set @strSQL='SELECT * FROM (SELECT ROW_NUMBER() OVER (Order By '+@strOrder+' '+@sort+') AS pos,'+@strGetFields+' FROM '+@tblName+@strWhere+') AS sp WHERE pos BETWEEN '+str((@PageIndex-1)*@PageSize+1)+' AND '+str(@PageIndex*@PageSize) exec (@strSQL) end go
为此,你需要在DAL里面将存储过程的参数拼接好,举一个简单的调用YX_ShowPage的示例,就知道这个过程要怎么用了:
declare @recordCount int
exec YX_ShowPage 'V_YX_ExamService ', ' * ', 'fServiceId ', 10, 1, '', 'desc', @recordCount output
[效果图]
以上,只是分页控件的一些思路,控件相对比较粗糙,没有什么技术含量,只是它也有一定程度的实用性,至少在性能上没有什么令人诟病的地方,或许大家会有更好的解决办法,或许大家能在此基础上,写出更方便,封装更好的控件。