C# 将 DataGridView 导出到 Excel 并带列标题
如何在 C# 中将带有列标题的 DataGridView 导出到 Excel?
将 Windows Forms DataGridView 控件中的数据导出为 Excel 格式是一个常见的需求,但开发人员经常会遇到一个令人沮丧的问题:导出的文件缺少列标题。 当您需要将 DataGridView 导出到 Excel 并保留列标题时,解决方案必须能够可靠地保留所有数据和格式。 使用 Microsoft Office Interop 的传统方法可能速度慢、不稳定,并且需要在运行代码的每台计算机上安装 MS Excel。
IronXL可以完美地解决这个问题。 这是一个.NET Excel 库,可以读取、创建和写入 Excel 文件,而无需任何 Office 依赖项。 本教程将引导您完成一个完整的导出解决方案——只需几行代码即可处理列标题、数据类型、空单元格和用户友好的文件保存。
最后,您将拥有一个可用的 Windows Forms 按钮处理程序,该处理程序可以将 DataGridView 导出到 XLSX 文件,并保留所有列标题。
开始之前你需要准备什么
在编写任何代码之前,请确保以下事项已就绪:
- Visual Studio 2022 或更高版本(任何版本) 已安装.NET 10 SDK
- 一个面向.NET 10 的 Windows 窗体应用程序项目 -NuGet访问来安装IronXL
以下示例通篇使用顶级语句和现代.NET 10 项目结构。
如何在.NET项目中安装库?
在 Visual Studio 中打开NuGet程序包管理器控制台,并运行以下任一命令。 该软件包已在NuGet Gallery上列出,面向.NET Standard 2.0 及更高版本,因此可与任何现代.NET项目配合使用。
Install-Package IronXL
Install-Package IronXL
dotnet add package IronXL
dotnet add package IronXL
安装完成后,请在表单文件顶部添加所需的命名空间:
using IronXL;
using System;
using System.Data;
using System.Windows.Forms;
using IronXL;
using System;
using System.Data;
using System.Windows.Forms;
Imports IronXL
Imports System
Imports System.Data
Imports System.Windows.Forms
这些导入功能可以访问IronXL 的 Excel 功能、数据表操作以及导出过程所需的 Windows 窗体控件。
如何使用示例数据设置 DataGridView?
首先在 Visual Studio 中创建一个 Windows 窗体应用程序。 通过设计器向主窗体添加名为 dataGridView1 的 DataGridView 和名为 btnExport 的 Button。 然后在表单加载时填充表格:
using System;
using System.Data;
using System.Windows.Forms;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
DataTable dt = new DataTable();
// Define columns -- these names become Excel headers
dt.Columns.Add("Product ID", typeof(int));
dt.Columns.Add("Product Name", typeof(string));
dt.Columns.Add("Price", typeof(decimal));
dt.Columns.Add("Stock Quantity", typeof(int));
// Add sample rows
dt.Rows.Add(1001, "Laptop", 999.99m, 15);
dt.Rows.Add(1002, "Mouse", 29.99m, 50);
dt.Rows.Add(1003, "Keyboard", 79.99m, 30);
dt.Rows.Add(1004, "Monitor", 299.99m, 12);
dt.Rows.Add(1005, "Headphones", 89.99m, 25);
// Bind data to the grid
dataGridView1.DataSource = dt;
}
}
using System;
using System.Data;
using System.Windows.Forms;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
DataTable dt = new DataTable();
// Define columns -- these names become Excel headers
dt.Columns.Add("Product ID", typeof(int));
dt.Columns.Add("Product Name", typeof(string));
dt.Columns.Add("Price", typeof(decimal));
dt.Columns.Add("Stock Quantity", typeof(int));
// Add sample rows
dt.Rows.Add(1001, "Laptop", 999.99m, 15);
dt.Rows.Add(1002, "Mouse", 29.99m, 50);
dt.Rows.Add(1003, "Keyboard", 79.99m, 30);
dt.Rows.Add(1004, "Monitor", 299.99m, 12);
dt.Rows.Add(1005, "Headphones", 89.99m, 25);
// Bind data to the grid
dataGridView1.DataSource = dt;
}
}
Imports System
Imports System.Data
Imports System.Windows.Forms
Public Partial Class Form1
Inherits Form
Public Sub New()
InitializeComponent()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim dt As New DataTable()
' Define columns -- these names become Excel headers
dt.Columns.Add("Product ID", GetType(Integer))
dt.Columns.Add("Product Name", GetType(String))
dt.Columns.Add("Price", GetType(Decimal))
dt.Columns.Add("Stock Quantity", GetType(Integer))
' Add sample rows
dt.Rows.Add(1001, "Laptop", 999.99D, 15)
dt.Rows.Add(1002, "Mouse", 29.99D, 50)
dt.Rows.Add(1003, "Keyboard", 79.99D, 30)
dt.Rows.Add(1004, "Monitor", 299.99D, 12)
dt.Rows.Add(1005, "Headphones", 89.99D, 25)
' Bind data to the grid
dataGridView1.DataSource = dt
End Sub
End Class
在 dt.Columns.Add(...) 调用中定义的列名将成为导出 Excel 文件中的标题行。您可以替换此处的任何数据源——数据库查询结果、CSV 导入或存储为 DataTable 的 API 响应,所有这些都与后面所示的导出代码的工作方式相同。 DataTable 方法特别方便,因为 DataGridView 公开了一个 DataSource 属性,该属性接受任何 IList 或 IBindingList,这意味着相同的导出代码无需修改即可推广到每个绑定场景。
如果您的 DataGridView 是通过 SqlDataAdapter 从数据库填充的,则 DataTable 已经包含类型化的列。 将这些键入的值传递给 SetCellValue 而不是对它们调用 ToString(),可以使 Excel 输出中的数值列保持为数值,这对于任何下游报告或数据透视表的使用都很重要。
这将创建一个包含所有数据的 DataGridView:
! 使用 IronXL 将 C# DataGridView 导出到 Excel 并带有列标题:图 1 - DataGridView 中的示例数据
对于更高级的数据绑定场景,微软关于DataGridView 数据绑定的文档提供了更多示例。
如何在将 DataGridView 导出到 Excel 时保留列标题?
导出方法从每个 DataGridView 列的 HeaderText 属性读取列标题,并将它们写入工作表的第 0 行。 数据行从第 1 行开始。 将此方法连接到 btnExport 点击事件:
private void btnExport_Click(object sender, EventArgs e)
{
try
{
// Create a new Excel workbook in XLSX format
WorkBook workbook = WorkBook.Create(ExcelFileFormat.XLSX);
WorkSheet worksheet = workbook.CreateWorkSheet("Exported Data");
// Write column headers to row 0
for (int col = 0; col < dataGridView1.Columns.Count; col++)
{
worksheet.SetCellValue(0, col, dataGridView1.Columns[col].HeaderText);
}
// Write data rows starting at row 1
for (int row = 0; row < dataGridView1.Rows.Count; row++)
{
// Skip the placeholder new-row at the bottom of the grid
if (dataGridView1.AllowUserToAddRows && row == dataGridView1.Rows.Count - 1)
continue;
for (int col = 0; col < dataGridView1.Columns.Count; col++)
{
var cellValue = dataGridView1.Rows[row].Cells[col].Value;
if (cellValue != null)
{
worksheet.SetCellValue(row + 1, col, cellValue.ToString());
}
}
}
// Prompt the user to choose a save location
using SaveFileDialog saveFileDialog = new SaveFileDialog
{
Filter = "Excel Files|*.xlsx",
FileName = "DataGridView_Export.xlsx"
};
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
workbook.SaveAs(saveFileDialog.FileName);
MessageBox.Show("Export completed successfully!", "Success",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
catch (Exception ex)
{
MessageBox.Show($"Export failed: {ex.Message}", "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void btnExport_Click(object sender, EventArgs e)
{
try
{
// Create a new Excel workbook in XLSX format
WorkBook workbook = WorkBook.Create(ExcelFileFormat.XLSX);
WorkSheet worksheet = workbook.CreateWorkSheet("Exported Data");
// Write column headers to row 0
for (int col = 0; col < dataGridView1.Columns.Count; col++)
{
worksheet.SetCellValue(0, col, dataGridView1.Columns[col].HeaderText);
}
// Write data rows starting at row 1
for (int row = 0; row < dataGridView1.Rows.Count; row++)
{
// Skip the placeholder new-row at the bottom of the grid
if (dataGridView1.AllowUserToAddRows && row == dataGridView1.Rows.Count - 1)
continue;
for (int col = 0; col < dataGridView1.Columns.Count; col++)
{
var cellValue = dataGridView1.Rows[row].Cells[col].Value;
if (cellValue != null)
{
worksheet.SetCellValue(row + 1, col, cellValue.ToString());
}
}
}
// Prompt the user to choose a save location
using SaveFileDialog saveFileDialog = new SaveFileDialog
{
Filter = "Excel Files|*.xlsx",
FileName = "DataGridView_Export.xlsx"
};
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
workbook.SaveAs(saveFileDialog.FileName);
MessageBox.Show("Export completed successfully!", "Success",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
catch (Exception ex)
{
MessageBox.Show($"Export failed: {ex.Message}", "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Private Sub btnExport_Click(sender As Object, e As EventArgs)
Try
' Create a new Excel workbook in XLSX format
Dim workbook As WorkBook = WorkBook.Create(ExcelFileFormat.XLSX)
Dim worksheet As WorkSheet = workbook.CreateWorkSheet("Exported Data")
' Write column headers to row 0
For col As Integer = 0 To dataGridView1.Columns.Count - 1
worksheet.SetCellValue(0, col, dataGridView1.Columns(col).HeaderText)
Next
' Write data rows starting at row 1
For row As Integer = 0 To dataGridView1.Rows.Count - 1
' Skip the placeholder new-row at the bottom of the grid
If dataGridView1.AllowUserToAddRows AndAlso row = dataGridView1.Rows.Count - 1 Then
Continue For
End If
For col As Integer = 0 To dataGridView1.Columns.Count - 1
Dim cellValue = dataGridView1.Rows(row).Cells(col).Value
If cellValue IsNot Nothing Then
worksheet.SetCellValue(row + 1, col, cellValue.ToString())
End If
Next
Next
' Prompt the user to choose a save location
Using saveFileDialog As New SaveFileDialog With {
.Filter = "Excel Files|*.xlsx",
.FileName = "DataGridView_Export.xlsx"
}
If saveFileDialog.ShowDialog() = DialogResult.OK Then
workbook.SaveAs(saveFileDialog.FileName)
MessageBox.Show("Export completed successfully!", "Success",
MessageBoxButtons.OK, MessageBoxIcon.Information)
End If
End Using
Catch ex As Exception
MessageBox.Show($"Export failed: {ex.Message}", "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
分解导出步骤
该方法的每个步骤都有其特定目的:
- WorkBook.Create :使用 XLSX 格式在内存中初始化一个新的 Excel 文件。 在调用
SaveAs之前,不会将任何文件写入磁盘。 - CreateWorkSheet :添加一个命名的工作表来保存导出的数据。 在 Excel 中,"导出数据"会显示为标签页名称。
-列标题循环:读取每一列的
dataGridView1.Columns[col].HeaderText并将其写入行索引 0。这是保留标题的关键步骤。 -数据行循环:嵌套循环遍历每一行和每一列,使用row + 1作为 Excel 行索引,因此数据始终从标题行下方开始。 -空值检查:防止单元格不包含值时出现异常,这在实际数据中很常见。 - SaveFileDialog :允许用户在运行时选择文件名和目标位置,而不是硬编码路径。
导出的文件如下所示:
使用 IronXL 将 C# DataGridView 导出到 Excel 并带有列标题:图 2 - 包含导出示例数据的输出 Excel 文件
导出过程中如何处理常见极端情况?
现实世界的数据很少像样本数据集那样干净。 以下是您最有可能遇到的情况以及应对方法。
空单元格
数据循环中的空值检查(if (cellValue != null))可以处理空单元格,而不会抛出异常。 在 Excel 输出中,空白单元格将保持空白,从而保留网格结构。
混合数据类型
IronXL可以自动处理不同的数据格式。 当您通过 ToString() 将数字作为字符串传递时,Excel 仍可能根据该值将其识别为数值。 为了精确控制数据类型,请在写入之前将单元格值强制转换为正确的类型:
if (cellValue is int intVal)
worksheet.SetCellValue(row + 1, col, intVal);
else if (cellValue is decimal decVal)
worksheet.SetCellValue(row + 1, col, (double)decVal);
else
worksheet.SetCellValue(row + 1, col, cellValue?.ToString() ?? string.Empty);
if (cellValue is int intVal)
worksheet.SetCellValue(row + 1, col, intVal);
else if (cellValue is decimal decVal)
worksheet.SetCellValue(row + 1, col, (double)decVal);
else
worksheet.SetCellValue(row + 1, col, cellValue?.ToString() ?? string.Empty);
If TypeOf cellValue Is Integer Then
Dim intVal As Integer = CType(cellValue, Integer)
worksheet.SetCellValue(row + 1, col, intVal)
ElseIf TypeOf cellValue Is Decimal Then
Dim decVal As Decimal = CType(cellValue, Decimal)
worksheet.SetCellValue(row + 1, col, CDbl(decVal))
Else
worksheet.SetCellValue(row + 1, col, If(cellValue?.ToString(), String.Empty))
End If
这种方法可以保持 Excel 中的数值列为数值型,以便后续的公式和排序功能能够正常工作。
标题中的特殊字符
包含特殊字符(例如 &、<、>)和带重音符号的字母的列标题可以正确导出。 IronXL会自动处理编码,无需您进行任何转义操作。
大型数据集
对于包含数万行的表格,请考虑将导出逻辑移至后台线程以保持 UI 的响应速度。将 WorkBook.Create 和循环逻辑封装在 Task.Run 调用中,并在完成的回调函数中更新 UI。 微软关于 使用 Task 进行异步编程的文档详细解释了这种模式。
主要出口方式有何异同?
在决定使用某个库之前,了解最常用方法之间的优缺点会很有帮助。
| 方法 | 办公室要求 | 列标题 | 表现 | 服务器安全 |
|---|---|---|---|---|
| Microsoft Office Interop | 是 | 手册 | 慢的 | 无 |
| OpenXml SDK | 无 | 手册 | 快速地 | 是 |
| ClosedXML | 无 | 手册 | 好的 | 是 |
| IronXL | 无 | 自动翻译 | 快速地 | 是 |
基于互操作性的解决方案需要在服务器上安装 Excel,这既是许可问题,也是部署复杂性问题。 OpenXml SDK 功能强大但底层——编写标题行需要手动构建 XML 结构。 IronXL和 ClosedXML 都提供了更高级别的 API,但 IronXL 的WorkBook 和 WorkSheet 模型与开发人员对电子表格的现有思维方式非常接近。
如何导出为 CSV 而不是 XLSX?
当您需要CSV 文件而不是 Excel 工作簿时,也适用相同的导出模式。 更改文件格式和扩展名:
WorkBook csvWorkbook = WorkBook.Create(ExcelFileFormat.CSV);
WorkSheet csvSheet = csvWorkbook.CreateWorkSheet("Data");
// Headers and data loops remain identical
using SaveFileDialog dialog = new SaveFileDialog
{
Filter = "CSV Files|*.csv",
FileName = "DataGridView_Export.csv"
};
if (dialog.ShowDialog() == DialogResult.OK)
csvWorkbook.SaveAs(dialog.FileName);
WorkBook csvWorkbook = WorkBook.Create(ExcelFileFormat.CSV);
WorkSheet csvSheet = csvWorkbook.CreateWorkSheet("Data");
// Headers and data loops remain identical
using SaveFileDialog dialog = new SaveFileDialog
{
Filter = "CSV Files|*.csv",
FileName = "DataGridView_Export.csv"
};
if (dialog.ShowDialog() == DialogResult.OK)
csvWorkbook.SaveAs(dialog.FileName);
Imports System.Windows.Forms
Dim csvWorkbook As WorkBook = WorkBook.Create(ExcelFileFormat.CSV)
Dim csvSheet As WorkSheet = csvWorkbook.CreateWorkSheet("Data")
' Headers and data loops remain identical
Using dialog As New SaveFileDialog With {
.Filter = "CSV Files|*.csv",
.FileName = "DataGridView_Export.csv"
}
If dialog.ShowDialog() = DialogResult.OK Then
csvWorkbook.SaveAs(dialog.FileName)
End If
End Using
当使用系统不支持 XLSX 格式,或者对于非常大的数据集来说文件大小是一个问题时,CSV 导出非常有用。
如何在导出的文件中设置标题行的样式?
导出的数据如果标题行在视觉上清晰可见,则更易于阅读。 IronXL提供单元格样式选项,允许您在写入标题值后应用粗体文本、背景颜色和字体大小:
// Write headers and apply bold styling
for (int col = 0; col < dataGridView1.Columns.Count; col++)
{
string cellAddress = worksheet.GetCellAddress(0, col);
worksheet[cellAddress].Value = dataGridView1.Columns[col].HeaderText;
worksheet[cellAddress].Style.Font.Bold = true;
worksheet[cellAddress].Style.SetBackgroundColor("#4472C4");
worksheet[cellAddress].Style.Font.FontColor = "#FFFFFF";
}
// Write headers and apply bold styling
for (int col = 0; col < dataGridView1.Columns.Count; col++)
{
string cellAddress = worksheet.GetCellAddress(0, col);
worksheet[cellAddress].Value = dataGridView1.Columns[col].HeaderText;
worksheet[cellAddress].Style.Font.Bold = true;
worksheet[cellAddress].Style.SetBackgroundColor("#4472C4");
worksheet[cellAddress].Style.Font.FontColor = "#FFFFFF";
}
' Write headers and apply bold styling
For col As Integer = 0 To dataGridView1.Columns.Count - 1
Dim cellAddress As String = worksheet.GetCellAddress(0, col)
worksheet(cellAddress).Value = dataGridView1.Columns(col).HeaderText
worksheet(cellAddress).Style.Font.Bold = True
worksheet(cellAddress).Style.SetBackgroundColor("#4472C4")
worksheet(cellAddress).Style.Font.FontColor = "#FFFFFF"
Next col
带有样式的标题行使导出的文件可以直接用于报告,而无需收件人手动应用格式。 有关样式属性的完整列表,请参阅IronXL单元格样式参考。
如何开始免费试用?
IronXL提供免费试用版,让您可以测试所有功能,包括本文中所示的导出工作流程。 使用上面显示的任一NuGet命令安装该软件包,在IronXL许可页面上生成试用密钥,并在首次调用库之前将其设置到您的应用程序中:
IronXL.License.LicenseKey = "YOUR-LICENSE-KEY-HERE";
IronXL.License.LicenseKey = "YOUR-LICENSE-KEY-HERE";
Imports IronXL
IronXL.License.LicenseKey = "YOUR-LICENSE-KEY-HERE"
该试用版会生成功能齐全的 Excel 文件,并且不会限制哪些功能可用。 对于生产部署,许可选项涵盖单个开发人员、团队和免版税再分发。
对于相关的导出场景,请探索IronXL如何处理将 DataTables 直接导出到 Excel 、将 Excel 文件读取到 DataGridViews以及从导出的数据创建图表。
为您的用例选择合适的出口策略
本教程中展示的逐单元格导出方法简单直接,适用于大多数 Windows Forms 应用程序。 如果您的 DataGridView 绑定到 DataTable,您还可以将 DataTable 直接传递给 IronXL 的DataTable 到工作表转换器,该转换器会自动处理标题行。 如果列布局固定,并且您希望代码路径最短,请选择直接使用 DataTable 的方法。 当您需要对每个单元格应用条件逻辑时,请选择逐单元格手动操作的方法——例如,突出显示值超出可接受范围的单元格,或者在写入之前显式映射可空类型。
对于生成可下载 Excel 报表的服务器端场景(例如ASP.NET Core控制器),同样的IronXL API 也适用。 唯一的区别在于保存目标:不是调用 SaveAs(filePath),而是调用 workbook.ToByteArray(),并将字节写入内容类型为 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 的 HTTP 响应。 无论输出到文件还是流,列标题的传输方式都完全相同。
常见问题解答
如何在 C# 中将 DataGridView 数据导出到 Excel?
使用IronXL库创建一个 Excel 工作簿,遍历 DataGridView 列,将标题写入第 0 行,然后遍历数据行,从第 1 行开始写入单元格值。使用 SaveAs 保存工作簿。
使用 IronXL 导出数据有什么好处?
IronXL提供了一个用于 Excel 操作的高级 API,无需安装 Microsoft Office。它能够处理标题、数据类型和文件格式,无需手动操作 XML。
从 DataGridView 导出到 Excel 时能否保留列标题?
是的。在写入任何数据行之前,请读取每个 DataGridView 列的 HeaderText 属性,并将其写入IronXL工作表的第 0 行。
开始使用 IronXL 导出到 Excel 是否需要试用版?
我们提供免费试用版,试用版可访问所有功能。请安装NuGet包,生成试用密钥,并在调用任何库之前设置IronXL。
IronXL.Excel 能否在导出到 Excel 时处理大型 DataGridView 数据集?
是的。对于非常大的网格,可以使用 Task.Run 在后台线程中运行导出操作,以在IronXL写入数据时保持 UI 的响应性。
使用 IronXL.Excel 将 DataGridView 导出到 Excel 的步骤是什么?
安装IronXL,创建一个 WorkBook 和 WorkSheet,遍历 DataGridView 列以在第 0 行写入标题,遍历行以从第 1 行开始写入数据,然后调用 SaveAs 保存文件。
为什么选择 IronXL 而不是其他导出到 Excel 的库?
IronXL无需安装 Office,可在服务器上运行,并提供简洁的工作簿/工作表 API,自然地与开发人员对电子表格的思考方式相匹配——并具有强大的文档和积极的支持。
IronXL 是否提供故障排除支持?
是的。IronXL 为授权客户提供详细的 API 文档、代码示例和直接的工程支持。
IronXL.Excel 能否自定义 Excel 导出过程?
是的。写入数据后,您可以使用 IronXL 的样式 API 对任何单元格区域应用粗体字体、背景颜色、边框和数字格式。
IronXL 是否支持将数据导出为 Excel 以外的其他格式?
是的。IronXL 支持 XLSX、XLS、CSV 和 TSV 格式。IronXL格式只需更改传递给 WorkBook.Create 的 ExcelFileFormat 枚举值即可。


