`
coconut_zhang
  • 浏览: 529739 次
  • 性别: Icon_minigender_1
  • 来自: 天津
社区版块
存档分类
最新评论

一种清晰而又简单通用的分页算法

阅读更多

分页,是WEB开发中面对的最常见的编程,实现方法多种多样。我也不来评论这些方法的好坏。

但我总感觉它们太复杂,不够清晰不够简单。我十分欣赏PHP中一个Pager.php分页类的算法。http://www.phpclasses.org/browse/file/288.html,作者不详。也在不同的项目中把这种思想转换成不同语言的分页类,你不妨也试试。

这种算法的思路是这样的:

1、把分页后的数据抽象为一个类,你可以把它想象成一个双向链表的一个结点。

结构如下:
 //页类
public class Page
{
    public int pageno { get; set; }//页号
    public int from { get; set; }//前一页号
    public int to { get; set; }//后一页号
    public IList<object> result { get; set; }//数据
    public Page(int page)
    {
        this.pageno = page;
    }
}
2、编写一个Pager类来管理根据你的数据源计算总页码、当前页码的数据、生成用于导航的页码条。
 using System;
using System.Collections.Generic;
using System.Text;

public class Pager
{
    int total_pages = 1;
    int elem_per_page = 10;
    int count_elements = 0;
    IList<object> arr = new List<object>();
    //创建函数
    public Pager(IList<object> arr, int per_page)
    {
        elem_per_page = per_page;
        count_elements = arr.Count;
        if ((this.count_elements % per_page) == 0)
        {
            total_pages = (int) (count_elements / per_page);
        }
        else
        {
            total_pages = (int) (count_elements / per_page) + 1;
        }
        this.arr = arr;
    }

    //计算出一页来
    public Page page(int pageno)
    {
        Page apage = new Page(pageno);
        int from = this.elem_per_page * (pageno - 1) + 1;
        int to = from + this.elem_per_page - 1;
        if (to > count_elements) { to = this.count_elements; }

        List<object> res = new List<object>();
        for (int i = (from - 1); i < to; i++)
        {
            res.Add(this.arr[i]);
        }
        apage.from = from;
        apage.to = to;
        apage.result = res;
        return apage;
    }
    //简单地生成页码
    public string PrintPageNumbers(int cp, string url)
    {
        string pageurl = "<div id=\"paperindex\">";
        if (url.Contains("?")) { url += "&pageno="; } else { url += "?pageno="; }
        for (int i = 1; i < this.total_pages + 1; i++)
        {
            if (i != cp)
            {
                pageurl = pageurl + "[<a href=\"" + url + "" + i + "\">" + i + "</a>]";
            }
            else
            {
                pageurl = pageurl + "<span id=\"current\">[" + i + "]</span>";
            }
        }
        return pageurl + "</div>";
    }
}
简单分析一下,创建函数根据每页显示数据条数计算出总页数,根据你取得的页码调用page(int)来取得那一页的page实例,page实例中包含它的页码,它上一页和下一页的页码,和分页完成后的数据。应用PrintPageNumbers()函数你就可以得到一个用于导航的索引条了。

3、使用方法

 IList<object> list=BLL.News.getAll();//从业务层取数据
Pager pager=new Pager(list,20);//从list取数据进行分页,每页20条.
Page ap=pager.page(Int32.Parse(Request["pageno"]));//以GET方式获取需要显示的页号。

Label1.Text=pager.PrintPageNumbers(Int32.Parse(Request["pageno"]), Request.FilePath.ToString());//向界面上写出导航条

因为ap.result保存着分页后的数据,我们显示数据就有很多选择了。如果你是用table表达那你循环一下拆箱后把每个对象的属性输出一下,如果是用AJAX表达那你把对象列表转换成JSON或XML,传给AJAX页面。OK。分页达成。

4、扩展

如果你需要更漂亮的导航,可以覆写Pager类的PrintPageNumbers方法,我们对它进行扩展非常的容易。比如为实现以下效果。

每次只显示5个页码,根据页码值来判断是否需要加上最前页等链接。如下图。
 


我给Pager类加了以下的生成分页导航的函数。
 #region 后面是可扩展的页码显示方式,我实现了其它两种。

/**

 * 生成前导串

 * 根据当前页号来生成是否有上一页或最前页

 * */

public string get_prestr(int pageindex, string url)
{

    string result = "<div id=\"paperindex\">";

    if (url.Contains("?")) { url += "&pageno="; } else { url += "?pageno="; }

    if (pageindex > 1)
    {
        if (pageindex >= 3)//从第三页起显示 &laquo;  &laquo;上一页
        {
            result += "<a href=\"" + url + "1\">最前页</a>,<a href=\"" + url + (pageindex - 1) + "\">上一页</a>,";
        }
        else//第2页显示 &laquo;上一页
        {
            result += "<a href=" + url + (pageindex - 1) + ">上一页</a>,";
        }
    }
    else
    {
    }
    return result;
}
/**
 * 生成中间数字串
 * 如总记13页当前页为5,每次显示5个页码
 * 则应该返回3,4,5,6,7
 * */
public List<int> get_midpageno(int pageindex, int display_count)
{
    List<int> l = new List<int>();
    int A = display_count / 2;//取中间值
    if (total_pages > display_count)
    {
        if (pageindex <= A)
        {
            for (int i = 1; i < display_count + 1; i++)
            {
                l.Add(i);
            }
        }
        if (pageindex > (total_pages - A))
        {
            for (int i = total_pages - display_count + 1; i < total_pages + 1; i++)
            {
                l.Add(i);
            }
        }
        if ((pageindex > A) && (pageindex <= (total_pages - A)))
        {
            for (int i = pageindex - A; i < pageindex + A + 1; i++)
            {
                l.Add(i);
            }
        }
    }
    else
    {
        for (int i = 1; i < total_pages + 1; i++)
        {
            l.Add(i);
        }
    }

    return l;
}

/**
 * 生成后导串
 * 根据当前页码和总页码来判断是否显示最后页和后一页
 * */
public string get_nextstr(int pageindex, string url)
{
    //与前导串算法类似,所以先计算当前页是倒数第几页
    int toend = total_pages - pageindex + 1;
    string result = "";
    if (url.Contains("?")) { url += "&pageno="; } else { url += "?pageno="; }

    if (toend > 1)
    {
        if (toend >= 3)//从倒数第三页起显示 下一页&raquo; &raquo;
        {
            result += "<a href=\"" + url + (pageindex + 1) + "\">下一页</a>,<a href=\"" + url + total_pages + "\">最后页</a>  ";
        }
        else//倒数第2页显示 下一页&raquo;
        {
            result += "<a href=" + url + (pageindex + 1) + ">下一页</a>";
        }
    }
    else
    {
    }
    return result + "</div>";
}
/*
 * 一种生成固定显示页码数量的页码display_count最好是个奇数,这样可以保证当前页处于分页条的正中间
 * 生成的页码以一个ID为paperindex的div封装,当前页码ID为current,方便加载样式。
 * */
public string PrintPageNumbers(int pageindex, string url, int display_count)
{
    string originurl = url;
    string pageurl = "";
    if (url.Contains("?")) { url += "&pageno="; } else { url += "?pageno="; }
    pageurl += get_prestr(pageindex, originurl);//加入前导串
    //生成中间串
    foreach (int a in get_midpageno(pageindex, display_count))
    {
        if (a.Equals(pageindex))
        {
            pageurl += "<span id=\"current\">" + a + "</span>,";
        }
        else
        {
            pageurl += "<a href=\"" + url + a + "\">" + a + "</a>,";
        }
    }
    pageurl += get_nextstr(pageindex, originurl);//加入后导串
    if (pageurl.EndsWith(",</div>")) { pageurl = pageurl.Replace(",</div>", "</div>"); }
    return pageurl;
}
//显示总记录数和总页数
public string PrintPageNumbers(int pageindex, string url, int display_count, bool todisplaytotalrecorder, bool todisplaytotalpages)
{
    string result = "";
    if (todisplaytotalrecorder) { result += "<span id=\"recordercount\">共有记录:" + this.arr.Count + "条</span>  "; }
    if (todisplaytotalpages) { result += "<span id=\"pagecount\">共记:" + this.total_pages + "页</span>"; }
    return result + PrintPageNumbers(pageindex, url, display_count);
}
#endregion
在调用这个函数生成的导航表达索引时,需要再配合一下简单的样式表。因为我在输出页码时给它加了ID属性,所以可以根据ID加载CSS。

我给它配的是这样的。
 <style type="text/css">

#paperindex{
font:14px #000000;
}
#paperindex #current{
border:1px solid #142A3B;
background-color:B1D3EC;
color:#000;

}
a,a:visited
{
 text-decoration:none;
 color:#000;
 }
a:hover
{
    color:red;
 text-decoration:underline;
}

</style>
总结:

我借鉴PHP中常用的一个分页类paper.php网址[http://www.phpclasses.org/browse/file/288.html]
的分页算法,PHP的作者不详,但分页算法十分清晰有效。


DEMO:

 List<object> list=BLL.Customers.getAll();//从业务层或数据层取出数据列表

Pager pager = new Pager(list, 20);//生成pager,数据从list中取,每页20条

int currentpageindex=Int32.parse(Request["pageno"]);//GET方式获取pageno,pageno是类中定义的页码传参变量

Page page = pager.page(currentpageindex);//

然后就可以通过page.result来取到用于显示的当前页的List<object>数据
再通过Pager的PrintPageNumbers方法来生成页码的字符串。

提示:这是一个干净的类,主要从算法上简捷地实现分页。如果对性能要求较高,可以考虑将分页的数据源进行缓存来达到目的。这些没有考虑在此分页类中。

分享到:
评论
2 楼 xchao 2010-12-21  
要是有JS版本的就好了!
                     :)
1 楼 xchao 2010-12-21  
好文!


:)

相关推荐

    GoodProject Maven Webapp.zip

    验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序。可以防止:...

    asp.net知识库

    .NET20 一种简单的窗口控件UI状态控制方法 翻译MSDN文章 —— 泛型FAQ:最佳实践 Visual C# 3.0 新特性概览 C# 2.0会给我们带来什么 泛型技巧系列:如何提供类型参数之间的转换 C#2.0 - Object Pool 简单实现 ...

    java开源包1

    Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,它功能强大,性能良好,秒杀当前流行的模板引擎。而且还易学易用。 Java的COM桥 JCom JCom (Java-COM Bridge) 可以...

    java开源包11

    Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,它功能强大,性能良好,秒杀当前流行的模板引擎。而且还易学易用。 Java的COM桥 JCom JCom (Java-COM Bridge) 可以...

    java开源包2

    Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,它功能强大,性能良好,秒杀当前流行的模板引擎。而且还易学易用。 Java的COM桥 JCom JCom (Java-COM Bridge) 可以...

    java开源包3

    Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,它功能强大,性能良好,秒杀当前流行的模板引擎。而且还易学易用。 Java的COM桥 JCom JCom (Java-COM Bridge) 可以...

    java开源包6

    Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,它功能强大,性能良好,秒杀当前流行的模板引擎。而且还易学易用。 Java的COM桥 JCom JCom (Java-COM Bridge) 可以...

    java开源包5

    Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,它功能强大,性能良好,秒杀当前流行的模板引擎。而且还易学易用。 Java的COM桥 JCom JCom (Java-COM Bridge) 可以...

    java开源包10

    Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,它功能强大,性能良好,秒杀当前流行的模板引擎。而且还易学易用。 Java的COM桥 JCom JCom (Java-COM Bridge) 可以...

    java开源包4

    Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,它功能强大,性能良好,秒杀当前流行的模板引擎。而且还易学易用。 Java的COM桥 JCom JCom (Java-COM Bridge) 可以...

    java开源包8

    Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,它功能强大,性能良好,秒杀当前流行的模板引擎。而且还易学易用。 Java的COM桥 JCom JCom (Java-COM Bridge) 可以...

    java开源包7

    Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,它功能强大,性能良好,秒杀当前流行的模板引擎。而且还易学易用。 Java的COM桥 JCom JCom (Java-COM Bridge) 可以...

    java开源包9

    Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,它功能强大,性能良好,秒杀当前流行的模板引擎。而且还易学易用。 Java的COM桥 JCom JCom (Java-COM Bridge) 可以...

    java开源包101

    Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,它功能强大,性能良好,秒杀当前流行的模板引擎。而且还易学易用。 Java的COM桥 JCom JCom (Java-COM Bridge) 可以...

    Java资源包01

    Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,它功能强大,性能良好,秒杀当前流行的模板引擎。而且还易学易用。 Java的COM桥 JCom JCom (Java-COM Bridge) 可以...

    JAVA面试题最全集

    2.J2EE是一种技术还是一种平台,他提供了那些技术。 3.什么是Application Server,它有什么功能和优点。 4.简单介绍连接池的优点和原理。 5.Web.xml的作用 四、其他 1.Web安全性的考虑(表单验证、浏览器Basic...

    JAVA上百实例源码以及开源项目源代码

    简单聊天软件CS模式 2个目标文件 一个简单的CS模式的聊天软件,用socket实现,比较简单。 凯撒加密解密程序 1个目标文件 1、程序结构化,用函数分别实现 2、对文件的加密,解密输出到文件 利用随机函数抽取幸运数字 ...

    oracle学习文档 笔记 全面 深刻 详细 通俗易懂 doc word格式 清晰 连接字符串

    1. 层次结构模型: 层次结构模型实质上是一种有根结点的定向有序树,IMS(Information Manage-mentSystem)是其典型代表。 2. 网状结构模型:按照网状数据结构建立的数据库系统称为网状数据库系统,其典型代表是DBTG...

    JAVA上百实例源码以及开源项目

     Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多的网络程序,这是最基础的部分。 递归遍历矩阵 1个目标文件,简单! 多人聊天室 3...

    Windows应用程序捆绑核心编程光盘代码

    12.6.3 一种生成自合并文件的分割软件制作 289 12.7 本章小结 292 参考文献 292 第13章 多线程下载和断点续传 293 13.1 引言 293 13.2 使用FTP进行多线程下载和断点续传 293 13.2.1 FTP协议简介 293 13.2.2 ...

Global site tag (gtag.js) - Google Analytics