分页,是WEB开发中面对的最常见的编程,实现方法多种多样。我也不来评论这些方法的好坏。
但我总感觉它们太复杂,不够清晰不够简单。我十分欣赏PHP中一个Pager.php分页类的算法。http://www.phpclasses.org/browse/file/288.html,作者不详。也在不同的项目中把这种思想转换成不同语言的分页类,你不妨也试试。
这种算法的思路是这样的:
1、把分页后的数据抽象为一个类,你可以把它想象成一个双向链表的一个结点。
结构如下:
1
//
页类
2
public
class
Page
3
{
4
public
int
pageno {
get
;
set
; }
//
页号
5
public
int
from {
get
;
set
; }
//
前一页号
6
public
int
to {
get
;
set
; }
//
后一页号
7
public
IList
<
object
>
result {
get
;
set
; }
//
数据
8
public
Page(
int
page)
9
{
10
this
.pageno
=
page;
11
}
12
13
}
2、编写一个Pager类来管理根据你的数据源计算总页码、当前页码的数据、生成用于导航的页码条。
1
using
System;
2
using
System.Collections.Generic;
3
using
System.Text;
4
5
public
class
Pager
6
{
7
int
total_pages
=
1
;
8
int
elem_per_page
=
10
;
9
int
count_elements
=
0
;
10
IList
<
object
>
arr
=
new
List
<
object
>
();
11
//
创建函数
12
public
Pager(IList
<
object
>
arr,
int
per_page)
13
{
14
elem_per_page
=
per_page;
15
count_elements
=
arr.Count;
16
if
((
this
.count_elements
%
per_page)
==
0
)
17
{
18
total_pages
=
(
int
)(count_elements
/
per_page);
19
}
20
else
21
{
22
total_pages
=
(
int
)(count_elements
/
per_page)
+
1
;
23
}
24
this
.arr
=
arr;
25
}
26
27
//
计算出一页来
28
public
Page page(
int
pageno)
29
{
30
Page apage
=
new
Page(pageno);
31
int
from
=
this
.elem_per_page
*
(pageno
-
1
)
+
1
;
32
int
to
=
from
+
this
.elem_per_page
-
1
;
33
if
(to
>
count_elements) { to
=
this
.count_elements; }
34
35
List
<
object
>
res
=
new
List
<
object
>
();
36
for
(
int
i
=
(from
-
1
); i
<
to; i
++
)
37
{
38
res.Add(
this
.arr[i]);
39
}
40
apage.from
=
from;
41
apage.to
=
to;
42
apage.result
=
res;
43
return
apage;
44
}
45
//
简单地生成页码
46
public
string
PrintPageNumbers(
int
cp,
string
url)
47
{
48
string
pageurl
=
"
<div id="
"
paperindex"
"
>
"
;
49
if
(url.Contains(
"
?
"
)) { url
+=
"
&pageno=
"
; }
else
{ url
+=
"
?pageno=
"
; }
50
for
(
int
i
=
1
; i
<
this
.total_pages
+
1
; i
++
)
51
{
52
if
(i
!=
cp)
53
{
54
pageurl
=
pageurl
+
"
[<a href="
""
+ url +
""
+ i +
"
"
"
>
"
+
i
+
"
</a>]
"
;
55
}
56
else
57
{
58
pageurl
=
pageurl
+
"
<span id="
"
current"
"
>[
"
+
i
+
"
]</span>
"
;
59
60
}
61
}
62
return
pageurl
+
"
</div>
"
;
63
}
64
}
简单分析一下,创建函数根据每页显示数据条数计算出总页数,根据你取得的页码调用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类加了以下的生成分页导航的函数。
1
#region
后面是可扩展的页码显示方式,我实现了其它两种。
2
/*
*
3
* 生成前导串
4
* 根据当前页号来生成是否有上一页或最前页
5
*
*/
6
public
string
get_prestr(
int
pageindex,
string
url)
7
{
8
string
result
=
"
<div id="
"
paperindex"
"
>
"
;
9
if
(url.Contains(
"
?
"
)) { url
+=
"
&pageno=
"
; }
else
{ url
+=
"
?pageno=
"
; }
10
11
if
(pageindex
>
1
)
12
{
13
14
if
(pageindex
>=
3
)
//
从第三页起显示 « «上一页
15
{
16
result
+=
"
<a href="
""
+ url +
"
1
"
"
>最前页</a>,<a href="
""
+ url + (pageindex - 1) +
"
"
"
>上一页</a>,
"
;
17
}
18
else
//
第2页显示 «上一页
19
{
20
result
+=
"
<a href=
"
+
url
+
(pageindex
-
1
)
+
"
>上一页</a>,
"
;
21
}
22
23
}
24
else
25
{
26
27
}
28
return
result;
29
}
30
/*
*
31
* 生成中间数字串
32
* 如总记13页当前页为5,每次显示5个页码
33
* 则应该返回3,4,5,6,7
34
*
*/
35
public
List
<
int
>
get_midpageno(
int
pageindex,
int
display_count)
36
{
37
List
<
int
>
l
=
new
List
<
int
>
();
38
int
A
=
display_count
/
2
;
//
取中间值
39
if
(total_pages
>
display_count)
40
{
41
if
(pageindex
<=
A)
42
{
43
for
(
int
i
=
1
; i
<
display_count
+
1
; i
++
)
44
{
45
l.Add(i);
46
}
47
}
48
if
(pageindex
>
(total_pages
-
A))
49
{
50
for
(
int
i
=
total_pages
-
display_count
+
1
; i
<
total_pages
+
1
; i
++
)
51
{
52
l.Add(i);
53
}
54
}
55
if
((pageindex
>
A)
&&
(pageindex
<=
(total_pages
-
A)))
56
{
57
for
(
int
i
=
pageindex
-
A; i
<
pageindex
+
A
+
1
; i
++
)
58
{
59
l.Add(i);
60
}
61
}
62
}
63
else
64
{
65
for
(
int
i
=
1
; i
<
total_pages
+
1
; i
++
)
66
{
67
l.Add(i);
68
}
69
70
}
71
72
return
l;
73
}
74
75
/*
*
76
* 生成后导串
77
* 根据当前页码和总页码来判断是否显示最后页和后一页
78
*
*/
79
public
string
get_nextstr(
int
pageindex,
string
url)
80
{
81
//
与前导串算法类似,所以先计算当前页是倒数第几页
82
int
toend
=
total_pages
-
pageindex
+
1
;
83
string
result
=
""
;
84
if
(url.Contains(
"
?
"
)) { url
+=
"
&pageno=
"
; }
else
{ url
+=
"
?pageno=
"
; }
85
86
if
(toend
>
1
)
87
{
88
89
if
(toend
>=
3
)
//
从倒数第三页起显示 下一页» »
90
{
91
result
+=
"
<a href="
""
+ url + (pageindex + 1) +
"
"
"
>下一页</a>,<a href="
""
+ url + total_pages +
"
"
"
>最后页</a>
"
;
92
}
93
else
//
倒数第2页显示 下一页»
94
{
95
result
+=
"
<a href=
"
+
url
+
(pageindex
+
1
)
+
"
>下一页</a>
"
;
96
}
97
98
}
99
else
100
{
101
102
}
103
return
result
+
"
</div>
"
;
104
}
105
/*
106
* 一种生成固定显示页码数量的页码display_count最好是个奇数,这样可以保证当前页处于分页条的正中间
107
* 生成的页码以一个ID为paperindex的div封装,当前页码ID为current,方便加载样式。
108
*
*/
109
public
string
PrintPageNumbers(
int
pageindex,
string
url,
int
display_count)
110
{
111
112
string
originurl
=
url;
113
string
pageurl
=
""
;
114
if
(url.Contains(
"
?
"
)) { url
+=
"
&pageno=
"
; }
else
{ url
+=
"
?pageno=
"
; }
115
pageurl
+=
get_prestr(pageindex, originurl);
//
加入前导串
116
//
生成中间串
117
foreach
(
int
a
in
get_midpageno(pageindex, display_count))
118
{
119
if
(a.Equals(pageindex))
120
{
121
pageurl
+=
"
<span id="
"
current"
"
>
"
+
a
+
"
</span>,
"
;
122
}
123
else
124
{
125
pageurl
+=
"
<a href="
""
+ url + a +
"
"
"
>
"
+
a
+
"
</a>,
"
;
126
}
127
}
128
pageurl
+=
get_nextstr(pageindex, originurl);
//
加入后导串
129
if
(pageurl.EndsWith(
"
,</div>
"
)) { pageurl
=
pageurl.Replace(
"
,</div>
"
,
"
</div>
"
); }
130
return
pageurl;
131
}
132
//
显示总记录数和总页数
133
public
string
PrintPageNumbers(
int
pageindex,
string
url,
int
display_count,
bool
todisplaytotalrecorder,
bool
todisplaytotalpages)
134
{
135
string
result
=
""
;
136
if
(todisplaytotalrecorder) { result
+=
"
<span id="
"
recordercount"
"
>共有记录:
"
+
this
.arr.Count
+
"
条</span>
"
; }
137
if
(todisplaytotalpages) { result
+=
"
<span id="
"
pagecount"
"
>共记:
"
+
this
.total_pages
+
"
页</span>
"
; }
138
return
result
+
PrintPageNumbers(pageindex, url, display_count);
139
}
140
#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方法来生成页码的字符串。
提示:这是一个干净的类,主要从算法上简捷地实现分页。如果对性能要求较高,可以考虑将分页的数据源进行缓存来达到目的。这些没有考虑在此分页类中。
提示:可以根据生成页码后的DIV的ID来为其加载样式表.