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.