Form间DataGridView批量数据传输的性能优化问题
问题背景
从Form1打开Form2,Form2的DataGridView展示数据库搜索结果,其中带复选框列用于选择要返回给Form1的DataGridView的记录。当前少量数据传输速度正常,但200条记录就耗时明显,后续需处理3000条以上数据,需要优化传输性能。
当前实现方式:Form1定义接口,遍历Form2的DataGridView,验证复选框状态和手机号格式,符合要求则调用接口将DataGridViewRow传给Form1插入。
当前核心代码
Form2中的遍历与传输逻辑:
for (int i = 0; i < dgvResutadoBusquedaBA.Rows.Count; i++) { if (Convert.ToBoolean(dgvResutadoBusquedaBA.Rows[i].Cells["agregar"].Value)) { bool isMatach = Regex.IsMatch(dgvResutadoBusquedaBA.Rows[i].Cells["Telefono"].Value.ToString(), "^(3)(\\d{9})$"); if (!isMatach) { codigosTelefonoError.Add(dgvResutadoBusquedaBA.Rows[i].Cells["codigo"].Value.ToString()); TelefonoError.Add(dgvResutadoBusquedaBA.Rows[i].Cells["Telefono"].Value.ToString()); } else { DataGridViewRow row1 = dgvResutadoBusquedaBA.Rows[i] as DataGridViewRow; interfaz1.tableDatosEntrantes(row1); } } }
Form1的接口定义:
internal interface interfazDatosEntrantes { void tableDatosEntrantes(DataGridViewRow row1); }
tableDatosEntrantes方法接收DataGridViewRow并插入到Form1的DataGridView中。
优化方案
1. 批量传输数据,减少接口调用次数
当前每条合法记录都调用一次接口,频繁的方法调用和UI刷新是性能瓶颈。改为先收集所有合法数据,一次性传输给Form1。
- 修改接口支持集合传输:
internal interface interfazDatosEntrantes { void tableDatosEntrantes(List<DataGridViewRow> validRows); }
- Form2中先收集数据再批量调用:
var validRows = new List<DataGridViewRow>(); for (int i = 0; i < dgvResutadoBusquedaBA.Rows.Count; i++) { var row = dgvResutadoBusquedaBA.Rows[i]; if (Convert.ToBoolean(row.Cells["agregar"].Value)) { string telefono = row.Cells["Telefono"].Value.ToString(); bool isMatch = Regex.IsMatch(telefono, "^(3)(\\d{9})$"); if (!isMatch) { codigosTelefonoError.Add(row.Cells["codigo"].Value.ToString()); TelefonoError.Add(telefono); } else { validRows.Add(row as DataGridViewRow); } } } // 一次性传输所有合法数据 interfaz1.tableDatosEntrantes(validRows);
2. 暂停Form1的DataGridView UI更新
批量插入时,DataGridView每添加一行都会触发UI刷新,这是大数量下的主要耗时点。插入前关闭自动布局、自动列宽等设置,插入后再恢复。
Form1的tableDatosEntrantes方法修改:
public void tableDatosEntrantes(List<DataGridViewRow> validRows) { // 暂停UI布局更新 dgvForm1.SuspendLayout(); // 临时关闭自动列宽(保留原有设置) var oldAutoSizeMode = dgvForm1.AutoSizeColumnsMode; dgvForm1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None; foreach (var row in validRows) { // 直接复制单元格值添加,避免行对象的复杂操作 dgvForm1.Rows.Add(row.Cells.Cast<DataGridViewCell>().Select(c => c.Value).ToArray()); } // 恢复原有设置 dgvForm1.AutoSizeColumnsMode = oldAutoSizeMode; dgvForm1.ResumeLayout(); }
3. 预编译正则表达式
当前每次验证手机号都创建新的Regex实例,预编译后可重复使用,提升验证速度。
在Form2中定义静态预编译的Regex:
private static readonly Regex TelefonoRegex = new Regex("^(3)(\\d{9})$", RegexOptions.Compiled);
验证时直接调用:
bool isMatch = TelefonoRegex.IsMatch(telefono);
4. 直接操作数据源而非DataGridViewRow
如果DataGridView绑定了DataTable等数据源,直接操作数据源比操作DataGridViewRow更高效。
- Form2中直接筛选数据源的合法行:
var sourceTable = dgvResutadoBusquedaBA.DataSource as DataTable; var validRows = sourceTable.AsEnumerable() .Where(row => Convert.ToBoolean(row["agregar"])) .Select(row => { string telefono = row["Telefono"].ToString(); if (TelefonoRegex.IsMatch(telefono)) return row; else { codigosTelefonoError.Add(row["codigo"].ToString()); TelefonoError.Add(telefono); return null; } }) .Where(row => row != null) .CopyToDataTable(); interfaz1.tableDatosEntrantes(validRows);
- 修改接口支持
DataTable传输:
internal interface interfazDatosEntrantes { void tableDatosEntrantes(DataTable validTable); }
- Form1中合并数据源:
public void tableDatosEntrantes(DataTable validTable) { var targetTable = dgvForm1.DataSource as DataTable; dgvForm1.SuspendLayout(); // 直接合并数据表,效率远高于逐行添加 targetTable.Merge(validTable); dgvForm1.ResumeLayout(); }
5. 异步处理(可选)
针对超大规模数据,可将筛选验证逻辑放到后台线程,避免阻塞UI,提升用户体验。注意操作UI必须回到主线程。
Form2中使用异步处理:
await Task.Run(() => { // 后台线程执行数据筛选和验证,收集validRows或validTable }); // 回到主线程执行数据传输 interfaz1.tableDatosEntrantes(validRows);
内容的提问来源于stack exchange,提问作者Jhonnatan Lopez

