阅读文章

Asp.Net 可自定义分页用户控件

[日期:2008-02-25] 来源:  作者: [字体: ]

    
  介绍
  借助 ASP.Net 提供的数据绑定控件,我们无需太多的代码,甚至不需要代码,只要在 VS2005 中拖拽几下控件,进行一些属性的设置,便可以实现在ASP时代需要做大量工作才能够实现的分页功能。但在实际的应用中,尤其是在Web站点程序中,我们经常需要更加丰富的用户界面,而类似DataList或者 GridView 这样的数据控件往往不能或者很难满足我们的要求。此时,我们常常求助于 Repeater 控件,这样我们依旧会面临分页及其显示的问题。
  
  本文不是讲述如何进行数据库分页,而将注意力集中在如何实现可定制地 获取页码、获取路径、显示分页链接,并且通过构建一个用户控件来实现代码重用上。这个用户控件我自己正在使用,如果你是一个初学者,你可以借鉴一下我的实现方式;如果你已经是一位高手,不妨提出设计的不足和改进意见。
  
  控件组成
  为了能迅速提起大家的兴趣,可以先点击这个链接,看看实际的效果:
  
  http://www.tracefact.net/Demo/Pager/Default.ASPx
  
  IUrlManager 接口
  想一想如果你在设计一个可重用的分页用户控件,你面临的问题是什么?每个人获取页码的方式都不同,例如,你的站点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();
   }
  }
  
  设置样式
  控件没有提供任何的样式控制,对于样式,你唯一能做的就是通过它的CSSClass属性来设置由Panel控件生成的Div的Class,然后利用这个Class编写样式表来控制显示。如果有必要,你还可以通过利用PagerIcon这个CSS类来控制“上一页”、“下一页”、“第一页”、“最末页”的显示;通过 CurrentPage 这个CSS类来控制 当前页 的显示。由此,在所有使用这个控件的页面,你都应该包含有控制控件样式的样式表。
  
  这里,我提供了一个默认的实现(在你不设置控件的CSSClass属性的时候,默认为Pager):
  
  .Pager a{
   display:block;
   border:1px solid #ccc;
   float:left;
   padding:4px 5px;
   text-decoration:none;
   text-align:center;
   margin:0 1px;
  }
  
  .Pager a.CurrentPage{
   background:#999;
   color:#eee;
  }
  
  .Pager span{
   position:relative;
   top:6px;
  }
  
  控件的使用
  好了,现在一切都OK了,让我们看看控件如何使用。我们以一种最简单的方式开始,再以一种最复杂的方式开始。
  
  声明式使用
  直接拖拽控件到页面上(比如Default.ASPx),然后设置一下它的RecordCount值就可以了。生成的页面代码如下:
  
  // ... 略
  <%@ Register Src="http://www.aspcool.com/lanmu/UserControl/Pager.ascx" TagName="Pager" TagPrefix="uc1" %>
  // ... 略
  <uc1:Pager id="Pager1" runat="server" RecordCount="337"></uc1:Pager>
  
  动态创建式使用
  我们也可以编写后置代码,然后来动态地使用它:
  
  protected void Page_Load(object sender, EventArgs e)
  {
   if (!IsPostBack) {
   CustomPager pager = (CustomPager)LoadControl("UserControl/Pager.ascx");
   pager.CSSClass = "GreenStyle"; // 设置颜色
   pager.UrlManager = new DefaultUrlManager("P"); // 根据Request.QueryString获取页码的参数
   pager.RecordCount = 132; // 设置记录数
   pager.PageSize = 7; // 设置分页大小
   pager.PreviousPageCount = 3; // 设置当前页之前显示的最大链接数目
   pager.AfterPageCount = 3; // 设置当前也之后可以显示的最大链接数目
  
   // 将控件加入到页面上,pnHolder是一个PlaceHolder 控件
   phHolder.Controls.Add(pager);
   }
  }
  
  注意到这里,由于我们使用了新的CSSClass样式,所以你也需要提供基于GreenStyle的样式表,我是这样提供的:
  
  .GreenStyle a{
   display:block;
   border:1px solid #9cba39;
   float:left;
   padding:4px 5px;
   text-decoration:none;
   text-align:center;
   margin:0 1px;
   color:#9cba39
  }
  
  .GreenStyle a.CurrentPage{
   background:#C5D985;
   color:#fff;
  }
  
  .GreenStyle span{
   position:relative;
   top:6px;
  }
  
  .GreenStyle span b{
   color:#C33;
  }
  
  总结
  本文我们实现了ASP.Net中的一个常见的功能,通过一个用户控件来实现数据分页的页面层以达到代码重用的目的。
  
  我们通过实现 IUrlManager 接口来封装了不同情况下根据URL获取页码的方法。在用户控件中我们实现了主要的逻辑,并调用了实现IUrlManager的类的方法。最后,我们还看了如何通过CSS来控制控件的显示。
  
  希望这篇文章能带给你帮助。
  
    


阅读:
录入:blue1000

评论 】 【 推荐 】 【 打印
上一篇:生成Gif动画缩略图-Gif动画水印的改进
下一篇:[翻译]MVC.创建你自己的IRouteHandler
相关文章      
本文评论
发表评论


点评: 字数
姓名:

  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款