Custom ASP.NET Data Pager Control

I like many others have been frustrated by the limitations of the DataPager control introducted in v3.5 of the .NET framework. The main problem is that it is tied in with the DataSource controls.  Whilst i did manage to come up with a solution using the OnPagePropertiesChanging event of the ListView i still couldn’t find a solution to display my pager without using LINQ to SQL or without the Object/Linq DataSource control or without setting the DataSource to the entire record set which is very inefficient. Therefore I set about coming up with my own solution and have come up with:

namespace Flixon.Web.UI.WebControls
{
  public class PagerCommandEventArgs : CommandEventArgs
  {
    public int StartRowIndex { get; set; }
    public int MaximumRows { get; set; }

    public PagerCommandEventArgs(int startRowIndex, int maximumRows, CommandEventArgs originalArgs) : base(originalArgs)
    {
      this.StartRowIndex = startRowIndex;
      this.MaximumRows = maximumRows;
    }
  }

  public class Pager : WebControl, INamingContainer
  {
    public new int Page
    {
      get
      {
        if (!string.IsNullOrEmpty(HttpContext.Current.Request.QueryString[this.QueryStringField]))
          return Convert.ToInt32(HttpContext.Current.Request.QueryString[this.QueryStringField]);
        else
          return (int)(this.ViewState["Page"] ?? 1);
      }
      set { this.ViewState["Page"] = value; }
    }

    public int PageSize
    {
      get { return (int)(this.ViewState["PageSize"] ?? 15); }
      set { this.ViewState["PageSize"] = value; }
    }

    public int TotalRowCount
    {
      get { return (int)(this.ViewState["TotalRowCount"] ?? 0); }
      set { this.ViewState["TotalRowCount"] = value; }
    }

    public int StartRowIndex
    {
      get { return (this.Page - 1) * this.PageSize; }
    }

    public int TotalPageCount
    {
      get { return (int)Math.Ceiling((double)this.TotalRowCount / (double)this.PageSize); }
    }

    public string Url
    {
      get { return (string)(this.ViewState["Url"] ?? string.Empty); }
      set { this.ViewState["Url"] = value; }
    }

    public string QueryStringField
    {
      get { return (string)(this.ViewState["QueryStringField"] ?? string.Empty); }
      set { this.ViewState["QueryStringField"] = value; }
    }

    public bool ShowPreviousNextLinks
    {
      get { return (bool)(this.ViewState["ShowPreviousNextLinks"] ?? true); }
      set { this.ViewState["ShowPreviousNextLinks"] = value; }
    }

    private static readonly object EventPagerCommand = new object();
    public event EventHandler PagerCommand
    {
      add
      {
        this.Events.AddHandler(EventPagerCommand, value);
      }
      remove
      {
        this.Events.RemoveHandler(EventPagerCommand, value);
      }
    }

    protected void OnPagerCommand(PagerCommandEventArgs e)
    {
      EventHandler handler = (EventHandler)base.Events[EventPagerCommand];

      if (handler != null)
        handler(this, e);
    }

    protected override void CreateChildControls()
    {
      if (!string.IsNullOrEmpty(this.QueryStringField))
        this.CreatePageForQueryString();
      else
        this.CreatePagerForCommand();
    }

    private void CreatePageForQueryString()
    {
      if (this.TotalRowCount > this.PageSize)
        this.Controls.Add(new LiteralControl("Page:"));

      // Add the previous links
      if (this.Page > 1 && this.ShowPreviousNextLinks)
      {
        this.Controls.Add(new LiteralControl(" "));
        this.Controls.Add(this.CreateLink("«", 1));
        this.Controls.Add(new LiteralControl(" "));
        this.Controls.Add(this.CreateLink("‹", this.Page - 1));
      }

      // Loop through the pages
      if (this.TotalRowCount > this.PageSize)
      {
        for (int i = (this.Page <= 2 ? 1 : this.Page - 2); i <= (this.Page >= this.TotalPageCount - 2 ? this.TotalPageCount : this.Page + 2); i++)
        {
          if (this.Page == i)
            this.Controls.Add(new LiteralControl(" " + i.ToString() + ""));
          else
          {
            this.Controls.Add(new LiteralControl(" "));
            this.Controls.Add(this.CreateLink(i.ToString(), i));
          }
        }

        // Add a link to the last page
        if (this.Page < this.TotalPageCount - 2)
        {
          this.Controls.Add(new LiteralControl(" ... "));
          this.Controls.Add(this.CreateLink(this.TotalPageCount.ToString(), this.TotalPageCount));
        }
      }

      // Add the next links
      if (this.Page < this.TotalPageCount && this.ShowPreviousNextLinks)
      {
        this.Controls.Add(new LiteralControl(" "));
        this.Controls.Add(this.CreateLink("›", this.Page + 1));
        this.Controls.Add(new LiteralControl(" "));
        this.Controls.Add(this.CreateLink("»", this.TotalPageCount));
      }
    }

    private void CreatePagerForCommand()
    {
      if (this.TotalRowCount > this.PageSize)
        this.Controls.Add(new LiteralControl("Page:"));

      // Add the previous links
      if (this.Page > 1 && this.ShowPreviousNextLinks)
      {
        this.Controls.Add(new LiteralControl(" "));
        this.Controls.Add(this.CreateCommand("First", "«", 1));
        this.Controls.Add(new LiteralControl(" "));
        this.Controls.Add(this.CreateCommand("Previous", "‹", this.Page - 1));
      }

      // Loop through the pages
      if (this.TotalRowCount > this.PageSize)
      {
        for (int i = (this.Page <= 2 ? 1 : this.Page - 2); i <= (this.Page >= this.TotalPageCount - 2 ? this.TotalPageCount : this.Page + 2); i++)
        {
          if (this.Page == i)
            this.Controls.Add(new LiteralControl(" " + i.ToString() + ""));
          else
          {
            this.Controls.Add(new LiteralControl(" "));
            this.Controls.Add(this.CreateCommand("PageChange", i.ToString(), i));
          }
        }

        // Add a link to the last page
        if (this.Page < this.TotalPageCount - 2)
        {
          this.Controls.Add(new LiteralControl(" ... "));
          this.Controls.Add(this.CreateCommand("Last", this.TotalPageCount.ToString(), this.TotalPageCount));
        }
      }

      // Add the next links
      if (this.Page < this.TotalPageCount && this.ShowPreviousNextLinks)
      {
        this.Controls.Add(new LiteralControl(" "));
        this.Controls.Add(this.CreateCommand("Next", "›", this.Page + 1));
        this.Controls.Add(new LiteralControl(" "));
        this.Controls.Add(this.CreateCommand("Last", "»", this.TotalPageCount));
      }
    }

    private HyperLink CreateLink(string text, int page)
    {
      HyperLink control = new HyperLink();
      control.Text = text;
      control.NavigateUrl = string.Format(this.Url, page);
      return control;
    }

    private LinkButton CreateCommand(string commandName, string text, int page)
    {
      LinkButton control = new LinkButton();
      control.Text = text;
      control.CommandName = commandName;
      control.CommandArgument = page.ToString();
      control.Command += new CommandEventHandler(HandleEvent);
      return control;
    }

    private void HandleEvent(Object sender, CommandEventArgs e)
    {
      this.Page = Convert.ToInt32(e.CommandArgument);
      OnPagerCommand(new PagerCommandEventArgs(this.StartRowIndex, this.PageSize, e));
      this.ChildControlsCreated = false;
    }
  }
}

I’m not going to bore you with exactly what it does but instead show you a couple of examples of how it can be used.

Without a DataSource control

With the new control this is now pretty easy because the TotalRowCount property is not read only. All you have to do is set this property once you have bound your data, ie (GetData is a method which passes in the StartRowIndex and the MaximumRows and returns only the rows you want to display for that page and GetDataCount returns the total number of rows):

protected void Page_Load(object sender, EventArgs e)
{
  if (!this.IsPostBack)
  {
    this.BindData(0);
    pager.TotalRowCount = this.GetDataCount();
  }
}

protected void BindData(int startRowIndex)
{
  lvwItems.DataSource = this.GetData(startRowIndex, pager.PageSize);
  lvwItems.DataBind();
}

protected void pager_PagerCommand(Object sender, Flixon.Web.UI.WebControls.PagerCommandEventArgs e)
{
  this.BindData(e.StartRowIndex);
}

Then in your aspx file you reference the control in the standard way and put:

<flixon:Pager ID="pager" runat="server" PageSize="15" OnPagerCommand="pager_PagerCommand" />

Without a DataSoruce control (but using the query string to make it seo friendly)

You could modify the above to:

<flixon:Pager ID="pager" runat="server" PageSize="15" QueryStringField="Page" Url="/Default.aspx?Page={0}" />

The PagerCommand event is not necessary if we are using the query string therefore change the code behind file accordingly:

protected void Page_Load(object sender, EventArgs e)
{
  if (!this.IsPostBack)
  {
    lvwItems.DataSource = this.GetData(pager.StartRowIndex, pager.PageSize);
    lvwItems.DataBind();
    pager.TotalRowCount = this.GetDataCount();
  }
}

 

Tags:
Overall Rating:
 
 [1 Vote(s)]

Bookmarks

de.lico.us Digg Facebook Reddit Stumble Upon
Added By: Lee Timmins on 28th Apr 2008 at 01:00
Last Updated: 10th Dec 2009 at 09:18

Comments

2. Comment at 19:21 on 04th Jan 2012 by rupasinha wrote:
This comment has been deleted.
1. Comment at 09:09 on 06th May 2010 by Joro wrote:

 Nice! Been looking for something "simple" like this. Found more complex solutions on CodePlex, but this is better since you can adapt it easier. So I didn't have to do it my self. =) BTW why not move this to CodePlex? I found some bugs, and made some improvements to your code.

    Quote
Order:

New Comment

You must be logged in to comment on this article. Please login or register.