Thursday, February 14, 2008

Datagridview Checkbox Select All

Though .Net allows to include Checkboxes in a datagridview,selection of all the checkboxes in a single column at once is always a tough task.I found it very tough initially.

Today I shall help you include a top Checkbox,clicking on which selection of all the checkboxes under that is possible.

public delegate void CheckBoxClickedHandler(bool state);
public class DataGridViewCheckBoxHeaderCellEventArgs : EventArgs
{
bool _bChecked;
public DataGridViewCheckBoxHeaderCellEventArgs(bool bChecked)
{
_bChecked = bChecked;
}
public bool Checked
{
get { return _bChecked; }
}
}
class DatagridViewCheckBoxHeaderCell : DataGridViewColumnHeaderCell
{
Point checkBoxLocation;
Size checkBoxSize;
bool _checked = false;
Point _cellLocation = new Point();
System.Windows.Forms.VisualStyles.CheckBoxState _cbState =
System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;
public event CheckBoxClickedHandler OnCheckBoxClicked;

public DatagridViewCheckBoxHeaderCell()
{
}

protected override void Paint(System.Drawing.Graphics graphics,
System.Drawing.Rectangle clipBounds,
System.Drawing.Rectangle cellBounds,
int rowIndex,
DataGridViewElementStates dataGridViewElementState,
object value,
object formattedValue,
string errorText,
DataGridViewCellStyle cellStyle,
DataGridViewAdvancedBorderStyle advancedBorderStyle,
DataGridViewPaintParts paintParts)
{
base.Paint(graphics, clipBounds, cellBounds, rowIndex,
dataGridViewElementState, value,
formattedValue, errorText, cellStyle,
advancedBorderStyle, paintParts);
Point p = new Point();
Size s = CheckBoxRenderer.GetGlyphSize(graphics,
System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal);
p.X = cellBounds.Location.X +
(cellBounds.Width / 2) - (s.Width / 2);
p.Y = cellBounds.Location.Y +
(cellBounds.Height / 2) - (s.Height / 2);
_cellLocation = cellBounds.Location;
checkBoxLocation = p;
checkBoxSize = s;
if (_checked)
_cbState = System.Windows.Forms.VisualStyles.
CheckBoxState.CheckedNormal;
else
_cbState = System.Windows.Forms.VisualStyles.
CheckBoxState.UncheckedNormal;
CheckBoxRenderer.DrawCheckBox
(graphics, checkBoxLocation, _cbState);
}

protected override void OnMouseClick(DataGridViewCellMouseEventArgs e)
{
Point p = new Point(e.X + _cellLocation.X, e.Y + _cellLocation.Y);
if (p.X >= checkBoxLocation.X && p.X <=
checkBoxLocation.X + checkBoxSize.Width
&& p.Y >= checkBoxLocation.Y && p.Y <=
checkBoxLocation.Y + checkBoxSize.Height)
{
_checked = !_checked;
if (OnCheckBoxClicked != null)
{
OnCheckBoxClicked(_checked);
this.DataGridView.InvalidateCell(this);
}
}
base.OnMouseClick(e);
}
}

This class needs to be included to the project to get the Header Checkbox feature.

Then the checkbox need to be added to the datagridview as a DatagridViewCheckBoxColumn.
Then we assign the header to it.
The header is assigned an event with the help of which we can uncheck or check all the checkboxes.

The code to add the checkbox to header and the corresponding event code is given below.

private void FormatGrid()
{
DataView dv = new DataView();
dv.Table = _loginDs.Tables[0];

DataGridViewCheckBoxColumn chkbox = new DataGridViewCheckBoxColumn();
DatagridViewCheckBoxHeaderCell chkHeader = new DatagridViewCheckBoxHeaderCell();
chkbox.HeaderCell = chkHeader;
chkHeader.OnCheckBoxClicked += new CheckBoxClickedHandler(chkHeader_OnCheckBoxClicked);
_chkBoxGrid.Columns.Add(chkbox);

DataGridViewTextBoxColumn uname = new DataGridViewTextBoxColumn();
uname.HeaderText = "user";
uname.Name = "username";
uname.DataPropertyName = "username";
_chkBoxGrid.Columns.Add(uname);

_chkBoxGrid.DataSource = dv;
}

void chkHeader_OnCheckBoxClicked(bool state)
{
foreach (DataGridViewRow row in _chkBoxGrid.Rows)
row.Cells[0].Value = state;
}

In the checkbox header clicked event,any conditions might be added to select only the needed rows..

I hope you'll have a good time working with the new header for checkboxes. :)

32 comments:

Anonymous said...

Thanks very much for your posting. I found this one on MSDN:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1197832&SiteID=1

I'll try yours first.
--kaborka

Anonymous said...

Your code worked PERFECTLY! Well done! You should post it on dodeproject.com or sourceforge.net.

Thank you very much.
--kaborka

Tulika said...

I implemented this and it worked. Except that the first cell doesn't get selected. Do you have any idea why?

Salu said...

Hi Tulika,

The first checkbox is actually selected.It wont be seen as the selection will also be in the same cell.Just click on some other cell you can see that it is selected.

Anonymous said...

I had the same problem as Tulika. I added this to the OnCheckBoxClicked method:

_chkBoxGrid.BeginEdit(true);
if(_chkBoxGrid.Rows[i].Cells[_chkBoxGrid.Columns.Count - 1].Selected)
{ _chkBoxGrid.Rows[i].Cells[_chkBoxGrid.Columns.Count - 1].Selected = false;
} _chkBoxGrid.Rows[i].Cells[_chkBoxGrid.Columns.Count - 1].Value = state; _chkBoxGrid.EndEdit();

Then all went well.

Salu said...

Thanks for the new addition in my code...I dint find time to test it yet as I am busy with something else.But I am sure it will work well...

Anonymous said...

maybe for somebody will be usefull, some manipulations for settings width:
private void MainForm_Shown(object sender, EventArgs e)
{
//для подгонки ширины столбца с чекбоксами по содержимому выполняю следующий финт ушами:
myDataGridView.Columns["Checked"].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCellsExceptHeader;
myDataGridView.Columns["Checked"].Width = myDataGridView.Columns["Checked"].Width;
myDataGridView.Columns["Checked"].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
}

Rodrigo Andres Prieto said...

The code works perfectly, but now I cannot check each row individually. I can have all checked, or none. I want the possibility to check or uncheck each row individually too.

Thanks!

--Rodrigo

Vimal Raj said...

Hi Rodrigo,

Something must have gone wrong with your code. It is possible to check/uncheck individual rows with the code provided in the blog. Please post your code so that we can help you out.

Thanks,
Vimal Raj

Anonymous said...

Hi there,

thumbs up for this code! :)

i also improved the eventargs so it can be used more like the other datagrid events:

public delegate void CheckBoxClickedHandler(object sender, DataGridViewCheckBoxHeaderCellEventArgs e);
public class DataGridViewCheckBoxHeaderCellEventArgs : EventArgs
{

public DataGridViewCheckBoxHeaderCellEventArgs(bool bChecked, int index)
{
Checked = bChecked;
ColumnIndex = index;
}

public bool Checked { get; private set; }

public int ColumnIndex { get; private set; }

}

Vimal Raj said...

Hi KeksImperium,

Thanks for sharing your code.

Happy Programming,
Vimal Raj

Ryudo Blaze said...

how do you include the class to your project if you already have a code ehind file? I made a c sharp file in my app code directory and put it in there, but is there like a using line or add namespace thing I ahve to do?

any help would be greatly appreciated!

thanks

Vimal Raj said...

Hi Dan,

You can add the class file to AppCode folder and it will be readily available in your other pages. You don't have to add any reference or using statements.

In Visual Studio, you can right click AppCode Folder and select 'Add New Item' and then 'Class'.
The newly created class will be available in other code files. (You can find it listed in intellisense drop down).
Hope this helps,

Happy Programming,
Vimal Raj

Anonymous said...

Hi Salu,

Thanks for your code. It's very useful.

Ann.

Salu said...

My Pleasure... Ann...

Unknown said...

This does not work in case we assign a data property to the column. If I have to assign DataPropertyName then how can achieve the same functionality?

Vimal Raj said...

Hi Jayant,

This code should very well work with DataPropertyName. If you are getting any specific errors, Please let me know.

Thanks,
Vimal Raj

Anonymous said...

Thanks for the code.
I translated this into VB which i am writting in and got an error with the class, here goes:

Protected Overloads Overrides Sub OnMouseClick(ByVal e As DataGridViewCellMouseEventArgs)
Dim p As New Point(e.X + _cellLocation.X, e.Y + _cellLocation.Y)
If p.X >= checkBoxLocation.X AndAlso p.X <= checkBoxLocation.X + checkBoxSize.Width AndAlso p.Y >= checkBoxLocation.Y AndAlso p.Y <= checkBoxLocation.Y + checkBoxSize.Height Then
_checked = Not _checked
If OnCheckBoxClicked IsNot Nothing Then
RaiseEvent OnCheckBoxClicked(_checked)
Me.DataGridView.InvalidateCell(Me)
End If
End If
MyBase.OnMouseClick(e)
End Sub

The "If OnCheckBoxClicked IsNot Nothing Then" line says it is an event and can not be called directly. You may notice I changed the line below it to "RaiseEvent..." in order to get rid of the error on that line. However, the same will not do for the condition testing line. Any ideas how I resolve this error?
Thanks

Anonymous said...

I too have converted it to VB code, but i am not getting a checkbox at the columnheader that allows the user to select all.

Sudhir said...

This is simple Code
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
c1 = new DataGridViewCheckBoxColumn();
c1.Name = "selection";
c1.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
this.dataGridView1.Columns.Add(c1);

this.dataGridView1.Rows.Add();
this.dataGridView1.Rows.Add();
this.dataGridView1.Rows.Add();
this.dataGridView1.Rows.Add();

ckBox = new CheckBox();
//Get the column header cell bounds
Rectangle rect =
this.dataGridView1.GetCellDisplayRectangle(0, -1, true);
ckBox.Size = new Size(18, 18);
//Change the location of the CheckBox to make it stay on the header
ckBox.Location = rect.Location;
ckBox.CheckedChanged += new EventHandler(ckBox_CheckedChanged);
//Add the CheckBox into the DataGridView
this.dataGridView1.Controls.Add(ckBox);
}

DataGridViewCheckBoxColumn c1;
CheckBox ckBox;

void ckBox_CheckedChanged(object sender, EventArgs e)
{
for (int j = 0; j < this.dataGridView1.RowCount; j++)
{
this.dataGridView1[0, j].Value = this.ckBox.Checked;
}
this.dataGridView1.EndEdit();
}
}

Unknown said...

Lots of Thanks ,
for sharing your knowledge

It will be useful for many in need.

Salu said...

Hi Anna,

Its my pleasure to share my knowledge with the community...

keep visiting Techisolutions.info

Anonymous said...

For just selection, this works just fine:

for (int i=0;i < dgItems.Rows.Count;i++)
{
if (this.dgItems.Rows[i].Cells[0].Value == null)
this.dgItems.Rows[i].Cells[0].Value = true;
}

Anonymous said...

Ouch.. That's a lot of code just for that!
Why dont you just use the following:
VB.net code

For i = 0 To datagridview1.Rows.Count - 1
datagridview1.Item(0, i).Value = True


Next

Hoerg said...

Hi there!
I think it would be a good idea to show a Header-text beside the checkbox.

Add this to the Class.
//member for Headertext
string _headerText = string.Empty;

//Constructor
public DatagridViewCheckBoxHeaderCell(string headerText) {_headerText = headerText;}

// with Headertext locate the checkbox on the left
if (_headerText == string.Empty)
{
p.X = cellBounds.Location.X + (cellBounds.Width / 2) - (s.Width / 2);}
else
{
p.X = cellBounds.Location.X + 1;
}
// Paint the HeaderText
if(_headerText != string.Empty)
CheckBoxRenderer.DrawCheckBox(graphics, checkBoxLocation, new Rectangle(p.X + s.Width + 2, p.Y, cellBounds.Width - s.Width - 4, s.Height), _headerText, cellStyle.Font, false, _cbState);
else
CheckBoxRenderer.DrawCheckBox(graphics, checkBoxLocation, _cbState);

Have fun!

Vimal Raj said...

Hi Hoerg,

Thanks for your code.

Happy Programming,
Vimal Raj

Erik K. said...

Some generic (almost) code for
the event handler:

void ckBox_CheckedChanged(object sender, EventArgs e)
{
CheckBox cb = sender as CheckBox;
DataGridView dgv = (DataGridView)cb.Parent;
foreach (DataGridViewRow row in dgv.Rows)
{
row.Cells[0].Value = cb.Checked;
}

}

The only assumption is that the CheckBox column is the first one which it normally is.

Anna said...

Thanks, just what I needed.

Surendra Chaluvadi said...

I want to create a data grid view with check boxes in column headers. how can i create the data grid view using c#.Net 3.5?

Anonymous said...

@all
use this.. this is perfect

http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/827907ea-c529-4254-9b15-2e6d571f5c5b/

Trevor McAlister said...

Setting 'DataPropertyName' stopped the OnCheckBoxClicked event firing.

Hi
I found the cause of this problem & the solution.
When 'DataPropertyName' is set it looks like the custom headerCell is cloned a number of times (possibly on painting the container).
The custom class dose not override the clone method so any handlers registered for the event are not copied to the newly cloned headerCell.
Adding the following solves the problem:
public override object Clone()
{
DatagridViewCheckBoxHeaderCell obj = (DatagridViewCheckBoxHeaderCell)base.Clone();
obj._checkBoxLocation = _checkBoxLocation;
obj._checkBoxSize = _checkBoxSize;
obj._checked = _checked;
obj._cellLocation = _cellLocation;
obj._cbState = _cbState;
obj.OnCheckBoxClicked = OnCheckBoxClicked;
return obj;
}

Hope this helps
Trevor

Anonymous said...

Hello Everyone,
this is great article!!!! thanks for sharing with us!!!! quite entertained, you may check out the following links for simplest one and more details.....

http://mindstick.com/Articles/77b73daa-2307-4de6-91df-7d6fe1094731/?Checkbox%20in%20DataGridView

Thanks !!!