You need to enable JavaScript to run this app. C# WinForm:点击导入按钮时如何用foreach逐行处理两个DataGridView
优惠活动
大模型
产品
解决方案
定价
更多

C# WinForm:点击导入按钮时如何用foreach逐行处理两个DataGridView

C# WinForm 双绑定DataGridView逐行遍历与匹配实现

问题背景

开发过程中使用两个绑定数据源的DataGridView(分别对应Documentos、OCR数据),存在两个待实现的逻辑缺口:

  • 导入按钮btn_Import的点击事件中,Clientes标签页分支需要用foreach逐行遍历两个网格的关联数据,但未明确foreach的遍历对象、循环内的匹配逻辑
  • 现有FillDGVDocs1方法仅提取Documentos数据源第一行的DocID、BatchID参数填充OCR网格,无法加载全量文档对应的OCR关联数据,需要调整为逐行匹配的逻辑

导入按钮事件的foreach实现逻辑

注意:绑定了DataTable作为数据源的DataGridView,遍历操作优先直接操作绑定的DataTable,而非遍历DataGridView本身的Rows集合。后者会受界面筛选、未提交的新增行等状态影响,直接操作数据源能拿到全量真实数据,稳定性更高。

原代码的问题是把数据源提取的逻辑放在了循环体内部,每遍历一行就重复执行强转操作,冗余且容易出错。修正后的分支逻辑如下:

switch (TabControl.SelectedTab.Name)
{
    case "Clientes":
        // 提前提取两个网格的绑定数据源,做强转和非空校验
        DataTable dtDocs = dataGridViewDocumentos.DataSource as DataTable;
        DataTable dtOcr = dataGridViewOCR.DataSource as DataTable;

        // 数据源为空时直接跳过逻辑,避免空引用异常
        if (dtDocs == null || dtDocs.Rows.Count == 0) break;

        // foreach的遍历对象:Documentos数据源的所有有效数据行
        foreach (DataRow docRow in dtDocs.Rows)
        {
            // 跳过空值/已删除标记的无效行
            if (docRow.RowState == DataRowState.Deleted || docRow.IsNull("DocID"))
                continue;

            // 从当前行提取关联匹配的键值
            int currentDocId = Convert.ToInt32(docRow["DocID"]);
            int currentBatchId = Convert.ToInt32(docRow["BatchID"]);

            // 通过DataTable.Select方法直接筛选匹配当前DocID的OCR行,避免全量嵌套循环
            DataRow[] matchedOcrRows = dtOcr.Select($"DocID = {currentDocId}");

            // 遍历匹配到的OCR行,执行导入处理
            foreach (DataRow ocrRow in matchedOcrRows)
            {
                // 在此处补充你的导入处理逻辑,例:取字段值做校验/落库
                // string ocrText = ocrRow["识别结果"].ToString();
            }
        }
        break;
    case "2":
        Console.WriteLine("Case 2");
        break;
    default:
        Console.WriteLine("Default case");
        break;
}

修正FillDGVDocs1的逐行填充逻辑

原方法固定取Documentos数据源第一行的参数调用OCR填充方法,仅能加载第一份文档对应的OCR数据。调整为逐行遍历全量文档、合并所有关联OCR数据的逻辑如下:

private void FillDGVDocs1(int JobID, int Status)
{
    dataGridViewDocumentos.DataSource = null;
    dataGridViewDocumentos.Columns.Clear();
    dataGridViewDocumentos.Refresh();
    
    // 加载Documentos全量数据
    DataTable dtDocs = da.SP_SELECT_DOCS_WHERE_JOBID_STATUS_IBML1(JobID, Status);
    dataGridViewDocumentos.DataSource = dtDocs;

    // 初始化OCR全量合并表
    DataTable mergedOcrTable = null;

    // 逐行遍历Documentos所有有效行,替代原固定取第一行的逻辑
    foreach (DataRow docRow in dtDocs.Rows)
    {
        // 跳过空值行
        if (docRow.IsNull("DocID")) continue;
        
        int docId = Convert.ToInt32(docRow["DocID"]);
        int batchId = Convert.ToInt32(docRow["BatchID"]);
        
        // 加载当前文档对应的OCR数据
        DataTable currentOcr = LoadOcrData(docId, batchId);
        if (currentOcr == null || currentOcr.Rows.Count == 0) continue;

        // 第一次加载时初始化合并表的结构
        if (mergedOcrTable == null)
        {
            mergedOcrTable = currentOcr.Clone();
        }
        // 合并当前文档的OCR数据到总表
        mergedOcrTable.Merge(currentOcr);
    }

    // 将合并完成的全量OCR数据绑定到OCR网格
    dataGridViewOCR.DataSource = mergedOcrTable;
}

// 抽取原FillDGVOCR的取数逻辑为独立方法,供逐行调用
private DataTable LoadOcrData(int DocID, int BatchID)
{
    // 替换为原FillDGVOCR方法中的数据库查询逻辑,返回对应OCR数据的DataTable
    return da.SP_SELECT_OCR_BY_DOCID_BATCHID(DocID, BatchID);
}

落地注意事项

  • 类型强转统一用as运算符,搭配非空判断,避免绑定数据源不是DataTable时直接抛异常
  • 用DataTable.Select()做关联行匹配,比嵌套循环遍历全量OCR表的执行效率更高
  • 遍历行时加空值校验,避免数据库返回的空字段触发类型转换异常
  • 如果OCR网格需要和Documentos网格做主从联动,不需要合并全量OCR数据,只需在Documentos的SelectionChanged事件中,根据选中行的DocID筛选OCR数据源即可,性能更好

内容的提问来源于stack exchange,提问作者Tiago Silva

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.31 10:15:02