C#如何在应用启动时读取Excel文件数据并显示到DataGridView
实现思路
- 你现有代码的核心问题是获取到Excel的已用区域Range后,没有执行单元格内容读取逻辑就直接释放了COM对象,只需要在获取行列数之后、释放COM资源之前,补充表头读取、逐行数据写入DataTable的逻辑,最后将DataTable绑定到DataGridView即可。
- 读取逻辑分两步:先读取第一行内容作为DataTable的列名(如果第一行不是表头可自行调整起始索引),再从第二行开始逐行读取单元格值存入DataTable。
- 所有读取操作完成后再执行GC回收和COM对象释放,避免中途对象被释放导致读取失败;读取过程中需要处理单元格空值、DBNull的情况,避免抛出空引用异常。
修改后完整代码
private void OpenExcelFile() { System.Data.DataTable dt = new System.Data.DataTable(); // Excel数据容器 // 找第一个匹配名称带Contacts的Excel文件,如需读取多个可去掉FirstOrDefault逻辑 var targetFile = Directory.GetFiles(this.ExcelFolderPath) .FirstOrDefault(p => p.Contains("Contacts") && (p.EndsWith(".xlsx") || p.EndsWith(".xls"))); if (targetFile == null) { MessageBox.Show("未找到目标Contacts文件"); return; } Microsoft.Office.Interop.Excel.Application excelApp = null; Microsoft.Office.Interop.Excel.Workbook excelWorkbook = null; Microsoft.Office.Interop.Excel._Worksheet excelWorksheet = null; Microsoft.Office.Interop.Excel.Range excelRange = null; try { excelApp = new Microsoft.Office.Interop.Excel.Application(); excelApp.Visible = false; // 后台运行不显示Excel窗口 excelWorkbook = excelApp.Workbooks.Open(targetFile); excelWorksheet = excelWorkbook.Sheets[1]; // 默认读第一个工作表,可按需改索引或表名 excelRange = excelWorksheet.UsedRange; int rowCount = excelRange.Rows.Count; int colCount = excelRange.Columns.Count; // 1. 读取表头(第一行),生成DataTable列 for (int col = 1; col <= colCount; col++) { string colName = (excelRange.Cells[1, col] as Microsoft.Office.Interop.Excel.Range)?.Text?.ToString(); // 处理空列名,避免绑定报错 if (string.IsNullOrWhiteSpace(colName)) { colName = $"列{col}"; } // 处理重复列名 while (dt.Columns.Contains(colName)) { colName += "_1"; } dt.Columns.Add(colName); } // 2. 逐行读取数据(从第二行开始,第一行是表头) for (int row = 2; row <= rowCount; row++) { DataRow dataRow = dt.NewRow(); for (int col = 1; col <= colCount; col++) { var cellValue = (excelRange.Cells[row, col] as Microsoft.Office.Interop.Excel.Range)?.Value2; dataRow[col - 1] = cellValue ?? DBNull.Value; // 处理空单元格 } dt.Rows.Add(dataRow); } // 3. 绑定到DataGridView dataGridView1.AutoGenerateColumns = true; // 自动生成列,如需自定义列可设为false dataGridView1.DataSource = dt; } catch (Exception ex) { MessageBox.Show($"读取Excel失败:{ex.Message}"); } finally { // 统一释放COM资源,顺序从最内层到最外层,异常场景也能正常关闭Excel进程 GC.Collect(); GC.WaitForPendingFinalizers(); if (excelRange != null) Marshal.ReleaseComObject(excelRange); if (excelWorksheet != null) Marshal.ReleaseComObject(excelWorksheet); if (excelWorkbook != null) { excelWorkbook.Close(); Marshal.ReleaseComObject(excelWorkbook); } if (excelApp != null) { excelApp.Quit(); Marshal.ReleaseComObject(excelApp); } } }
注意事项
- 如果你的Excel文件第一行不是表头是有效数据,只需要把列生成逻辑改为自动生成
列1/列2格式的默认列名,数据读取的起始行从1开始即可。 - 上述代码默认读取第一个名称匹配的Contacts文件,如果需要合并目录下所有匹配文件的内容,去掉FirstOrDefault判断,在循环内追加读取工作表数据到同一个DataTable即可。
- 使用Microsoft.Office.Interop.Excel要求程序运行的设备必须安装对应版本的Microsoft Office,如果需要无Office环境运行,建议替换为NPOI、EPPlus等不依赖Office的开源类库实现读取逻辑。
- 如果读取操作放在后台线程执行,绑定DataGridView时必须通过Invoke切换到UI线程操作,否则会抛出跨线程访问控件的异常。
内容的提问来源于stack exchange,提问作者jacko10101
相关产品推荐
相关产品推荐

