在C#的WinForms开发中,DataGridView是最常用的数据展示控件之一。当业务要求用户在表格中多选记录进行删除、导出或状态变更时,最直观的做法就是给每一行前面加一个复选框。很多初学者会尝试自己放一堆CheckBox控件去对齐行高,这种做法不仅布局难维护,而且滚动和分页时状态极易丢失。实际上DataGridView已经内置了对复选框列的支持,只要用对方法,几十行代码就能稳定实现多选。

利用数据源布尔字段自动生成复选框列
最常见也最省事的方式,是先把数据模型定义一个布尔类型的属性,比如叫IsSelected。当我们把实现了IList或BindingList的集合赋值给DataGridView的DataSource时,WinForms会自动识别布尔属性,并生成对应的DataGridViewCheckBoxColumn。这种方式的优势在于数据状态和界面完全绑定,不用手动去读写单元格的值。
下面用一个简单的用户类来做演示。我们准备了编号、名称和是否选中三个字段,其中是否选中就是布尔类型。绑定之后,用户勾选方框,底层对象的IsSelected会自动变成true,反之取消勾选也会同步改回false。这样就避免了在按钮事件里遍历控件找状态的麻烦。
需要注意一点,如果使用的是普通List<T>而不是BindingList<T>,那么在代码里修改对象的IsSelected时,界面可能不会立刻刷新。此时可以调用DataGridView.Refresh()或者改用支持通知的BindingList。对于大多数批量操作界面来说,用户手动点勾才会变,所以用List也能跑,只是程序控制选中时记得刷新。
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace DgvCheckDemo
{
public class UserItem
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
public partial class Form1 : Form
{
private DataGridView dgv;
private List<UserItem> users;
public Form1()
{
InitializeComponent();
dgv = new DataGridView();
dgv.Dock = DockStyle.Fill;
this.Controls.Add(dgv);
users = new List<UserItem>
{
new UserItem { Id = 1, Name = "张三", IsSelected = false },
new UserItem { Id = 2, Name = "李四", IsSelected = false },
new UserItem { Id = 3, Name = "王五", IsSelected = false }
};
// 绑定数据源,布尔属性自动变成复选框列
dgv.DataSource = users;
}
// 获取所有被勾选的用户
public List<UserItem> GetCheckedUsers()
{
List<UserItem> result = new List<UserItem>();
foreach (var u in users)
{
if (u.IsSelected)
{
result.Add(u);
}
}
return result;
}
}
}
手动添加DataGridViewCheckBoxColumn实现灵活控制
当表格列是动态拼出来的,或者数据来自没有布尔字段的数据库查询结果时,自动生成就不太方便了。这时候可以手动创建一个DataGridViewCheckBoxColumn对象,设置好列名和标题,再把它加进Columns集合里。这样做不会依赖数据源结构,适合在已有表格右侧插入一个"选择"列的场景。
手动添加时要关注两个细节:一是把列的Width设小一点,比如40,让界面更紧凑;二是如果希望表头也能点一下全选,可以把HeaderText设成空,然后在CellClick事件里判断点击的是不是头单元格。相比自动绑定,手动列的值需要通过dgv.Rows[i].Cells["selectCol"].Value来读取,类型是object,要做真假判断时最好用Convert.ToBoolean包一层,防止null导致异常。
下面的例子展示怎么在窗体加载时给已经绑定了普通文本的DataGridView追加复选框列,并在按钮里统计选中行。可以看到,即使原始数据没有布尔字段,我们依然能用独立列完成多选,且不影响原有数据绑定逻辑。
using System;
using System.Data;
using System.Windows.Forms;
namespace DgvManualCheck
{
public partial class Form2 : Form
{
private DataGridView dgv;
public Form2()
{
InitializeComponent();
dgv = new DataGridView();
dgv.Dock = DockStyle.Fill;
this.Controls.Add(dgv);
// 模拟只有文本列的数据
DataTable dt = new DataTable();
dt.Columns.Add("Id");
dt.Columns.Add("Product");
dt.Rows.Add("1", "键盘");
dt.Rows.Add("2", "鼠标");
dt.Rows.Add("3", "显示器");
dgv.DataSource = dt;
// 手动插入复选框列
DataGridViewCheckBoxColumn chk = new DataGridViewCheckBoxColumn();
chk.Name = "selectCol";
chk.HeaderText = "选择";
chk.Width = 40;
dgv.Columns.Insert(0, chk);
}
// 统计选中行数量
public int CountChecked()
{
int count = 0;
foreach (DataGridViewRow row in dgv.Rows)
{
if (row.Cells["selectCol"].Value != null &&
Convert.ToBoolean(row.Cells["selectCol"].Value))
{
count++;
}
}
return count;
}
}
}
全选反选与获取选中行集合的实用封装
有了复选框列之后,用户通常还希望有"全选"和"反选"按钮。如果每次都去遍历所有行改单元格值,代码会散落在各个事件里。更好的做法是写一个通用方法,接收目标状态和DataGridView实例,统一处理。对于手动列,直接给Value赋true或false;对于绑定布尔字段的列,也可以绕过界面直接改数据源,再刷新。
获取选中行时,建议返回一个明确的列表而不是在循环里直接做业务,这样单元测试和后期维护都简单。下面这段代码演示了全选、反选以及导出选中行文本ID的完整逻辑。注意在改完单元格值后,如果绑定的是List,要调用dgv.Refresh();如果是手动列则无需额外刷新,控件自己会重绘。
还有一个容易踩的坑:当用户点击复选框时,CellValueChanged事件里如果又去改别的行,可能会触发递归。所以全选操作最好放在按钮点击里,而不是在某个单元格变化事件中去联动,否则界面会卡顿甚至死循环。把读写逻辑收口到几个方法里,是保持DataGridView多选功能稳定的关键。
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace DgvSelectUtil
{
public class DgvHelper
{
// 全选或取消全选
public static void SetAllChecked(DataGridView dgv, string colName, bool check)
{
foreach (DataGridViewRow row in dgv.Rows)
{
if (row.Cells[colName].ReadOnly == false)
{
row.Cells[colName].Value = check;
}
}
dgv.Refresh();
}
// 反选
public static void ReverseChecked(DataGridView dgv, string colName)
{
foreach (DataGridViewRow row in dgv.Rows)
{
object val = row.Cells[colName].Value;
bool cur = val != null && Convert.ToBoolean(val);
row.Cells[colName].Value = !cur;
}
dgv.Refresh();
}
// 获取选中行的主键文本列表
public static List<string> GetCheckedIds(DataGridView dgv, string checkCol, string idCol)
{
List<string> ids = new List<string>();
foreach (DataGridViewRow row in dgv.Rows)
{
object val = row.Cells[checkCol].Value;
if (val != null && Convert.ToBoolean(val))
{
ids.Add(row.Cells[idCol].Value.ToString());
}
}
return ids;
}
}
}
通过上述三种角度的实现,你可以根据项目里表格是静态还是动态、有没有现成布尔字段,来选择合适的复选框接入方案。无论哪种方式,核心都是把选中状态和数据或单元格值绑定清楚,并提供统一的全选反选入口,这样在多选批量处理需求下界面既好用又不容易出bug。
C#DataGridViewCheckBox修改时间:2026-08-17 04:40:37