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
相关产品推荐
相关产品推荐

