项目使用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" οnchange="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]
[获取分页数据的存储过程]
-- =============================================
-- 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
[效果图]
以上,只是分页控件的一些思路,控件相对比较粗糙,没有什么技术含量,只是它也有一定程度的实用性,至少在性能上没有什么令人诟病的地方,或许大家会有更好的解决办法,或许大家能在此基础上,写出更方便,封装更好的控件。