I\'m developing a window application in C# VS2005. I have a dataGridView in which the first column has Checkboxes. Now i want the Column header also to be a CheckBox which i
If you choose @Ehsan solution you must know that if the checkbox cell is selected it won't update the checkbox UI. You need to call RefreshEdit() afterwards.
It only happens when the control is child-control of the datagridview. For some reason when it is a child-control the selected cell checkboxes are not able to refresh the UI by their own.
I also needed to have a CheckBox
in the column header of a DataGridView
column. Here's how I did it:
DataGridViewColumnHeaderCell
System.Windows.Forms.CheckBox
to store the checked state and provide the OS styled visual CheckBox
representation.Bitmap
as a buffer and draw the regular CheckBox
on it (using CheckBox.DrawToBitmap
)DataGridViewColumnHeaderCell.Paint
and, if necessary, update the buffer before drawing the buffer to the Graphics
supplied by Paint
DataGridViewColumnHeaderCell
, and also a CheckedChanged eventDataGridViewColumnHeaderCell
in the column's HeaderCell
when the DataGridView
is being populated.CheckBox
when it is the column header is clicked, only if the mouse click is within the bounds of the CheckBox
CheckedChanged
event, updating the underlying data object and then calling ResetBindings
to update the DataGridView
Here's the class I wrote which is derived from DataGridViewColumnHeaderCell
:
class DataGridViewCheckBoxColumnHeaderCell : DataGridViewColumnHeaderCell
{
private Bitmap buffer;
private CheckBox checkBox;
private Rectangle checkBoxBounds;
public DataGridViewCheckBoxColumnHeaderCell()
{
this.checkBox = new CheckBox();
}
public event EventHandler CheckedChanged;
public bool Checked
{
get
{
return this.checkBox.Checked;
}
set
{
if (!this.Checked == value)
{
this.checkBox.Checked = value;
if (this.buffer != null)
{
this.buffer.Dispose();
this.buffer = null;
}
this.OnCheckedChanged(EventArgs.Empty);
if (this.DataGridView != null)
{
this.DataGridView.Refresh();
}
}
}
}
protected override void Paint(
Graphics graphics,
Rectangle clipBounds,
Rectangle cellBounds,
int rowIndex,
DataGridViewElementStates dataGridViewElementState,
object value,
object formattedValue,
string errorText,
DataGridViewCellStyle cellStyle,
DataGridViewAdvancedBorderStyle advancedBorderStyle,
DataGridViewPaintParts paintParts)
{
// Passing String.Empty in place of
// value and formattedValue prevents
// this header cell from having text.
base.Paint(
graphics,
clipBounds,
cellBounds,
rowIndex,
dataGridViewElementState,
String.Empty,
String.Empty,
errorText,
cellStyle,
advancedBorderStyle,
paintParts);
if (this.buffer == null
|| cellBounds.Width != this.buffer.Width
|| cellBounds.Height != this.buffer.Height)
{
this.UpdateBuffer(cellBounds.Size);
}
graphics.DrawImage(this.buffer, cellBounds.Location);
}
protected override Size GetPreferredSize(
Graphics graphics,
DataGridViewCellStyle cellStyle,
int rowIndex,
Size constraintSize)
{
return this.checkBox.GetPreferredSize(constraintSize);
}
protected override void OnMouseClick(DataGridViewCellMouseEventArgs e)
{
if (e.Button == MouseButtons.Left
&& this.checkBoxBounds.Contains(e.Location))
{
this.Checked = !this.Checked;
}
base.OnMouseClick(e);
}
private void UpdateBuffer(Size size)
{
Bitmap updatedBuffer = new Bitmap(size.Width, size.Height);
this.checkBox.Size = size;
if (this.checkBox.Size.Width > 0 && this.checkBox.Size.Height > 0)
{
Bitmap renderedCheckbox = new Bitmap(
this.checkBox.Width,
this.checkBox.Height);
this.checkBox.DrawToBitmap(
renderedCheckbox,
new Rectangle(new Point(), this.checkBox.Size));
MakeTransparent(renderedCheckbox, this.checkBox.BackColor);
Bitmap croppedRenderedCheckbox = AutoCrop(
renderedCheckbox,
Color.Transparent);
// TODO implement alignment, right now it is always
// MiddleCenter regardless of this.Style.Alignment
this.checkBox.Location = new Point(
(updatedBuffer.Width - croppedRenderedCheckbox.Width) / 2,
(updatedBuffer.Height - croppedRenderedCheckbox.Height) / 2);
Graphics updatedBufferGraphics = Graphics.FromImage(updatedBuffer);
updatedBufferGraphics.DrawImage(
croppedRenderedCheckbox,
this.checkBox.Location);
this.checkBoxBounds = new Rectangle(
this.checkBox.Location,
croppedRenderedCheckbox.Size);
renderedCheckbox.Dispose();
croppedRenderedCheckbox.Dispose();
}
if (this.buffer != null)
{
this.buffer.Dispose();
}
this.buffer = updatedBuffer;
}
protected virtual void OnCheckedChanged(EventArgs e)
{
EventHandler handler = this.CheckedChanged;
if (handler != null)
{
handler(this, e);
}
}
// The methods below are helper methods for manipulating Bitmaps
private static void MakeTransparent(Bitmap bitmap, Color transparencyMask)
{
int transparencyMaskArgb = transparencyMask.ToArgb();
int transparentArgb = Color.Transparent.ToArgb();
List deadColumns = new List();
for (int x = 0; x = 0; x--)
{
if (deadColumns.Count == bitmap.Height)
{
break;
}
for (int y = bitmap.Height - 1; y >= 0; y--)
{
if (deadColumns.Contains(y))
{
continue;
}
int pixel = bitmap.GetPixel(x, y).ToArgb();
if (pixel == transparencyMaskArgb)
{
bitmap.SetPixel(x, y, Color.Transparent);
}
else if (pixel != transparentArgb)
{
deadColumns.Add(y);
break;
}
}
}
}
public static Bitmap AutoCrop(Bitmap bitmap, Color backgroundColor)
{
Size croppedSize = bitmap.Size;
Point cropOrigin = new Point();
int backgroundColorToArgb = backgroundColor.ToArgb();
for (int x = bitmap.Width - 1; x >= 0; x--)
{
bool allPixelsAreBackgroundColor = true;
for (int y = bitmap.Height - 1; y >= 0; y--)
{
if (bitmap.GetPixel(x, y).ToArgb() != backgroundColorToArgb)
{
allPixelsAreBackgroundColor = false;
break;
}
}
if (allPixelsAreBackgroundColor)
{
croppedSize.Width--;
}
else
{
break;
}
}
for (int y = bitmap.Height - 1; y >= 0; y--)
{
bool allPixelsAreBackgroundColor = true;
for (int x = bitmap.Width - 1; x >= 0; x--)
{
if (bitmap.GetPixel(x, y).ToArgb() != backgroundColorToArgb)
{
allPixelsAreBackgroundColor = false;
break;
}
}
if (allPixelsAreBackgroundColor)
{
croppedSize.Height--;
}
else
{
break;
}
}
for (int x = 0; x = 0 && xWhole = 0)
{
bitmapSection.SetPixel(x, y, bitmap.GetPixel(xWhole, yWhole));
}
else
{
bitmapSection.SetPixel(x, y, Color.Transparent);
}
}
}
return bitmapSection;
}
}
So - to address the top-most checkbox not showing as checked (even though it is), I ended up editing your event as follows:
private void checkboxHeader_CheckedChanged(object sender, EventArgs e) { //CheckBox headerBox = ((CheckBox)dtgv1.Controls.Find("checkboxHeader", true)[0]);
var headerBox = (CheckBox)sender;
var b = headerBox.Checked;
var c = int.Parse(headerBox.Name.Replace("checkboxHeader", ""));
for (int i = 0; i < dgvSources_fuzzyID.RowCount; i++)
{
dgvSources_fuzzyID.Rows[i].Cells[0].Value = headerBox.Checked;
***dgvSources_fuzzyID.RefreshEdit();***
}
}
That is what finally fixed it for me...
Thanks Ehsan for the easy way, works fine for me. For the problem only one CheckBox is always at the upper left corner, I add a name suffix to each one and solved. In addition, I would like to slightly modify the event handling method by directly cast the object sender since it was very specific up on the creation (unless this method is used to handling other kind of controls' event either).
private string[] _colLst = columNameArray;
private void AddCheckBoxGridViewHeader()
{
for (int ndx = 0; ndx < _colLst.Length; ndx++)
{
var rect = dtgv1.GetCellDisplayRectangle(ndx, -1, true);
var x = rect.X + (rect.Width * 4 / 5);
var y = 3;
Rectangle nrect = new Rectangle(x, y, rect.Width, rect.Height);
CheckBox checkboxHeader = new CheckBox();
checkboxHeader.BackColor = Color.Transparent;
checkboxHeader.Name = "checkboxHeader" + ndx;
checkboxHeader.Size = new Size(18, 18);
checkboxHeader.Location = nrect.Location;
checkboxHeader.CheckedChanged += new EventHandler(checkboxHeader_CheckedChanged);
dtgv1.Controls.Add(checkboxHeader);
}
}
private void checkboxHeader_CheckedChanged(object sender, EventArgs e)
{
//CheckBox headerBox = ((CheckBox)dtgv1.Controls.Find("checkboxHeader", true)[0]);
var headerBox = (CheckBox)sender;
var b = headerBox.Checked;
var c = int.Parse(headerBox.Name.Replace("checkboxHeader", ""));
for (int i = 0; i < dtgv1.RowCount; i++)
{
dtgv1.Rows[i].Cells[c].Style = new DataGridViewCellStyle();
dtgv1.Rows[i].Cells[c].Style.BackColor = (b)? Color.Salmon : Color.White;
}
}
The above solution is kind of good, but there is also an easier way! Simply add these two methods and then you would have what you want!
First add a show_chkBox
method to your code and call it in the onload
function of your form or after creating your DataGridView
:
private void show_chkBox()
{
Rectangle rect = dataGridView1.GetCellDisplayRectangle(0, -1, true);
// set checkbox header to center of header cell. +1 pixel to position
rect.Y = 3;
rect.X = rect.Location.X + (rect.Width/4);
CheckBox checkboxHeader = new CheckBox();
checkboxHeader.Name = "checkboxHeader";
//datagridview[0, 0].ToolTipText = "sdfsdf";
checkboxHeader.Size = new Size(18, 18);
checkboxHeader.Location = rect.Location;
checkboxHeader.CheckedChanged += new EventHandler(checkboxHeader_CheckedChanged);
dataGridView1.Controls.Add(checkboxHeader);
}
and then you would have the checkbox in the header.
For the selecting problem, just add this code:
private void checkboxHeader_CheckedChanged(object sender, EventArgs e)
{
CheckBox headerBox = ((CheckBox)dataGridView1.Controls.Find("checkboxHeader", true)[0]);
int index = 0;
for (int i = 0; i < dataGridView1.RowCount; i++)
{
dataGridView1.Rows[i].Cells[0].Value = headerBox.Checked;
}
}
Following is the solution that worked for me as Ehsaan's solution was not checking the first row in the DataGridView
.
Call the AddChkBoxHeader_DataGridView()
method in OnLoad
function of your form.
CheckBox checkboxHeader = null;
bool isHeaderCheckBoxClicked = false;
private void AddChkBoxHeader_DataGridView()
{
Rectangle rect = dgvRecipeSelector.GetCellDisplayRectangle(0, -1, true);
rect.Y = 10;
rect.X = rect.Location.X + (rect.Width / 4);
checkboxHeader = new CheckBox();
checkboxHeader.Size = new Size(15,15);
checkboxHeader.Location = rect.Location;
dgvRecipeSelector.Controls.Add(checkboxHeader);
checkboxHeader.MouseClick += new MouseEventHandler(checkboxHeader_CheckedChanged);
}
private void checkboxHeader_CheckedChanged(object sender, EventArgs e)
{
HeaderCheckBoxClick((CheckBox)sender);
}
private void HeaderCheckBoxClick(CheckBox headerCheckbox)
{
isHeaderCheckBoxClicked = true;
foreach (DataGridViewRow r in dgvRecipeSelector.Rows)
{
((DataGridViewCheckBoxCell)r.Cells[0]).Value = headerCheckbox.Checked;
}
dgvRecipeSelector.RefreshEdit();
isHeaderCheckBoxClicked = false;
}