文/JimmyZhang 出处/博客园
介绍借助 Asp.Net 提供的数据绑定控件,我们无需太多的代码,甚至不需要代码,只要在 VS2005 中拖拽几下控件,进行一些属性的设置,便可以实现在Asp时代需要做大量工作才能够实现的分页功能。但在实际的应用中,尤其是在Web站点程序中,我们经常需要更加丰富的用户界面,而类似DataList或者 GridView 这样的数据控件往往不能或者很难满足我们的要求。此时,我们常常求助于 Repeater 控件,这样我们依旧会面临分页及其显示的问题。
本文不是讲述如何进行数据库分页,而将注意力集中在如何实现可定制地 获取页码、获取路径、显示分页链接,并且通过构建一个用户控件来实现代码重用上。这个用户控件我自己正在使用,如果你是一个初学者,你可以借鉴一下我的实现方式;如果你已经是一位高手,不妨提出设计的不足和改进意见。
控件组成为了能迅速提起大家的兴趣,可以先点击这个链接,看看实际的效果:
http://www.tracefact.net/Demo/Pager/Default.aspxIUrlManager 接口想一想如果你在设计一个可重用的分页用户控件,你面临的问题是什么?每个人获取页码的方式都不同,例如,你的站点URL可能是类似这样的 Default.aspx?page=1 ,而另外一个站点的URL 是这样的 Default.aspx?p=1。更有一些可能根本不使用 QueryString 来获取页码,它们的URL可能是这样的 Default-1.aspx、Default-2.aspx 等等。获取页码的方式不同,根据页码产生链接地址的方法自然也不相同。按照封装变化的思想,我们应该将这变化的部分取出来,建一个 IUrlManager 接口:
public interface IUrlManager
{
int GetCurrentPageIndex(); // 获取 当前页面 的页码
string GetPageUrl(int pageIndex); // 根据 页码 获取页面路径
}
DefaultUrlManager 类现在 所有获取页码 及 根据页码获取路径 的逻辑都可以放在实现了这个接口的类中。如果你想使用这个控件,你需要提供一个实现了IUrlManger接口的类。为了使控件立即可用,我在这里提供了一个默认实现,我管它叫做 DefaultUrlManger。它通过Request.QueryString获取页码,并默认以"Page"作为参数。
public class DefaultUrlManager : IUrlManager {
private HttpContext context;
private Regex reg;
private string queryParam;
public DefaultUrlManager(string queryParam) {
this.queryParam = queryParam;
context = HttpContext.Current;
string pattern = @"(?<r1>[?&]" + queryParam + @"=)[^&]+";
reg = new Regex(pattern, RegexOptions.IgnoreCase);
}
public DefaultUrlManager() : this("Page") { }
public string GetPageUrl(int pageIndex) {
string pageUrl = context.Request.RawUrl;
// 如果找到匹配,也就是URL中含有类似 ?page=3 或者 &page=4 这样的字符串
// 则对后面的数值进行替换
if (reg.IsMatch(pageUrl)) {
pageUrl = reg.Replace(pageUrl, "${r1}" + pageIndex.ToString());
} else {
string queryString = context.Request.Url.Query;
if (string.IsNullOrEmpty(queryString))
pageUrl += "?" + queryParam + "=" + pageIndex.ToString();
else
pageUrl += "&" + queryParam + "=" + pageIndex.ToString();
}
return pageUrl;
}
public int GetCurrentPageIndex() {
string queryIndex = context.Request.QueryString[queryParam];
if (string.IsNullOrEmpty(queryIndex))
return 1;
else {
int pageIndex;
try {
pageIndex = Math.Abs(int.Parse(queryIndex));
} catch {
pageIndex = 1;
}
return pageIndex;
}
}
}
Pager.ascx 文件由于我们的链接是动态生成的,所以我们大部分的代码都会存在于 Pager.ascx.cs 中,在Pager.ascx 文件中,我们只提供一个装链接的容器:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Pager.ascx.cs" Inherits="CustomPager" %>
<asp:Panel ID="pnPager" runat="server" CssClass="Pager"></asp:Panel>
Pager.ascx.cs 文件我们对于页面的显示的主要实现都放置在了这个文件中,我在文件中做了大量注释,这里就不再文字描述了:
using System;
// ... 略
public partial class CustomPager : System.Web.UI.UserControl
{
private int pageSize = 10; // 分页大小
private int recordCount; // 记录总数
private int pageCount; // 总页数
private int currentPage; // 当前页的页码
private int previousPageCount = 5; // 当前页之前可以显示的最多条目数,大于此条目的将被隐藏
private int afterPageCount = 4; // 当前页之后可以显示的最多条目数,大于此条目的将被隐藏
private bool showPrevious = false; // 是否显示 上一页、第一页 的链接
private bool showNext = false; // 是否显示 下一页、最末页 的链接
private int startPage; // 显示的第一页 的 页码
private int endPage; // 显示的最末页 的 页码
private IUrlManager urlManager; // 设置接口
public int RecordCount
{
get { return recordCount; }
set { recordCount = value; }
}
public int PageSize
{
get { return pageSize; }
set { pageSize = value; }
}
public int PreviousPageCount
{
get { return previousPageCount; }
set { previousPageCount = value; }
}
public int AfterPageCount
{
get { return afterPageCount; }
set { afterPageCount = value; }
}
public IUrlManager UrlManager
{
get { return urlManager; }
set { urlManager = value; }
}
// 设置 pnPager Div 的 CssClass 属性,通过它来为控件定义样式
public string CssClass {
set { pnPager.CssClass = value; }
get { return pnPager.CssClass; }
}
private void RenderPager()
{
if (urlManager == null)
{
urlManager = new DefaultUrlManager();
}
// 获取页数
double recordCount2 = Convert.ToDouble(recordCount);
double pageSize2 = Convert.ToDouble(pageSize);
pageCount = Convert.ToInt32(Math.Ceiling(recordCount2 / pageSize2));
// 获取当前页
currentPage = urlManager.GetCurrentPageIndex();
if (currentPage < 0)
currentPage = 1;
else if (currentPage > pageCount)
currentPage = pageCount;
SetStartPage();
SetEndPage();
HyperLink link;
// 循环打印链接
for (int i = startPage; i <= endPage; i++)
{
if (showPrevious) // 如果需要显示前一页、第一页链接
AddPreviousLink(urlManager);
link = new HyperLink();
if (i == currentPage)
link.CssClass = "CurrentPage";
link.Text = i.ToString();
link.NavigateUrl = urlManager.GetPageUrl(i);
pnPager.Controls.Add(link);
if (i == endPage && showNext) // 如果需要显示 下一页、最末页 链接
AddNextLink(urlManager);
}
Label lbCount = new Label();
lbCount.Text = String.Format(" ( 第<b>{0}</b>页/共<b>{1}</b>页 )", currentPage, pageCount);
pnPager.Controls.Add(lbCount);
}
// 添加“第一页”,“上一页”的连接
private void AddPreviousLink(IUrlManager urlManager)
{
HyperLink first = new HyperLink();
first.CssClass = "PagerIcon";
first.Text = "<<";
first.ToolTip = "第一页";
first.NavigateUrl = urlManager.GetPageUrl(1);
pnPager.Controls.Add(first);
HyperLink previous = new HyperLink();
previous.CssClass = "PagerIcon";
previous.Text = "<";
previous.ToolTip = "上一页";
previous.NavigateUrl = urlManager.GetPageUrl(currentPage - 1);
pnPager.Controls.Add(previous);
showPrevious = false; // 只显示一次
}
// 添加 “下一页”、“最末页” 的链接
private void AddNextLink(IUrlManager urlManager)
{
HyperLink next = new HyperLink();
next.CssClass = "PagerIcon";
next.Text = ">";
next.ToolTip = "下一页";
next.NavigateUrl = urlManager.GetPageUrl(currentPage + 1);
pnPager.Controls.Add(next);
HyperLink last = new HyperLink();
last.CssClass = "PagerIcon";
last.Text = ">>";
last.ToolTip = "最末页";
last.NavigateUrl = urlManager.GetPageUrl(pageCount);
pnPager.Controls.Add(last);
showNext = false; // 可有可无,程序会跳出循环
}
// 根据当前页,当前页之前可以显示的页数,算得从第几页开始进行显示
private void SetStartPage()
{
// 如果当前页小于 它前面所可以显示的条目数,那么显示第一页就是实际的第一页
if (currentPage <= previousPageCount)
{
startPage = 1;
} else // 这种情况下 currentPage 前面总是能显示完,要根据后面的长短确定是不是前面应该多显示
{
if(currentPage > previousPageCount+1)
showPrevious = true;
int linkLength = (pageCount - currentPage + 1) + previousPageCount;
int startPage = currentPage - previousPageCount;
while (linkLength < previousPageCount + afterPageCount + 1 && startPage > 1)
{
linkLength++;
startPage--;
}
this.startPage = startPage;
}
}
// 根据CurrentPage、总页数、当前页之后长度 算得显示的最末页是 第几页
private void SetEndPage()
{
// 如果当前页加上它之后可以显示的页数 大于 总页数,那么显示的最末页就是实际的最末页
if (currentPage + afterPageCount >= pageCount)
{
endPage = pageCount;
} else // 这种情况下 currentPage后面的总是可以显示完,要根据前面的长短确定是不是后面应该多显示
{
int linkLength = (currentPage - startPage + 1) + afterPageCount;
int endPage = currentPage + afterPageCount;
while (linkLength < previousPageCount + afterPageCount + 1 && endPage < pageCount)
{
linkLength++;
endPage++;
}
if (endPage < pageCount)
showNext = true;
this.endPage = endPage;
}
}
protected void Page_Load(object sender, EventArgs e)
{
RenderPager();
}
}
| 友情提示:此文并不表示本站肯定持有相同观点,转载请注明出处。 |