在C#桌面或后台程序中,经常需要把DataGridView展示的数据交给用户做进一步分析,而Excel是最通用的载体。相比直接打印或复制粘贴,编程导出能精确保留数据类型、列顺序和格式。下面以EPPlus为例,说明如何不依赖Office组件完成转换。

一、为什么不用Excel Interop
早期很多教程使用Microsoft.Office.Interop.Excel,通过COM调用本机Excel。这种方式要求服务器或用户电脑必须安装特定版本的Excel,且在ASP.NET等服务环境下极易出现权限不足、进程无法释放的问题。一旦Excel进程卡死,还会占用系统资源。
从原理上看,Interop只是把操作转发给Excel应用程序,属于自动化控制;而Open_XML直接按照Excel文件格式(其实是zip包里的XML)生成文档。EPPlus就是对Open_XML的封装,不需要安装Excel,速度更快,适合批量导出。
二、准备EPPlus环境
在Visual Studio中,通过NuGet包管理器搜索EPPlus并安装。当前常用版本为4.5.3或5.x(5.x需注意许可证模式)。安装完成后,在代码文件顶部引用命名空间。
using OfficeOpenXml; using System.IO; using System.Windows.Forms;
EPPlus的LicenseContext在5.x后需要显式设置,否则运行时会抛异常。可以在程序启动时写一行代码指定为非商业或商业用途。下面演示导出方法前先配置上下文。
// EPPlus 5.x 需要设置许可证 ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
三、基础导出实现
核心逻辑是创建ExcelPackage对象,获取Worksheet,然后双重循环DataGridView的列和行。表头取Columns[i].HeaderText,数据取Rows[r].Cells[i].Value。注意Value可能为null,需要转为空字符串。
下面的方法接收DataGridView和文件路径,生成xlsx文件。使用using确保资源释放,FileInfo描述目标位置。写入时索引从1开始,符合Excel习惯。
public void ExportToExcel(DataGridView dgv, string filePath)
{
using (ExcelPackage package = new ExcelPackage())
{
ExcelWorksheet sheet = package.Workbook.Worksheets.Add("Sheet1");
// 写表头
for (int col = 0; col < dgv.Columns.Count; col++)
{
sheet.Cells[1, col + 1].Value = dgv.Columns[col].HeaderText;
}
// 写数据
for (int row = 0; row < dgv.Rows.Count; row++)
{
for (int col = 0; col < dgv.Columns.Count; col++)
{
var val = dgv.Rows[row].Cells[col].Value;
sheet.Cells[row + 2, col + 1].Value = val == null ? "" : val.ToString();
}
}
package.SaveAs(new FileInfo(filePath));
}
}
这段代码能应付大多数场景,但所有单元格都会被写成字符串。如果原始数据是数字或日期,在Excel里会左对齐且不能直接计算,因此需要类型处理。
四、保留数据类型与格式
DataGridView的单元格Value通常是object,可能是int、decimal或DateTime。EPPlus会根据赋给Cells.Value的对象类型自动识别,所以直接传原值而不是ToString()就能保留格式。同时可以用Numberformat设置显示样式。
以下改进版在写入时判断类型,并对日期列应用短日期格式。这样导出的Excel中,日期可参与运算,数字右对齐。
public void ExportWithType(DataGridView dgv, string filePath)
{
using (ExcelPackage package = new ExcelPackage())
{
ExcelWorksheet sheet = package.Workbook.Worksheets.Add("Report");
for (int c = 0; c < dgv.Columns.Count; c++)
{
sheet.Cells[1, c + 1].Value = dgv.Columns[c].HeaderText;
}
for (int r = 0; r < dgv.Rows.Count; r++)
{
for (int c = 0; c < dgv.Columns.Count; c++)
{
object v = dgv.Rows[r].Cells[c].Value;
var cell = sheet.Cells[r + 2, c + 1];
if (v is DateTime)
{
cell.Value = (DateTime)v;
cell.Style.Numberformat.Format = "yyyy-mm-dd";
}
else
{
cell.Value = v;
}
}
}
package.SaveAs(new FileInfo(filePath));
}
}
五、处理隐藏列与用户选择
实际界面中,有些辅助列可能隐藏。导出时应判断是否Visible,跳过隐藏列以免混淆。同时可让用户勾选要导出的列,提升灵活性。
下面示例仅导出可见列,并建立列索引映射,避免数据错位。这种做法在字段很多的后台系统里很实用。
public void ExportVisible(DataGridView dgv, string filePath)
{
using (ExcelPackage package = new ExcelPackage())
{
ExcelWorksheet sheet = package.Workbook.Worksheets.Add("Data");
int excelCol = 1;
for (int c = 0; c < dgv.Columns.Count; c++)
{
if (!dgv.Columns[c].Visible) continue;
sheet.Cells[1, excelCol].Value = dgv.Columns[c].HeaderText;
for (int r = 0; r < dgv.Rows.Count; r++)
{
sheet.Cells[r + 2, excelCol].Value = dgv.Rows[r].Cells[c].Value;
}
excelCol++;
}
package.SaveAs(new FileInfo(filePath));
}
}
六、性能与大数据量建议
当行数超过一万时,逐单元格写入仍可行,但建议先禁用DataGridView的自动重绘,导出完再恢复。若数据来自数据库,最好直接读DataTable再写,避开UI控件开销。
另外,EPPlus支持通过LoadFromCollection或LoadFromDataTable一次性写入,比循环更快。若坚持用DataGridView,可在导出前调用SuspendLayout减少界面负担,保障主线程流畅。
| 方案 | 是否需装Excel | 速度 | 适用场景 |
|---|---|---|---|
| Interop | 是 | 慢 | 本地富格式操作 |
| EPPlus | 否 | 快 | 服务端、批量 |
| CSV直写 | 否 | 最快 | 纯文本交换 |
综上,用EPPlus把DataGridView导出Excel,关键在类型映射与列过滤。掌握这几段代码,就能稳定生成业务报表。
C#DataGridViewExcel_export修改时间:2026-08-02 18:18:16