C# 如何将复选框作为 dataGridView 的 ColumnHeader
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/868654/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to have Checkbox as ColumnHeader of dataGridView
提问by SyncMaster
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 if i select all the Checkboxex in the column should get selected. How can i do this.? I referred the Code Project link
我正在 C# VS2005 中开发一个窗口应用程序。我有一个 dataGridView,其中第一列有复选框。现在我希望列标题也是一个复选框,如果我选择列中的所有 Checkboxex 应该被选中。我怎样才能做到这一点。?我提到了代码项目链接
But if i use that, if i click the FirstCell (not the Header)all the below cells are getting selected. But i want a CheckBox in the Column header. How can i do this.?
但是,如果我使用它,如果我单击FirstCell(而不是标题),则会选择以下所有单元格。但我想在列标题中有一个复选框。我怎样才能做到这一点。?
采纳答案by Zach Johnson
I also needed to have a CheckBox
in the column header of a DataGridView
column. Here's how I did it:
我还需要CheckBox
在列的列标题中有一个DataGridView
。这是我如何做到的:
- Create a class which inherits from
DataGridViewColumnHeaderCell
- Internally use a
System.Windows.Forms.CheckBox
to store the checked state and provide the OS styled visualCheckBox
representation. - Use a
Bitmap
as a buffer and draw the regularCheckBox
on it (usingCheckBox.DrawToBitmap
) - Override
DataGridViewColumnHeaderCell.Paint
and, if necessary, update the buffer before drawing the buffer to theGraphics
supplied byPaint
- Provide a Checked property on the derived
DataGridViewColumnHeaderCell
, and also a CheckedChanged event - Substitute the derived
DataGridViewColumnHeaderCell
in the column'sHeaderCell
when theDataGridView
is being populated. - Check and uncheck the
CheckBox
when it is the column header is clicked, only if the mouse click is within the bounds of theCheckBox
- Implement the check-all/uncheck-all outside of the derived class by listening to the
CheckedChanged
event, updating the underlying data object and then callingResetBindings
to update theDataGridView
- 创建一个继承自的类
DataGridViewColumnHeaderCell
- 在内部使用 a
System.Windows.Forms.CheckBox
来存储选中的状态并提供操作系统风格的视觉CheckBox
表示。 - 使用 a
Bitmap
作为缓冲区并CheckBox
在其上绘制正则(使用CheckBox.DrawToBitmap
) - 覆盖
DataGridViewColumnHeaderCell.Paint
并在必要时更新缓冲区,然后再将缓冲区绘制到Graphics
由Paint
- 在派生的上提供 Checked 属性
DataGridViewColumnHeaderCell
,以及 CheckedChanged 事件 - 填充时替换
DataGridViewColumnHeaderCell
列中的派生。HeaderCell
DataGridView
- 勾选和取消勾选
CheckBox
当点击列标题时,仅当鼠标点击在列标题的范围内时CheckBox
- 在派生类之外通过监听
CheckedChanged
事件,更新底层数据对象,然后调用ResetBindings
更新DataGridView
Here's the class I wrote which is derived from DataGridViewColumnHeaderCell
:
这是我编写的类,它源自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;
}
}
回答by Ehsan
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
:
首先show_chkBox
向您的代码添加一个方法,并在onload
您的表单函数中或在创建您的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;
}
}
回答by Ana
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.
如果您选择@Ehsan 解决方案,您必须知道,如果选中复选框单元格,它不会更新复选框 UI。之后您需要调用 RefreshEdit()。
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.
它仅在控件是 datagridview 的子控件时发生。出于某种原因,当它是子控件时,选定的单元格复选框无法自行刷新 UI。
回答by user1779049
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).
感谢 Ehsan 的简单方法,对我来说很好用。对于左上角总是只有一个CheckBox的问题,我给每一个加了一个名字后缀就解决了。此外,我想通过直接强制转换对象发送者来稍微修改事件处理方法,因为它在创建时非常具体(除非此方法用于处理其他类型的控件事件)。
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;
}
}
回答by ekotek
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]);
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...
这就是最终为我解决的问题......
回答by jo_Veera
Following is the solution that worked for me as Ehsaan's solution was not checking the first row in the DataGridView
.
以下是对我有用的解决方案,因为 Ehsaan 的解决方案没有检查DataGridView
.
Call the AddChkBoxHeader_DataGridView()
method in OnLoad
function of your form.
AddChkBoxHeader_DataGridView()
在OnLoad
您的表单函数中调用该方法。
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;
}