You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DataGridView勾选行计数异常求助:导出Excel行号错误

问题分析与解决建议

嘿,我来帮你拆解下这个问题——你导出Excel时所有行号都变成总勾选数的核心原因,是并行执行中的竞态条件,再加上Excel COM对象本身的线程安全限制,咱们一步步来解决:

问题根源

你用Parallel.ForEach来并行处理行,但checkedRowCount是一个共享的普通整型变量。多个线程会同时读取和修改这个变量:比如线程A和线程B同时读到checkedRowCount=14,然后都把它递增到15,接着都往Excel的第15行写数据,最后就出现了所有行都是同一个序号的情况。

另外要划重点:Excel的COM对象完全不是线程安全的,并行操作同一个Worksheet本身就可能引发各种不可预测的bug(比如数据错乱、程序崩溃),这也是你单独测试可能正常(单线程环境),但实际场景出问题的关键原因。

解决方案

方案1:用线程安全的计数器(临时救急,但仍有风险)

如果一定要用并行,可以用Interlocked.Increment来原子性地递增计数,确保每个线程拿到唯一的序号:

int checkedRowCount = 0;
// ... 省略SaveFileDialog和Excel初始化代码
Parallel.ForEach(dataGridView1.Rows.Cast<DataGridViewRow>(), row => {
    DataGridViewCell cbCell = row.Cells[6];
    if (!(cbCell.Value is DBNull)) {
        if (Convert.ToBoolean(cbCell.Value)) {
            // 原子性递增,避免多线程同时修改的竞态问题
            int currentRow = Interlocked.Increment(ref checkedRowCount);
            for (int i = 0; i < dataGridView1.Columns.Count - 1; i++) {
                // 注意:这里仍存在Excel对象线程不安全的风险,不推荐长期用
                xlWS.Cells[currentRow, i + 1] = dataGridView1.Rows[row.Index].Cells[i].Value?.ToString() ?? string.Empty;
            }
        }
    }
});

方案2:改用普通循环(最稳妥,推荐)

既然Excel不支持多线程操作,直接放弃并行,用普通的foreach循环反而更稳定,还能彻底解决行号问题:

int checkedRowCount = 0;
// ... 省略SaveFileDialog和Excel初始化代码
foreach (DataGridViewRow row in dataGridView1.Rows.Cast<DataGridViewRow>()) {
    DataGridViewCell cbCell = row.Cells[6];
    // 简化条件判断,同时增加空值保护
    if (!(cbCell.Value is DBNull) && Convert.ToBoolean(cbCell.Value)) {
        checkedRowCount++;
        for (int i = 0; i < dataGridView1.Columns.Count - 1; i++) {
            // 用?.ToString()避免NullReferenceException,空值时显示空字符串
            xlWS.Cells[checkedRowCount, i + 1] = row.Cells[i].Value?.ToString() ?? string.Empty;
        }
    }
}

额外优化:先筛选再处理(代码更清晰)

你可以先把所有勾选的行筛选出来,再批量写入Excel,逻辑更直观:

// 先筛选出所有勾选的行
var checkedRows = dataGridView1.Rows.Cast<DataGridViewRow>()
    .Where(row => !(row.Cells[6].Value is DBNull) && Convert.ToBoolean(row.Cells[6].Value))
    .ToList();

// 遍历筛选后的行写入Excel
for (int rowIdx = 0; rowIdx < checkedRows.Count; rowIdx++) {
    var targetRow = checkedRows[rowIdx];
    for (int colIdx = 0; colIdx < dataGridView1.Columns.Count - 1; colIdx++) {
        // 行号从1开始,所以是rowIdx+1
        xlWS.Cells[rowIdx + 1, colIdx + 1] = targetRow.Cells[colIdx].Value?.ToString() ?? string.Empty;
    }
}

内容的提问来源于stack exchange,提问作者Alexander Moiseev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:16:57