
Introduction
The GridView control is a very powerful and scalable .net control. You use it inside some application to list entites. However , there is a disadvantage when you want to use the Paging implementation : you must systematically bind the gridview with a select of all data when you change the index to see the other pages. So Imagine that you must work with a datasource with 400 000 records : the postback is expensive , the treatment is not optimized.
Background
I tryed to imagin an implementation solution with a gridview and an independant paging functionnality. The purpose is just to bind the gridview with the current datarows that you see. Sql server allow you to get a part of rows of a query with this syntaxe :
With Prod AS
( SELECT [ProductID],
[ProductName],
[SupplierID],
[CategoryID],
[QuantityPerUnit],
[UnitPrice],
[UnitsInStock],
[UnitsOnOrder],
[ReorderLevel],
[Discontinued] ,
ROW_NUMBER() OVER (order by ProductName) as RowNumber from Products )
SELECT [ProductID],
[ProductName],
[SupplierID],
[CategoryID],
[QuantityPerUnit],
[UnitPrice],
[UnitsInStock],
[UnitsOnOrder],
[ReorderLevel],
[Discontinued] from Prod Where RowNumber
Between @pBegin and @pEnd
I serach on Codeproject if a similar solution existed , and i found a source code whose implemented a very good smart pager. You can find it at : http://www.codeproject.com/KB/aspnet/SmartPager.aspx. So i extend the Gridview class to create GridviewPager class whose own a instance of this SmartPager control. The smart pager control is a complex type property (it has several property) so the pager class must implement IStateManager Interface , and a event to know when the index change
The implementation of the smart pager generate a inconvegnient postback in javascript (method SmartPagerSelectPage of Smartpager.js) when the index change so 2 postback are executed. To resolve this problem i parsed all the request form keys to find CALLBACKPARAM value in order to filter the bad post back , and just executing the good in GridviewPager Page_Load event.
Using the Code
using System;
using System.Net;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Avg.Controls;
namespace Avg.Controls
{
public delegate void DataSourceChangedEventHandler(object sender, bool isEmpty);
[ToolboxData("<{0}:GridViewPager runat="server">")]
public class GridViewPager : GridView
{
public event DataSourceChangedEventHandler DataSourceChanged;
public string CurrentViewID
{
get { return string.Concat("CurrentView_", UniqueID); }
}
public override object DataSource
{
get
{
if (ViewState[CurrentViewID] != null)
return ViewState[CurrentViewID];
else
return base.DataSource;
}
set
{
base.DataSource = value;
ViewState[CurrentViewID] = value;
if (value == null)
{
if (DataSourceChanged != null)
DataSourceChanged(this, true);
}
else
{
if (DataSourceChanged != null)
DataSourceChanged(this, false);
}
}
}
protected SmartPager pager;
public int PageNumberCliked
{
get { return (ViewState["PageNumberCliked"] == null) ? 1 : Int32.Parse(
ViewState["PageNumberCliked"].ToString()); }
set { ViewState["PageNumberCliked"] = value; }
}
public int Display
{
get { return pager.Display; }
set { pager.Display = value; }
}
public int rowB
{
get { return (Page.Session["rowB"] == null) ? 0 : Int32.Parse(
Page.Session["rowB"].ToString()); }
set { Page.Session["rowB"] = value; }
}
public int rowE
{
get { return (Page.Session["rowE"] == null) ? PageSize : Int32.Parse(
Page.Session["rowE"].ToString()); }
set { Page.Session["rowE"] = value; }
}
public int PageCount
{
get
{
if (RowCount == 0)
{
return PageSize;
}
if (this.DataSource == null)
{
throw new Exception("Datasource is empy");
}
if (PageSize == 0)
{
throw new Exception("Page size must be positive");
}
return (int)(RowCount / PageSize) + 1;
}
}
public int RowCount
{
get { return (Page.Session["RowCount"] == null) ? 5 : Int32.Parse(
Page.Session["RowCount"].ToString()); }
set { Page.Session["RowCount"] = value; }
}
public int CurrentPage
{
get { return (ViewState["CurrentPage"] == null) ? 1 : Int32.Parse(
ViewState["CurrentPage"].ToString()); }
set { ViewState["CurrentPage"] = value; }
}
public GridViewPager() : base()
{
pager = new SmartPager();
pager.OnClickEvent += new OnClickPagerNumberEventHandler(pager_OnClickEvent);
}
#region events to implement on the page side
private static readonly object OnSelectRowEventKey = new object();
public event EventHandler DoSelectRow
{
add { Events.AddHandler(OnSelectRowEventKey, value); }
remove { Events.RemoveHandler(OnSelectRowEventKey, value); }
}
protected virtual void OnSelectedRow(EventArgs e)
{
EventHandler handler = Events[OnSelectRowEventKey] as EventHandler;
if (handler != null)
handler(this, e);
else
throw new Exception("You must implement OnSelectRow method");
}
private static readonly object OnLoadRowCountEventKey = new object();
public event EventHandler DoLoadRowCount
{
add { Events.AddHandler(OnLoadRowCountEventKey, value); }
remove { Events.RemoveHandler(OnLoadRowCountEventKey, value); }
}
protected virtual void OnLoadedRowCount(EventArgs e)
{
EventHandler handler = Events[OnLoadRowCountEventKey] as EventHandler;
if (handler != null)
handler(this, e);
else
throw new Exception("You must implement OnLoadRowCount method");
}
#endregion events to implement on the page side
#region Component event
protected override void OnInit(EventArgs e)
{
base.OnInit(e); Page.Load += new EventHandler(Page_Load);
}
void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack) OnLoadedRowCount(EventArgs.Empty);
bool goodCall = true;
foreach (string Key in Page.Request.Form.AllKeys)
{
if (Key.EndsWith("CALLBACKPARAM"))
{
goodCall = false;
}
}
if (goodCall)
OnSelectedRow(EventArgs.Empty);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e); Controls.Add(pager);
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
pager.PageCount = PageCount;
}
void pager_OnClickEvent(object sender, string pPageNum)
{
PageNumberCliked = Int32.Parse(pPageNum);
CurrentPage = PageNumberCliked;
if (CurrentPage == 1)
{
rowB = 0;
rowE = PageSize;
}
else
{
rowE = (PageSize * CurrentPage) - 1;
rowB = rowE - (PageSize - 1);
}
Page.Response.Write( "<script language="Javascript">__doPostBack('__Page', 'MyCustomArgument');</script>" );
}
#endregion Component event
#region IStateManager Members
protected override void LoadViewState(object savedState)
{
if (savedState != null)
{
object[] state = savedState as object[];
if (state != null && state.Length == 2)
{
base.LoadViewState(state[0]);
if (state[1] != null)
((IStateManager)this.pager).LoadViewState(state[1]);
}
}
else
base.LoadViewState(savedState);
}
protected override object SaveViewState()
{
object[] state = new object[2];
state[0] = base.SaveViewState();
if (pager != null)
state[1] = ((IStateManager)this.pager).SaveViewState();
return state;
}
protected override void TrackViewState()
{
base.TrackViewState();
if (pager != null)
((IStateManager)this.pager).TrackViewState();
}
#endregion
}
Points of Interest
I learned how to extend a complex control like gridview.
History
If you have some improvement , i'm open to listen them