CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeffrey Palermo (.com)

Blog moved to www.jeffreypalermo.com

How to create a jump-to-page feature to enhance the DataGrid pager - level 200

When displaying information in a grid on an ASP.NET web page, it's often a good idea to page the information if there is a lot of it.  For instance, if you have 10000 rows of information to potentially display, it's unreasonable to expect the user to wait to download all of it at one time and scroll forever.  It's wasteful, too.  Instead, page it in smaller chunks (10-50).  Then let the user move to different pages of information.  But when there is a lot of pages, you don't want the user to have to click multiple times to get to page 15.  If information is sorted, a user will often know what page to jump to.  In this post, I'll cover how to add a "jump-to-page" feature to the paging mechanism of an ASP.NET DataGrid.
 
First, let's cover how to get paging working on a normal, stock DataGrid.  I've spoofed some data in the form of a string array, and I have my basic, numeric pager:
 
<form id="Form1" method="post" runat="server">
  <asp:DataGrid AllowPaging="True" PagerStyle-Mode="NumericPages" 
     PagerStyle-PageButtonCount="10" ID="grid" Runat="server" />
</form>

 

using System;

using System.Web.UI;

using System.Web.UI.WebControls;

 

public class WebForm1 : Page

{

    protected DataGrid grid;

 

    protected override void OnLoad(EventArgs e)

    {

        base.OnLoad(e);

        if (!IsPostBack)

        {

            grid.DataSource = this.GetSource();

            this.BindGrid(0);

        }

    }

 

    protected override void OnInit(EventArgs e)

    {

        base.OnInit(e);

        grid.PageIndexChanged += new DataGridPageChangedEventHandler(grid_PageIndexChanged);

    }

 

    private string[] GetSource()

    {

        //just imagine how you would get/cache your data source.

        string[] rows = new string[10000];

        for (int i = 0; i < 10000; i++)

        {

            rows[i] = i.ToString();

        }

        return rows;

    }

 

    private void BindGrid(int currentPageIndex)

    {

        grid.DataSource = this.GetSource();

        grid.CurrentPageIndex = currentPageIndex;

        grid.DataBind();

    }

 

    private void grid_PageIndexChanged(object source, DataGridPageChangedEventArgs e)

    {

        this.BindGrid(e.NewPageIndex);

    }

}

This works fine, but I have to click so many times to get to page 100 (10 clicks).  I would like to be able to punch in a number and jump to that page quickly.  Let's examine the following code and how I created some controls to insert into the DataGrid paging mechanism.  The DataGrid doesn't provide a PageTemplate, so we have to do a bit of manual control arrangement.  First, we'll set up how we would like it to work:

Let's add our jump-to section:

<form id="Form1" method="post" runat="server">
 <asp:DataGrid AllowPaging="True" PagerStyle-Mode="NumericPages"
  PagerStyle-PageButtonCount="10" ID="grid" Runat="server" />
 <asp:Panel ID="pagerEnhancement" Runat="server">
  Jump to page:
  <asp:TextBox id=jumpToText Runat="server" Width="25"></asp:TextBox>
  <asp:Button id=jumpToButton Runat="server" Text="Go"></asp:Button>
 </asp:Panel>
</form>

Notice that we just have a textbox and a button.  We've added code to make this affect the DataGrid page:

using System;

using System.Web.UI;

using System.Web.UI.WebControls;

 

public class WebForm1 : Page

{

    protected System.Web.UI.WebControls.TextBox jumpToText;

    protected System.Web.UI.WebControls.Button jumpToButton;

    protected System.Web.UI.WebControls.Panel pagerEnhancement;

    protected DataGrid grid;

 

    protected override void OnLoad(EventArgs e)

    {

        base.OnLoad(e);

        if (!IsPostBack)

        {

            grid.DataSource = this.GetSource();

            this.BindGrid(0);

        }

    }

 

    protected override void OnInit(EventArgs e)

    {

        base.OnInit(e);

        grid.PageIndexChanged += new DataGridPageChangedEventHandler(grid_PageIndexChanged);

        jumpToButton.Click +=new EventHandler(jumpToButton_Click);

    }

 

    private string[] GetSource()

    {

        //just imagine how you would get/cache your data source.

        string[] rows = new string[10000];

        for (int i = 0; i < 10000; i++)

        {

            rows[i] = i.ToString();

        }

        return rows;

    }

 

    private void BindGrid(int currentPageIndex)

    {

        grid.DataSource = this.GetSource();

        grid.CurrentPageIndex = currentPageIndex;

        grid.DataBind();

    }

 

    private void grid_PageIndexChanged(object source, DataGridPageChangedEventArgs e)

    {

        this.BindGrid(e.NewPageIndex);

    }

 

    private void jumpToButton_Click(object sender, System.EventArgs e)

    {

        int newPage = int.Parse(jumpToText.Text);

        this.BindGrid(newPage - 1); //The DataGrid needs a page index, zero-based.

    }

}

This works, but when I make my DataGrid pretty, I don't want this paging attachment left out.  I need it to be inside the DataGrid, so let's write some code to move it from outside the DataGrid to right next to the default pager:

using System;

using System.Web.UI;

using System.Web.UI.WebControls;

 

public class WebForm1 : Page

{

    protected TextBox jumpToText;

    protected Button jumpToButton;

    protected Panel pagerEnhancement;

    protected DataGrid grid;

 

    protected override void OnLoad(EventArgs e)

    {

        base.OnLoad(e);

        if (!IsPostBack)

        {

            grid.DataSource = this.GetSource();

            this.BindGrid(0);

        }

    }

 

    protected override void OnInit(EventArgs e)

    {

        base.OnInit(e);

        grid.PageIndexChanged += new DataGridPageChangedEventHandler(grid_PageIndexChanged);

        jumpToButton.Click += new EventHandler(jumpToButton_Click);

        grid.ItemCreated +=new DataGridItemEventHandler(grid_ItemCreated);

    }

 

    private string[] GetSource()

    {

        //just imagine how you would get/cache your data source.

        string[] rows = new string[10000];

        for (int i = 0; i < 10000; i++)

        {

            rows[i] = i.ToString();

        }

        return rows;

    }

 

    private void BindGrid(int currentPageIndex)

    {

        grid.DataSource = this.GetSource();

        grid.CurrentPageIndex = currentPageIndex;

        grid.DataBind();

    }

 

    private void grid_PageIndexChanged(object source, DataGridPageChangedEventArgs e)

    {

        this.BindGrid(e.NewPageIndex);

    }

 

    private void jumpToButton_Click(object sender, EventArgs e)

    {

        int newPage = int.Parse(jumpToText.Text);

        this.BindGrid(newPage - 1); //The DataGrid needs a page index, zero-based.

    }

 

    private void grid_ItemCreated(object sender, DataGridItemEventArgs e)

    {

        if(e.Item.ItemType == ListItemType.Pager)

        {

            TableCell pagerCell = (TableCell) e.Item.Controls[0];// User Trace to view the

            //control tree and found out how to grab the pager.;

            pagerCell.Controls.Add(pagerEnhancement);

        }

    }

}

Notice how I am listening to the ItemCreated event of the DataGrid and then moving the control when I'm on the Pager.  I used the Trace feature to observe the DataGrid control to see how the Pager was laid out.  The DataGrid is rendered as an Html table, so I just needed to move my extra paging controls to the appropriate TableCell.

Obviously, I have simplified this example to demonstrate the basics of adding to the default paging mechanism in the DataGrid.  When I use this technique, I make the appearance a lot more attractive, and I also validate that an actual page number was entered into the TextBox.  After all, I have to gracefully handle whatever the user types into that box. 



Comments

Chris said:

I love this tip, but it doesn't appear to work in ASP.NET 2.0, at least with my code. I am getting this error msg trying to move the panel control from the page into the gridview pager cell:

System.Web.HttpException: The control collection cannot be modified during DataBind, Init, Load, PreRender or Unload phases.

It doesn't appear to be a gridview control problem, more of a page problem.

This works:
Private Sub RowCreated(ByVal sender As Object, ByVal e As GridViewRowEventArgs) Handles gvPageHits.RowCreated
If e.Row.RowType = DataControlRowType.Pager Then
e.Row.Cells(0).Controls.Add(New TextBox)
End If
End Sub

But this doesn't
Private Sub RowCreated(ByVal sender As Object, ByVal e As GridViewRowEventArgs) Handles gvPageHits.RowCreated
If e.Row.RowType = DataControlRowType.Pager Then
e.Row.Cells(0).Controls.Add(pnlJump)
End If
End Sub

I haven't figured out another way to do this. Any ideas?
# December 1, 2005 7:55 PM

Jeffrey Palermo said:

I haven't tried this with 2.0 yet. Please post again if you get it working.
# December 1, 2005 10:10 PM

About Jeffrey Palermo

Jeffrey Palermo is a software management consultant and the CTO of Headspring Systems in Austin, TX. Jeffrey specializes in Agile coaching and helps companies double the productivity of software teams. Jeffrey is an MCSD.Net , Microsoft MVP, Certified Scrummaster, Austin .Net User Group leader, AgileAustin board member, INETA speaker, INETA Membership Mentor, Christian, husband, father, motorcyclist, Eagle Scout, U.S. Army Veteran, and Texas A&M University graduate. Check out Devlicio.us!

Our Sponsors

Free Tech Publications

This Blog

Syndication

News

Headspring Systems

View Jeffrey Palermo's profile on LinkedIn

See my new blog at .jeffreypalermo.com