Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

Toggling the States of all CheckBoxes Inside a DataGridView Column

4.96/5 (72 votes)
2 Oct 2009CPOL5 min read 316.4K   20.3K  
This article describes how to toggle the states of all CheckBoxes inside a particular DataGridView column.
Image 1

Table of Contents

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:

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

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

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

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

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

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

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

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

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

C#
private void AddHeaderCheckBox()
{
    HeaderCheckBox = new CheckBox();

    HeaderCheckBox.Size = new Size(15, 15);

    //Add the CheckBox into the DataGridView
    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.

C#
private void ResetLocation(int ColumnIndex, int RowIndex)
{
   //Get the column header cell bounds
   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;

   //Change the location of the CheckBox to make it stay on the header
   HeaderCheckBox.Location = oPoint;
}

HeaderCheckBoxClick Method

This method is used to toggle the state of all row CheckBoxes 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 CheckBoxes 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 CheckBoxes' 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.

C#
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 CheckBoxes of a DataGridView column are checked or unchecked.

C#
private void RowCheckBoxClick(DataGridViewCheckBoxCell RCheckBox)
{
   if (RCheckBox != null)
   {
      //Modify Counter;            
      if ((bool)RCheckBox.Value && TotalCheckedCheckBoxes < TotalCheckBoxes)
         TotalCheckedCheckBoxes++;
      else if (TotalCheckedCheckBoxes > 0)
         TotalCheckedCheckBoxes--;

      //Change state of the header CheckBox.
      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.

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

License

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