A very nice and complete custom GridView pager with no ViewState
How to implement a custom pager for the GridView without relying on ViewState or the GridView's paging features at all.
Introduction
In my article about a custom GridView pager, I received feedback with concerns on how the paging ability of Web Controls (DataSources, GridView
etc.) relies on ViewState thus affecting the size of the page data. In this article, I present you with a ViewState-free version of the original FullGridPager
class. The new class is called FullGridPagerNVS
where NVS stands for "NoViewState". It implements paging functionality without relying on GridView
's paging features - the GridView
's AllowPaging
property is set to false
. Furthermore, no data source is used to bind data to the GridView
. This is done by two Data Layer classes (DataLayerHelper
and CustomersDataLayer
) that return only the appropriate rowset constrained by row boundaries.
Figure 1 illustrates the custom pager (the format is exactly the same as in the original article).
Background
Optionally, my original article on how to create a custom pager for the GridView
PagerTemplate
template.
What you need to run the code
Besides the FullGridPagerNVS.cs found in the App_Code folder, the following files are also needed in order to make the pager work:
- FullGridPagerNVS.css: the styles used by the pager.
- Images folder: contains the images for the First, Previous, Next, and Last links.
- DataLayerHelper.cs: a general purpose database data layer class.
- CustomersDataLayer.cs: a class that returns database rowsets with paging in mind. It uses the Customers table of the Northwind database.
As always, a test bed ASPX web form is included in the ZIP file to demonstrate the usage of the FullGridPagerNVS
class:
- NVSTest.aspx and NVSTest.aspx.cs.
Using the code
Since this is a no-standard ASP.NET implementation, let me explain how it works. First of all, let me list the primary concerns and constraints of this project:
- No data source can be used to fetch data. Instead, code is written to implement a very simple data layer that brings data from the database with paging in mind. The
DataLayerHelper
class is a general purpose data layer that merely executes parameterized SQL queries against a database. Although it contains a bunch of methods, only theGet
and theExecuteScalar
are of interest to us. - The
GetCustomers
method returns all the rows of the Customers table in aDataTable
. - The
GetCustomersCount
method returns the total number of rows in the Customers table. - The
GetPageCustomers
method uses the other two methods above to return a row-limitedDataTable
. It takes two parameters: startIndex
, the row number to start retrieving from, andmaxPageRows
, the maximum number of rows to display per page.
The CustomersDataLayer
class uses the DataLayerHelper
methods to query the Customers table of the Northwind database, in particular:
This way, we can specify row boundaries, for example if startIndex
= 10 and maxPageRows
= 5, the rowset to be returned will only contain rows 10, 11, 12, 13, 14. Have in mind that startIndex
is zero based. The code snippet below simply removes the unnecessary rows outside the desired boundaries (rows 0-9 and 15-max):
public static DataTable GetPageCustomers(int startIndex, int maxPageRows)
{
// Delete the unneccessary rows and return the datatable.
DataTable tbl= GetCustomers();
int maxRows = GetCustomersCount();
int max = startIndex + (maxPageRows - 1);
if (max > maxRows) max = maxRows;
for (int i = 0; i < startIndex; i++)
tbl.Rows[i].Delete();
for (int i = max + 1; i < maxRows; i++)
tbl.Rows[i].Delete(); return tbl;
}
Since there is no data source, the following code (NVSTest.aspx.cs file) is used to manually bind data to the GridView
:
GridView1.DataSource =
CustomersDataLayer.GetPageCustomers(StartIndex, PageSize); GridView1.DataBind();
GridView
must have its AllowPaging
property set to false
, of course. Consequently, there is no PageIndex
or PageCount
properties to rely on. The total number of rows is a value we must know of, too. As a result, such values are initialized in the web page code, and are stored in Session state:public partial class NVSTest : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e
{
if (!IsPostBack)
{
StartIndex = 0;
MaxRows = CustomersDataLayer.GetCustomersCount();
BindData();
}
else
ShowFullGridPagerNVS();
}
public int StartIndex
{
get { return (int)Session["startIndex"]; }
set { Session["startIndex"] = value; }
}
public int MaxRows
{
get { return (int)Session["maxRows"]; }
set { Session["maxRows"] = value; }
}
}
You may as well store them anywhere you like - even in the ViewState! Here is what they mean:
StartIndex
: the binding rowset must begin with this row.MaxRows
: the total number of rows in the binding rowset.
With the above points in mind, I had to make quite a few changes to the FullGridPagerNVS
class.
- Besides the
MaxVisiblePageNumbers
, the total number of rows (MaxRows
) and the number of rows per page (MaxPageRows
) must be passed in as parameters. - The class calculates the total number of pages (
totalPages
). This substitutes thePageCount
property of theGridView
. - The class calculates the current page by converting the row index (
StartIndex
) to a page index:
currentPageIndex = (int)Math.Ceiling((decimal)StartIndex / maxPageRows);
This substitutes the PageIndex
property of the GridView
.
PageTemplate
template. This affects the behaviour of the dynamically created controls during postbacks. The class now dynamically creates all the necessary controls including the First, Previous, Next, Last links as well as the page groups DropDownList
. Please note that before re-creating them, the HTML container is cleared out:public void CreateCustomPager(Control Container, int StartIndex) {
[...]
HtmlTable pagerInnerTable =
(HtmlTable)Container.FindControl("pagerInnerTable");
if (pagerInnerTable != null)
{
// Remove the dynamically created cells before
// recreating them.
pagerInnerTable.Rows[0].Cells.Clear();
[...]
}
[...]
HtmlTable pagerOuterTable =
(HtmlTable)Container.FindControl("pagerOuterTable");
if (pagerOuterTable != null)
{
// Remove the pageGroups cell if it exists.
if (pagerOuterTable.Rows[0].Cells.Count > 1)
pagerOuterTable.Rows[0].Cells.RemoveAt(1);
[...]
}
}
CommandArgument
value can no longer be the PageIndex
value. This is also true for the page groups DropDownList
. The FullGridPagerNVS
maps each page index to the starting row index simply by multiplying the page index with the total number of rows per page. For example:- The Previous page button value:
((currentPageIndex - 1) * maxPageRows).ToString();
((i - 1) * maxPageRows).ToString();
((totalPages-1) * maxPageRows).ToString();
ddlPageGroups
selected values:new ListItem(group, (groupFirstPageNumber * maxPageRows).ToString());
StartIndex
value must change and the GridView
must rebind data. The web page (NVSTest.aspx) has this responsibility now. The FullGridPagerNVS
class is unaware of data binding (and it is not its business, anyway). But, it must provide the means for handling the events of the dynamically created controls. It does so by specifying two event handlers:public event CommandEventHandler PageNumberClick;
public event EventHandler PageGroupChange;
The dynamic controls are bound to these event handlers, for example:
lnkPage.Command += new CommandEventHandler(PageNumberClick);
Finally, the necessary code in the web page code-behind:
ullGridPager.PageNumberClick += new CommandEventHandler(lnkPage_Command);
fullGridPager.PageGroupChange += new EventHandler(ddlPageGroups_SelectedIndexChanged);
And, here is what happens when the user clicks a page number link:
void lnkPage_Command(object sender, CommandEventArgs e)
{
StartIndex = Int32.Parse(e.CommandArgument.ToString());
BindData();
}
Points of interest
Please make sure you understand this is a no-standard ASP.NET paging implementation. This is an on-demand work based on the feedback I received in my article about how to create a custom GridView pager. I urge you to read the original article which provides you with a straight-forward, ASP.NET oriented solution.
History
- 06 Oct. 2007 - Implementation of the original FullGridPager project.
- 14 Oct. 2007 - Implementation of the FullGridPagerNVS project.