65.9K
CodeProject 正在变化。 阅读更多。
Home

切换DataGridView列中所有复选框的状态

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (66投票s)

2009年9月17日

CPOL

5分钟阅读

viewsIcon

319943

downloadIcon

20304

本文介绍了如何切换特定 DataGridView 列中所有复选框的状态。

目录

引言

一年半前,我写了一篇文章,选择/取消选择 GridView 中的所有复选框,适用于 Web Forms。上个月,我得到了一个机会,为 Windows Forms 的 DataGridView 控件实现相同的功能。最初,我以为它会像 Web Forms 的 GridView 一样简单,但事实并非如此。对于 Windows Forms 的 DataGridView 控件来说,它有点棘手。例如,在 DataGridView 控件的列中添加标题 CheckBox 并不是一项简单的工作。因此,在进行了大量研究并花费大量时间研究控件的功能后,我终于得到了以下解决方案。欢迎所有修改和澄清!

在 DataGridView 列中添加行复选框

要在 DataGridView 列中添加行 CheckBoxes,我添加了一个 DataGridViewCheckBoxColumn 列控件。我还将 DataGridView [dgvSelectAll] 的 AllowUserToAddRowsAllowUserToDeleteRows 属性设置为 false,如下所示:

dgvSelectAll.AllowUserToAddRows = false;
dgvSelectAll.AllowUserToDeleteRows = false; 

在 DataGridView 列中添加标题复选框

正如我之前所说,在 DataGridView 列中添加标题 CheckBox 有点棘手。我通过 Windows Form 的 Load 事件调用 AddHeaderCheckBox 方法,在 DataGridView 列中添加了一个标题 CheckBox [HeaderCheckBox],如下所示:

private void frmSelectAll_Load(object sender, EventArgs e)
{
   AddHeaderCheckBox();
   …
}

我稍后会解释 AddHeaderCheckBox 方法。

附加标题复选框的 MouseClick 和 KeyUp 事件

我通过 Windows Form 的 Load 事件连接了标题 CheckBoxMouseClickKeyUp 事件,如下所示:

private void frmSelectAll_Load(object sender, EventArgs e) 
{
   … 
   HeaderCheckBox.KeyUp += new KeyEventHandler(HeaderCheckBox_KeyUp);
   HeaderCheckBox.MouseClick += new MouseEventHandler(HeaderCheckBox_MouseClick);
   … 
}

标题复选框的 MouseClick 事件处理程序

每当我们点击标题 CheckBox 时,都会触发 MouseClick 事件。在这里,通过将标题 CheckBox 的引用作为参数传递来调用 HeaderCheckBoxClick 方法。我稍后会讨论 HeaderCheckBoxClick 方法。

private void HeaderCheckBox_MouseClick(object sender, MouseEventArgs e) 
{
    HeaderCheckBoxClick((CheckBox)sender); 
}

标题复选框的 KeyUp 事件处理程序

如果标题 CheckBox 获得焦点,每当我们释放标题 CheckBox 上的键时,都会触发 KeyUp 事件。在这里,如果此事件是由空格键引发的,则通过将标题 CheckBox 的引用作为参数来调用 HeaderCheckBoxClick 方法。

private void HeaderCheckBox_KeyUp(object sender, KeyEventArgs e)
{
    if(e.KeyCode == Keys.Space)
       HeaderCheckBoxClick((CheckBox)sender);
}

附加 DataGridView 的 CellValueChanged、CurrentCellDirtyStateChanged 和 CellPainting 事件

我通过 Windows Form 的 Load 事件分别连接了 DataGridViewCellValueChangedCurrentCellDirtyStateChangedCellPainting 事件,如下所示:

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 的 CellValueChanged 事件处理程序

每当 DataGridView 单元格的值发生变化时,都会触发 CellValueChanged 事件。在这里,如果未点击标题 CheckBox,则通过传递引发此事件的 DataGridViewCheckBoxCell 的引用来调用 RowCheckBoxClick 方法。我稍后会描述 RowCheckBoxClick 方法。

private void dgvSelectAll_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
   if (!IsHeaderCheckBoxClicked)
      RowCheckBoxClick((DataGridViewCheckBoxCell)dgvSelectAll[e.ColumnIndex, e.RowIndex]);
}

DataGridView 的 CellPainting 事件处理程序

每当需要绘制 DataGridView 单元格时,都会触发 CellPainting 事件。基本上,我处理此事件是为了在需要时重置标题 CheckBoxDataGridView 中的位置。在这里,首先确保要绘制的单元格是第一列的标题;然后,通过传递 e.ColumnIndexe.RowIndex 作为参数来调用 ResetHeaderCheckBoxLocation 方法。我稍后会讨论 ResetHeaderCheckBoxLocation 方法的详细信息。

private void dgvSelectAll_CellPainting(object sender, 
             DataGridViewCellPaintingEventArgs e)
{
   if (e.RowIndex == -1 && e.ColumnIndex == 0)
      ResetHeaderCheckBoxLocation(e.ColumnIndex, e.RowIndex);
}

DataGridView 的 CurrentCellDirtyStateChanged 事件处理程序

每当 DataGridView 单元格的状态因其内容的变化而变化时,都会触发 CurrentCellDirtyStateChanged 事件。基本上,此事件调用 CommitEdit 方法以引发 CellValueChanged 事件并确定 DataGridViewCheckBoxCell 的当前值。

private void dgvSelectAll_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    if (dgvSelectAll.CurrentCell is DataGridViewCheckBoxCell)
       dgvSelectAll.CommitEdit(DataGridViewDataErrorContexts.Commit);
}

AddHeaderCheckBox 方法

AddHeaderCheckBox 方法用于在 DataGridView 控件中添加标题 CheckBox 控件。基本上,此方法不会在特定的 DataGridView 列中添加 CheckBox 控件;它只是在 DataGridView 中添加一个标题 CheckBox 控件。这是 ResetLocation 方法的工作。

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

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

    //Add the CheckBox into the DataGridView
    this.dgvSelectAll.Controls.Add(HeaderCheckBox);
}

ResetLocation 方法

此方法负责设置标题 CheckBox 在特定 DataGridView 列中的位置。在此方法中,我首先获取特定标题单元格的单元格边界,然后计算标题 CheckBox 的坐标,以更改其位置,使其停留在特定 DataGridview 列的标题上。最后,我设置标题 CheckBoxDataGridview 中的位置。

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 方法

此方法用于根据标题 CheckBox 的状态切换特定 DataGridView 列的所有行 CheckBox 的状态。在开始此过程之前,我将全局变量 IsHeaderCheckBoxClicked 设置为 false,表示行 CheckBox 的状态即将被切换。每次行 CheckBox 的状态改变时,都会触发 DataGridViewCellValueChanged 事件。在此事件中,如果 IsHeaderCheckBoxClicked 的值被发现为 false,则会调用 RowCheckBoxClick 方法。现在,在切换行 CheckBox 的状态后,我调用 DataGridViewRefreshEdit 方法,以便在单元格处于编辑模式时,用底层单元格的值刷新当前单元格的值,并丢弃任何先前的值。接下来,根据标题 CheckBox 的状态设置全局变量 TotalCheckedCheckBoxes 的值。最后,我恢复全局变量 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 方法

此方法根据 DataGridView 列的所有 CheckBoxes 是否被选中或取消选中来检查/取消选中标题 CheckBox 的状态。

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 方法

此方法用于绑定 DataGridView 以及初始化全局变量 TotalCheckBoxesTotalCheckedCheckBoxes

private void BindGridView()
{
   dgvSelectAll.DataSource = GetDataSource();

   TotalCheckBoxes = dgvSelectAll.RowCount;
   TotalCheckedCheckBoxes = 0;
}

总结

这就是我为实现此功能所采用的方法。如果任何人有不同的想法或建议来进一步改进此功能,请与我分享。我已在安装了 VS 2008Win XP SP3 的机器上创建并测试了此演示应用程序。

历史

  • 2009 年 10 月 1 日 -- 文章更新(修改了引言部分)
  • 2009 年 9 月 25 日 -- 文章更新(添加了目录)
  • 2009 年 9 月 18 日 -- 原始版本发布
© . All rights reserved.