
Introduction
This article provides a way to programmatically implement paging in a Windows DataGridView control. To my knowledge, there is no such functionality currently existing in the standard DataGridView control.
Background
A few months ago, I was put on a project to help out with the work load. Deadlines were looming and the extra help was needed. The form I had to code was a query form that listed various log tables in the database. The user could then select one of these log tables from a dropdown menu and the events captured by the selected log table was displayed in a DataGridView control. This part of the coding was easy, but there seemed to be just one problem. Sometimes these log tables were really large, containing hundreds even thousands of lines of entries. The customer wanted only to display a set amount of entries at a time and be able to page to the next, previous, last, and first pages of the grid.
I was then faced with trying to make a DataGridView control pageable. At first I thought that this task would be easy enough, but the more I researched it, the more I realised that there was no built-in functionality for making a DataGridView control pageable. I had to therefore come up with my own solution to extend the DataGridView control to include paging. The article below outlines a very basic Windows Form containing a Windows Toolstrip control with four buttons labeled First, Previous, Next, and Last. It also contains a DataGridView control and a BindingSource control.
A great idea for this project is to create your own custom control inheriting the base class for a DataGridView control and just extending its functionality to include paging, but for simplicity's sake, I have done the quick and dirty version by just adding the code in the form. Perhaps in a future article I will outline the steps to create your own custom control that inherits from the DataGridView control.
Using the code
Before we look at the code, do the following:
- Create a new C# Windows Application with Visual Studio 2005
- Drag a
DataGridView control on to the Windows Form and call it dgNames
- Drag a
BindingSource control on to the form
- Drag a
Toolstrip control on to the form
- On the
Toolstrip control, create four buttons using the dropdown menu provided by the control
- In the Properties of each button, set the
DisplayStyle of each button to Text
- Set the buttons' text to First, Previous, Next, and Last, respectively
- Name the buttons as follows:
- First =
tsbFirst
- Previous =
tsbPrevious
- Next =
tsbNext
- Last =
tsbLast
Now that we have done this, let's look at the code.
Our Data Class
Usually you would read the data from a database via some sort of class. For simplicity's sake, I created a class called clsData that returns a dataset of hardcoded values. But you would need to use a class that reads the data you want to display in the DataGridView from some sort of data source. The following code is the code for the class used to return our dummy data.
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
namespace GridviewPaging
{
class clsData
{
public static DataSet GetData()
{
DataSet ds = new DataSet();
try
{
ds.Tables.Add(GetTable());
return ds;
}
catch (Exception ex)
{
String msg = ex.Message;
return null;
}
finally
{
}
}
private static DataTable GetTable()
{
DataTable dt;
dt = MakeTable();
try
{
int i = 0;
String Name = "Name";
String LastName = "LastName";
DataRow dr;
for (i = 0; i <= 1000; i++)
{
dr = dt.NewRow();
dr["Fname"] = i + " " + Name;
dr["LName"] = i + " " + LastName;
dt.Rows.Add(dr);
}
return dt;
}
catch (Exception ex)
{
String msg = ex.Message;
return null;
}
finally
{
}
}
private static DataTable MakeTable()
{
DataTable namesTable = new DataTable("Names");
DataColumn idColumn = new DataColumn();
idColumn.DataType = System.Type.GetType("System.Int32");
idColumn.ColumnName = "RecordID";
idColumn.AutoIncrement = true;
namesTable.Columns.Add(idColumn);
DataColumn fNameColumn = new DataColumn();
fNameColumn.DataType = System.Type.GetType("System.String");
fNameColumn.ColumnName = "Fname";
fNameColumn.DefaultValue = "Fname";
namesTable.Columns.Add(fNameColumn);
DataColumn lNameColumn = new DataColumn();
lNameColumn.DataType = System.Type.GetType("System.String");
lNameColumn.ColumnName = "LName";
namesTable.Columns.Add(lNameColumn);
DataColumn[] keys = new DataColumn[1];
keys[0] = idColumn;
namesTable.PrimaryKey = keys;
return namesTable;
}
}
}
Our Main Form
If the code segments below don't make much sense now, don't worry. I have included the full code for the Windows Form at the end of this article.
At the top of the code window of our Windows Form, make sure you have these using statements:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
I am also using the following namespace: namespace GridviewPaging.
I have also added a few variables global to this form to enable the paging of the DataGridView control. Add the following variables:
private string NavClicked = "";
private string RecordID1 = "";
private string RecordID2 = "";
private DataSet dsTemp = new DataSet();
int CurrentPage = 1;
Just below these variables, add the following enumerator. This determines which button was clicked.
private enum NavButton
{
First = 1,
Next = 2,
Previous = 3,
Last = 4,
}
In the Form1_Load event, add the following code:
private void Form1_Load(object sender, EventArgs e)
{
DataSet ds = new DataSet();
ds = GridviewPaging.clsData.GetData();
dsTemp = ds;
if (ds.Tables[0].Rows.Count > 0)
{
fillDataGrid_dtgBrowse(ds);
}
else
{
bindingSource1.DataSource = null;
bindingSource1.Clear();
dgNames.DataSource = bindingSource1;
}
}
In the Form1_Load event, we are doing the following:
- We are instantiating a new
DataSet called ds.
- We are setting
ds equal to the DataSet returned by the GetData method in our clsData class.
- We are setting another
DataSet called dsTemp equal to ds to enable the persistence of data.
- If our DataSet
ds contains any data, we fill our DataGridView with this data.
- If no data is returned, we clear the
DataGridView.
We will now use two methods to enable paging. These methods are fillDataGrid_dtgBrowse and DeterminePageBoundaries. These methods are very well commented, so I didn't go into much detail in explaining what they do. The comments in the code do this.
fillDataGrid_dtgBrowse
#region fillDataGrid_dtgBrowse
private void fillDataGrid_dtgBrowse(DataSet dtsTableData)
{
try
{
Cursor = Cursors.WaitCursor;
bindingSource1.DataSource = dtsTableData.Tables[0];
DeterminePageBoundaries(dtsTableData);
bindingSource1.Filter = "RecordID >= " + RecordID1 + " and RecordID <= " + RecordID2;
dgNames.DataSource = bindingSource1;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
Cursor = Cursors.Default;
}
}
#endregion
The fillDataGrid_dtgBrowse method simply does the binding of the data to the DataGridView control. Next we have the method DeterminePageBoundaries. We need to know the boundaries of the pages to enable the paging in the DataGridView.
DeterminePageBoundaries
#region DeterminePageBoundaries
private void DeterminePageBoundaries(DataSet dsPages)
{
int TotalRowCount = dsPages.Tables[0].Rows.Count;
int pageRows = 100;
int pages = 0;
if (pageRows < TotalRowCount)
{
if ((TotalRowCount % pageRows) > 0)
{
pages = ((TotalRowCount / pageRows) + 1);
}
else
{
pages = TotalRowCount / pageRows;
}
}
else
{
pages = 1;
}
int LowerBoundary = 0;
int UpperBoundary = 0;
switch (NavClicked)
{
case "First":
CurrentPage = 1;
LowerBoundary = ((pageRows * CurrentPage) - (pageRows - 1));
if (pageRows < TotalRowCount)
{
UpperBoundary = (pageRows * CurrentPage);
}
else
{
UpperBoundary = TotalRowCount;
}
RecordID1 = dsPages.Tables[0].Rows[LowerBoundary - 1]["RecordID"].ToString();
RecordID2 = dsPages.Tables[0].Rows[UpperBoundary - 1]["RecordID"].ToString();
break;
case "Last":
CurrentPage = pages;
LowerBoundary = ((pageRows * CurrentPage) - (pageRows - 1));
UpperBoundary = TotalRowCount;
RecordID1 = dsPages.Tables[0].Rows[LowerBoundary - 1]["RecordID"].ToString();
RecordID2 = dsPages.Tables[0].Rows[UpperBoundary - 1]["RecordID"].ToString();
break;
case "Next":
if (CurrentPage != pages)
{
CurrentPage += 1;
}
LowerBoundary = ((pageRows * CurrentPage) - (pageRows - 1));
if (CurrentPage == pages)
{
UpperBoundary = TotalRowCount;
}
else
{
UpperBoundary = (pageRows * CurrentPage);
}
RecordID1 = dsPages.Tables[0].Rows[LowerBoundary - 1]["RecordID"].ToString();
RecordID2 = dsPages.Tables[0].Rows[UpperBoundary - 1]["RecordID"].ToString();
break;
case "Previous":
if (CurrentPage != 1)
{
CurrentPage -= 1;
}
LowerBoundary = ((pageRows * CurrentPage) - (pageRows - 1));
if (pageRows < TotalRowCount)
{
UpperBoundary = (pageRows * CurrentPage);
}
else
{
UpperBoundary = TotalRowCount;
}
RecordID1 = dsPages.Tables[0].Rows[LowerBoundary - 1]["RecordID"].ToString();
RecordID2 = dsPages.Tables[0].Rows[UpperBoundary - 1]["RecordID"].ToString();
break;
default:
LowerBoundary = ((pageRows * CurrentPage) - (pageRows - 1));
if (pageRows < TotalRowCount)
{
UpperBoundary = (pageRows * CurrentPage);
}
else
{
UpperBoundary = TotalRowCount;
}
RecordID1 = dsPages.Tables[0].Rows[LowerBoundary - 1]["RecordID"].ToString();
RecordID2 = dsPages.Tables[0].Rows[UpperBoundary - 1]["RecordID"].ToString();
break;
}
}
#endregion
Lastly, we need to add code to the click events for our Next, Previous, First, and Last buttons. After you've done this, the rest is a piece of old tackie.
Button click events
#region Page through DataGridView
private void tsbFirst_Click(object sender, EventArgs e)
{
try
{
NavClicked = NavButton.First.ToString();
fillDataGrid_dtgBrowse(dsTemp);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
Cursor = Cursors.Default;
}
}
private void tsbPrevious_Click(object sender, EventArgs e)
{
try
{
NavClicked = NavButton.Previous.ToString();
fillDataGrid_dtgBrowse(dsTemp);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
Cursor = Cursors.Default;
}
}
private void tsbNext_Click(object sender, EventArgs e)
{
try
{
NavClicked = NavButton.Next.ToString();
fillDataGrid_dtgBrowse(dsTemp);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
Cursor = Cursors.Default;
}
}
private void tsbLast_Click(object sender, EventArgs e)
{
try
{
NavClicked = NavButton.Last.ToString();
fillDataGrid_dtgBrowse(dsTemp);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
Cursor = Cursors.Default;
}
}
#endregion
After you have added the click events for the buttons on your form, your code should look like this:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace GridviewPaging
{
public partial class frmMain : Form
{
private string NavClicked = "";
private string RecordID1 = "";
private string RecordID2 = "";
private DataSet dsTemp = new DataSet();
int CurrentPage = 1;
private enum NavButton
{
First = 1,
Next = 2,
Previous = 3,
Last = 4,
}
public frmMain()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
DataSet ds = new DataSet();
ds = GridviewPaging.clsData.GetData();
dsTemp = ds;
if (ds.Tables[0].Rows.Count > 0)
{
fillDataGrid_dtgBrowse(ds);
}
else
{
bindingSource1.DataSource = null;
bindingSource1.Clear();
dgNames.DataSource = bindingSource1;
}
}
#region fillDataGrid_dtgBrowse
private void fillDataGrid_dtgBrowse(DataSet dtsTableData)
{
try
{
Cursor = Cursors.WaitCursor;
bindingSource1.DataSource = dtsTableData.Tables[0];
DeterminePageBoundaries(dtsTableData);
bindingSource1.Filter = "RecordID >= " + RecordID1 +
" and RecordID <= " + RecordID2;
dgNames.DataSource = bindingSource1;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
Cursor = Cursors.Default;
}
}
#endregion
#region DeterminePageBoundaries
private void DeterminePageBoundaries(DataSet dsPages)
{
int TotalRowCount = dsPages.Tables[0].Rows.Count;
int pageRows = 100;
int pages = 0;
if (pageRows < TotalRowCount)
{
if ((TotalRowCount % pageRows) > 0)
{
pages = ((TotalRowCount / pageRows) + 1);
}
else
{
pages = TotalRowCount / pageRows;
}
}
else
{
pages = 1;
}
int LowerBoundary = 0;
int UpperBoundary = 0;
switch (NavClicked)
{
case "First":
CurrentPage = 1;
LowerBoundary = ((pageRows * CurrentPage) - (pageRows - 1));
if (pageRows < TotalRowCount)
{
UpperBoundary = (pageRows * CurrentPage);
}
else
{
UpperBoundary = TotalRowCount;
}
RecordID1 = dsPages.Tables[0].Rows[LowerBoundary - 1]
["RecordID"].ToString();
RecordID2 = dsPages.Tables[0].Rows[UpperBoundary - 1]
["RecordID"].ToString();
break;
case "Last":
CurrentPage = pages;
LowerBoundary = ((pageRows * CurrentPage) - (pageRows - 1));
UpperBoundary = TotalRowCount;
RecordID1 = dsPages.Tables[0].Rows[LowerBoundary - 1]
["RecordID"].ToString();
RecordID2 = dsPages.Tables[0].Rows[UpperBoundary - 1]
["RecordID"].ToString();
break;
case "Next":
if (CurrentPage != pages)
{
CurrentPage += 1;
}
LowerBoundary = ((pageRows * CurrentPage) - (pageRows - 1));
if (CurrentPage == pages)
{
UpperBoundary = TotalRowCount;
}
else
{
UpperBoundary = (pageRows * CurrentPage);
}
RecordID1 = dsPages.Tables[0].Rows[LowerBoundary - 1]
["RecordID"].ToString();
RecordID2 = dsPages.Tables[0].Rows[UpperBoundary - 1]
["RecordID"].ToString();
break;
case "Previous":
if (CurrentPage != 1)
{
CurrentPage -= 1;
}
LowerBoundary = ((pageRows * CurrentPage) - (pageRows - 1));
if (pageRows < TotalRowCount)
{
UpperBoundary = (pageRows * CurrentPage);
}
else
{
UpperBoundary = TotalRowCount;
}
RecordID1 = dsPages.Tables[0].Rows[LowerBoundary - 1]
["RecordID"].ToString();
RecordID2 = dsPages.Tables[0].Rows[UpperBoundary - 1]
["RecordID"].ToString();
break;
default:
LowerBoundary = ((pageRows * CurrentPage) - (pageRows - 1));
if (pageRows < TotalRowCount)
{
UpperBoundary = (pageRows * CurrentPage);
}
else
{
UpperBoundary = TotalRowCount;
}
RecordID1 = dsPages.Tables[0].Rows[LowerBoundary - 1]
["RecordID"].ToString();
RecordID2 = dsPages.Tables[0].Rows[UpperBoundary - 1]
["RecordID"].ToString();
break;
}
}
#endregion
#region Page through DataGridView
private void tsbFirst_Click(object sender, EventArgs e)
{
try
{
NavClicked = NavButton.First.ToString();
fillDataGrid_dtgBrowse(dsTemp);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
Cursor = Cursors.Default;
}
}
private void tsbPrevious_Click(object sender, EventArgs e)
{
try
{
NavClicked = NavButton.Previous.ToString();
fillDataGrid_dtgBrowse(dsTemp);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
Cursor = Cursors.Default;
}
}
private void tsbNext_Click(object sender, EventArgs e)
{
try
{
NavClicked = NavButton.Next.ToString();
fillDataGrid_dtgBrowse(dsTemp);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
Cursor = Cursors.Default;
}
}
private void tsbLast_Click(object sender, EventArgs e)
{
try
{
NavClicked = NavButton.Last.ToString();
fillDataGrid_dtgBrowse(dsTemp);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
Cursor = Cursors.Default;
}
}
#endregion
}
}
I hope that this will shed some light on how to make a DataGridView pageable. It is probably not the easiest method to accomplish this, but it worked very well for me. Download the source code and demo for the sample application and play around with it a bit.
Points of interest
The class clsData has some nice examples of how to create a datatable and add that datatable to a dataset. Somewhere sometime you might just need to do this, so have a look at the included class clsData as well.
Updates
- 20 June 2007: I was going to post another article, extending this code into a user control that would include paging. However, after reading my article, topherino posted his own user control extending the
DataGridView control to include paging. (See the Messages section below for the link to his article.) Check out his article for a DataGridView control that you simply drag and drop on to your form. It's a nicely written control and well worth using in your applications.
- 3 July 2007: I have found a control in Visual Studio 2005 that provides more or less the functionality we need for paging. After you add a
BindingSource control to your form, add a BindingNavigator control to the form as well. You set the BindingSource to your dataset, like this:
bindingSource1.DataSource = ds.Tables[0];
Then you set your DataGridView's datasource to the BindingSource like this:
dgNames.DataSource = bindingSource1;
After you do this, you add the BindingNavigator control to the form and set its BindingSource property to your BindingSource (bindingSource1) that you added to your page. The BindingNavigator only allows you to advance one row at a time when you click the Next button. I do feel however that there might be a way to override this functionality and make it advance multiple rows at a time. Anyway, it's worth looking into. I haven't had time to look at this myself, but I'm sure that it could help others out there that only want to move one row at a time.
28 March 2009: Fixed the article to display correctly in the Firefox browser.