Custom ASP.NET Data Pager Control
Monday, April 28th, 2008Hi, 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<PagerCommandEventArgs> PagerCommand
{
add
{
this.Events.AddHandler(EventPagerCommand, value);
}
remove
{
this.Events.RemoveHandler(EventPagerCommand, value);
}
}protected void OnPagerCommand(PagerCommandEventArgs e)
{
EventHandler<PagerCommandEventArgs> handler = (EventHandler<PagerCommandEventArgs>)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(" <u>" + i.ToString() + "</u>"));
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(" <u>" + i.ToString() + "</u>"));
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();
}
}
Hope this helps.