Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

A very nice and complete custom GridView pager with no ViewState

3.73/5 (8 votes)
14 Oct 2007CPOL5 min read 1   984  
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).

Image 1

Figure 1: The custom pager

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 the Get and the ExecuteScalar are of interest to us.
  • The CustomersDataLayer class uses the DataLayerHelper methods to query the Customers table of the Northwind database, in particular:

    • The GetCustomers method returns all the rows of the Customers table in a DataTable.
    • 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-limited DataTable. It takes two parameters:
      1. startIndex, the row number to start retrieving from, and
      2. maxPageRows, the maximum number of rows to display per page.

    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):

    C#
    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:

    C#
    GridView1.DataSource = 
      CustomersDataLayer.GetPageCustomers(StartIndex, PageSize); GridView1.DataBind();
  • The 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:
  • C#
    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 the PageCount property of the GridView.
  • The class calculates the current page by converting the row index (StartIndex) to a page index:
  • C#
    currentPageIndex = (int)Math.Ceiling((decimal)StartIndex / maxPageRows);

    This substitutes the PageIndex property of the GridView.

  • The customer pager can no longer be placed inside the 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:
  • C#
    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); 
            [...] 
        } 
    }
  • All the page links can no longer call the special Page commands. Their 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:
    • C#
      ((currentPageIndex - 1) * maxPageRows).ToString();
    • The page number links values:
    • C#
      ((i - 1) * maxPageRows).ToString();
    • The Last page link value:
    • C#
      ((totalPages-1) * maxPageRows).ToString();
    • The ddlPageGroups selected values:
    • C#
      new ListItem(group, (groupFirstPageNumber * maxPageRows).ToString());
  • Each time the user clicks the customer pager, the 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:
  • C#
    public event CommandEventHandler PageNumberClick; 
    public event EventHandler PageGroupChange;

    The dynamic controls are bound to these event handlers, for example:

    C#
    lnkPage.Command += new CommandEventHandler(PageNumberClick);

    Finally, the necessary code in the web page code-behind:

    C#
    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:

    C#
    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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)