Table of Contents
- Introduction
- Adding Row/ Header CheckBox(es)
- Attaching Header CheckBox's/ DataGridView's Events
- Methods Related to Row/ Header CheckBox and DataGridView
- Winding Up
- History
Introduction
More than a year and a half ago, I wrote an article, Selecting/ Deselecting all the CheckBoxes Inside a GridView for Web Forms. Last month, I got a chance to implement the same functionality for the DataGridView
control for Windows Forms. Initially, I thought it would be as easy as in the case of the GridView
for Web Forms, but it wasn't. It is a bit tricky in the case of the DataGridView
control for Windows Forms. E.g., adding a header CheckBox
in a column of the DataGridView
control is not a straightforward job. So after doing a lot of research work and spending a lot of time on studying the functionality of the control, I have finally arrived at the following solution. All modifications and clarifications are most welcome!
Adding Row CheckBoxes in a DataGridView Column
To add row CheckBoxes
in a DataGridView
column, I've added a DataGridViewCheckBoxColumn
column control. I've also made the AllowUserToAddRows
and AllowUserToDeleteRows
properties of the DataGridView
[dgvSelectAll
] equal to false
, as:
dgvSelectAll.AllowUserToAddRows = false;
dgvSelectAll.AllowUserToDeleteRows = false;
Adding a Header CheckBox in a DataGridView Column
As I said earlier, adding a header CheckBox
in a DataGridView
column is a bit tricky. I've added a header CheckBox
[HeaderCheckBox
] in a DataGridView
column by invoking the AddHeaderCheckBox
method through the Windows Form’s Load
event, as:
private void frmSelectAll_Load(object sender, EventArgs e)
{
AddHeaderCheckBox();
…
}
I'll explain the AddHeaderCheckBox
method later.
Attaching the Header CheckBox’s MouseClick and KeyUp Events
I've wired up the header CheckBox
’s MouseClick
and KeyUp
events through the Windows Form’s Load
event, as:
private void frmSelectAll_Load(object sender, EventArgs e)
{
…
HeaderCheckBox.KeyUp += new KeyEventHandler(HeaderCheckBox_KeyUp);
HeaderCheckBox.MouseClick += new MouseEventHandler(HeaderCheckBox_MouseClick);
…
}
Header CheckBox’s MouseClick Event Handler
The MouseClick
event gets fired whenever we click on the header CheckBox
. Here, the HeaderCheckBoxClick
method is invoked by passing the header CheckBox
’s reference as an argument. I'll discuss the HeaderCheckBoxClick
method later.
private void HeaderCheckBox_MouseClick(object sender, MouseEventArgs e)
{
HeaderCheckBoxClick((CheckBox)sender);
}
Header CheckBox’s KeyUp Event Handler
The KeyUp
event gets fired whenever we release a key from the header CheckBox
if it has focus. Here also, the HeaderCheckBoxClick
method is invoked by passing the header CheckBox
’s reference as an argument, provided this event is raised by the space bar.
private void HeaderCheckBox_KeyUp(object sender, KeyEventArgs e)
{
if(e.KeyCode == Keys.Space)
HeaderCheckBoxClick((CheckBox)sender);
}
Attaching the DataGridView’s CellValueChanged, CurrentCellDirtyStateChanged and CellPainting Events
I've wired up the DataGridView
’s CellValueChanged
, CurrentCellDirtyStateChanged
, and CellPainting
events, respectively, through the Windows Form’s Load
event, as:
private void frmSelectAll_Load(object sender, EventArgs e)
{
...
dgvSelectAll.CellValueChanged +=
new DataGridViewCellEventHandler(dgvSelectAll_CellValueChanged);
dgvSelectAll.CurrentCellDirtyStateChanged +=
new EventHandler(dgvSelectAll_CurrentCellDirtyStateChanged);
dgvSelectAll.CellPainting +=
new DataGridViewCellPaintingEventHandler(dgvSelectAll_CellPainting);
...
}
DataGridView’s CellValueChanged Event Handler
The CellValueChanged
event gets fired whenever the value of a DataGridView
cell is changed. Here, the RowCheckBoxClick
method is invoked by passing a reference of DataGridViewCheckBoxCell
that raised this event, provided the header CheckBox
isn't clicked. I'll describe the RowCheckBoxClick
method later.
private void dgvSelectAll_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (!IsHeaderCheckBoxClicked)
RowCheckBoxClick((DataGridViewCheckBoxCell)dgvSelectAll[e.ColumnIndex, e.RowIndex]);
}
DataGridView’s CellPainting Event Handler
The CellPainting
event gets fired whenever a DataGridView
cell needs to be drawn. Basically, I've handled this event to reset the position of the header CheckBox
inside the DataGridView
whenever it is required. Here, first it is ensured that the cell to be drawn is the header of the first column; then, the ResetHeaderCheckBoxLocation
method is invoked by passing e.ColumnIndex
and e.RowIndex
as arguments. I'll discuss the details of the ResetHeaderCheckBoxLocation
method later.
private void dgvSelectAll_CellPainting(object sender,
DataGridViewCellPaintingEventArgs e)
{
if (e.RowIndex == -1 && e.ColumnIndex == 0)
ResetHeaderCheckBoxLocation(e.ColumnIndex, e.RowIndex);
}
DataGridView’s CurrentCellDirtyStateChanged Event Handler
The CurrentCellDirtyStateChanged
event gets fired whenever the state of a DataGridView
cell changes in relation to a change in its contents. Basically, this event calls the CommitEdit
method to raise the CellValueChanged
event and determine the current value of a DataGridViewCheckBoxCell
.
private void dgvSelectAll_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (dgvSelectAll.CurrentCell is DataGridViewCheckBoxCell)
dgvSelectAll.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
AddHeaderCheckBox Method
The AddHeaderCheckBox
method is used to add a header CheckBox
control in the DataGridView
control. Basically, this method doesn't add a CheckBox
control in a particular DataGridView
column; it simply adds a header CheckBox
control in the DataGridView
. This is the job of the ResetLocation
method.
private void AddHeaderCheckBox()
{
HeaderCheckBox = new CheckBox();
HeaderCheckBox.Size = new Size(15, 15);
this.dgvSelectAll.Controls.Add(HeaderCheckBox);
}
ResetLocation Method
This method is responsible for setting the header CheckBox
location in a particular DataGridView
column. In this method, first, I get cell boundaries of a particular header cell, and then calculate the coordinates for the header CheckBox
in order to change the location to make it stay on the header of a particular DataGridview
column. Finally, I set the location of the header CheckBox
in the DataGridview
.
private void ResetLocation(int ColumnIndex, int RowIndex)
{
Rectangle oRectangle =
this.dgvSelectAll.GetCellDisplayRectangle(ColumnIndex, RowIndex, true);
Point oPoint = new Point();
oPoint.X = oRectangle.Location.X + (oRectangle.Width - HeaderCheckBox.Width) / 2 + 1;
oPoint.Y = oRectangle.Location.Y + (oRectangle.Height - HeaderCheckBox.Height) / 2 + 1;
HeaderCheckBox.Location = oPoint;
}
HeaderCheckBoxClick Method
This method is used to toggle the state of all row CheckBox
es of a particular DataGridView
column depending on the header CheckBox
’s state. Before starting this process, I make the global variable IsHeaderCheckBoxClicked
equal to false
indicating that the state of the row CheckBox
es is just going to be toggled. Each time the state of a row CheckBox
is changed, the DataGridView
’s CellValueChanged
event is fired. In this event, the RowCheckBoxClick
method is invoked if the value of IsHeaderCheckBoxClicked
is found to be false
. Now, after toggling the row CheckBox
es' states, I call the DataGridView
’s RefreshEdit
method in order to refresh the value of the current cell with the underlying cell value when the cell is in edit mode, discarding any previous value. Next, the value of the global variable TotalCheckedCheckBoxes
is set as per the header CheckBox
’s state. Finally, I revert back the value of the global variable IsHeaderCheckBoxClicked
.
private void HeaderCheckBoxClick(CheckBox HCheckBox)
{
IsHeaderCheckBoxClicked = true;
foreach (DataGridViewRow Row in dgvSelectAll.Rows)
((DataGridViewCheckBoxCell)Row.Cells["chkBxSelect"]).Value = HCheckBox.Checked;
dgvSelectAll.RefreshEdit();
TotalCheckedCheckBoxes = HCheckBox.Checked ? TotalCheckBoxes : 0;
IsHeaderCheckBoxClicked = false;
}
RowCheckBoxClick Method
This method checks / unchecks the header CheckBox
’s state depending upon whether all CheckBox
es of a DataGridView
column are checked or unchecked.
private void RowCheckBoxClick(DataGridViewCheckBoxCell RCheckBox)
{
if (RCheckBox != null)
{
if ((bool)RCheckBox.Value && TotalCheckedCheckBoxes < TotalCheckBoxes)
TotalCheckedCheckBoxes++;
else if (TotalCheckedCheckBoxes > 0)
TotalCheckedCheckBoxes--;
if (TotalCheckedCheckBoxes < TotalCheckBoxes)
HeaderCheckBox.Checked = false;
else if (TotalCheckedCheckBoxes == TotalCheckBoxes)
HeaderCheckBox.Checked = true;
}
}
BindGridView Method
This method is used to bind the DataGridView
as well as to initialize the global variables TotalCheckBoxes
and TotalCheckedCheckBoxes
.
private void BindGridView()
{
dgvSelectAll.DataSource = GetDataSource();
TotalCheckBoxes = dgvSelectAll.RowCount;
TotalCheckedCheckBoxes = 0;
}
Winding Up
This is the path that I've adopted to implement this functionality. If anyone has a different idea or suggestion to improve this functionality further, share it with me. I've created and tested this demo application on a machine having VS 2008 and Win XP SP3.
History
- 1st October, 2009 -- Article updated (Modified Introduction section)
- 25th September, 2009 -- Article updated (Added table of contents)
- 18th September, 2009 -- Original version posted