在C#项目开发中,使用NPOI操作Excel是处理数据导出的常见方案,当遇到多级表头、跨行跨列合并的复杂表头需求时,需要掌握NPOI中合并单元格的相关API和逻辑处理方式。

环境准备
首先需要安装NPOI相关的NuGet包,在项目中引入以下依赖:
- NPOI
- NPOI.OOXML
- NPOI.HPSF
- NPOI.HSSF
安装完成后,在代码文件中引入对应的命名空间:
using NPOI.HSSF.UserModel; using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; using System.IO;
合并单元格核心API说明
NPOI中合并单元格主要通过ISheet接口的AddMergedRegion方法实现,该方法接收一个CellRangeAddress对象作为参数,用于指定合并的区域范围。
CellRangeAddress的构造函数参数说明:
| 参数位置 | 参数含义 |
|---|---|
| 第一个参数 | 合并区域的起始行索引(从0开始) |
| 第二个参数 | 合并区域的结束行索引(从0开始) |
| 第三个参数 | 合并区域的起始列索引(从0开始) |
| 第四个参数 | 合并区域的结束列索引(从0开始) |
动态生成复杂合并表头实现步骤
1. 创建Workbook和Sheet
根据需求选择创建HSSFWorkbook(对应xls格式)或者XSSFWorkbook(对应xlsx格式),然后创建对应的Sheet对象。
// 创建xlsx格式的Workbook
IWorkbook workbook = new XSSFWorkbook();
// 创建Sheet,名称为数据表
ISheet sheet = workbook.CreateSheet("数据表");
2. 创建表头行并设置单元格样式
先定义表头的基础样式,包括字体、对齐方式、边框等,方便后续复用。
// 创建表头样式 ICellStyle headerStyle = workbook.CreateCellStyle(); // 设置水平居中 headerStyle.Alignment = HorizontalAlignment.Center; // 设置垂直居中 headerStyle.VerticalAlignment = VerticalAlignment.Center; // 设置边框 headerStyle.BorderTop = BorderStyle.Thin; headerStyle.BorderBottom = BorderStyle.Thin; headerStyle.BorderLeft = BorderStyle.Thin; headerStyle.BorderRight = BorderStyle.Thin; // 创建字体 IFont headerFont = workbook.CreateFont(); headerFont.FontHeightInPoints = 11; headerFont.IsBold = true; headerStyle.SetFont(headerFont);
3. 动态生成多级合并表头
假设我们需要生成一个三级表头,结构如下:
- 第一级:基本信息(合并第0行0-3列)、成绩信息(合并第0行4-7列)
- 第二级:基本信息下分姓名(第1行0列)、年龄(第1行1列)、性别(合并第1行2-3列);成绩信息下分语文(第1行4列)、数学(第1行5列)、英语(合并第1行6-7列)
- 第三级:性别下分男(第2行2列)、女(第2行3列);英语下分听力(第2行6列)、笔试(第2行7列)
实现代码如下:
// 创建三行表头行
IRow row0 = sheet.CreateRow(0);
IRow row1 = sheet.CreateRow(1);
IRow row2 = sheet.CreateRow(2);
// 设置列宽,适配内容
for (int i = 0; i < 8; i++)
{
sheet.SetColumnWidth(i, 15 * 256);
}
// 第一级表头内容
row0.CreateCell(0).SetCellValue("基本信息");
row0.CreateCell(4).SetCellValue("成绩信息");
// 设置第一级表头样式
for (int i = 0; i < 8; i++)
{
if (i < 4)
{
row0.GetCell(i).CellStyle = headerStyle;
}
else
{
row0.GetCell(i).CellStyle = headerStyle;
}
}
// 合并第一级表头:基本信息合并0-0行,0-3列
sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, 3));
// 合并第一级表头:成绩信息合并0-0行,4-7列
sheet.AddMergedRegion(new CellRangeAddress(0, 0, 4, 7));
// 第二级表头内容
row1.CreateCell(0).SetCellValue("姓名");
row1.CreateCell(1).SetCellValue("年龄");
row1.CreateCell(2).SetCellValue("性别");
row1.CreateCell(4).SetCellValue("语文");
row1.CreateCell(5).SetCellValue("数学");
row1.CreateCell(6).SetCellValue("英语");
// 设置第二级表头样式
for (int i = 0; i < 8; i++)
{
if (row1.GetCell(i) != null)
{
row1.GetCell(i).CellStyle = headerStyle;
}
}
// 合并第二级表头:性别合并1-1行,2-3列
sheet.AddMergedRegion(new CellRangeAddress(1, 1, 2, 3));
// 合并第二级表头:英语合并1-1行,6-7列
sheet.AddMergedRegion(new CellRangeAddress(1, 1, 6, 7));
// 第三级表头内容
row2.CreateCell(0).SetCellValue("");
row2.CreateCell(1).SetCellValue("");
row2.CreateCell(2).SetCellValue("男");
row2.CreateCell(3).SetCellValue("女");
row2.CreateCell(4).SetCellValue("");
row2.CreateCell(5).SetCellValue("");
row2.CreateCell(6).SetCellValue("听力");
row2.CreateCell(7).SetCellValue("笔试");
// 设置第三级表头样式
for (int i = 0; i < 8; i++)
{
if (row2.GetCell(i) != null)
{
row2.GetCell(i).CellStyle = headerStyle;
}
}
// 合并第三级表头:姓名合并0-2行,0列
sheet.AddMergedRegion(new CellRangeAddress(0, 2, 0, 0));
// 合并第三级表头:年龄合并0-2行,1列
sheet.AddMergedRegion(new CellRangeAddress(0, 2, 1, 1));
// 合并第三级表头:语文合并0-2行,4列
sheet.AddMergedRegion(new CellRangeAddress(0, 2, 4, 4));
// 合并第三级表头:数学合并0-2行,5列
sheet.AddMergedRegion(new CellRangeAddress(0, 2, 5, 5));
4. 导出Excel文件
将生成的Workbook写入文件流,完成Excel导出。
// 导出文件路径
string filePath = "D:\复杂表头示例.xlsx";
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
workbook.Write(fs);
}
注意事项
- 合并单元格时,只需要给合并区域的第一个单元格设置值即可,其他被合并的单元格不需要单独赋值
- 合并区域的坐标不要超出Sheet的实际行和列范围,否则会抛出异常
- 如果表头结构需要动态配置,可以将表头的结构信息定义为配置类,通过读取配置动态生成合并逻辑,提升代码的灵活性
- 操作完成后及时释放Workbook资源,避免内存泄漏