您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# C#中怎么使用Npoi导出Excel合并行列
## 目录
1. [Npoi简介与环境配置](#npoi简介与环境配置)
2. [基础Excel导出操作](#基础excel导出操作)
3. [单元格合并基础](#单元格合并基础)
4. [行列合并实战技巧](#行列合并实战技巧)
5. [复杂合并场景实现](#复杂合并场景实现)
6. [样式与格式处理](#样式与格式处理)
7. [性能优化建议](#性能优化建议)
8. [常见问题解决方案](#常见问题解决方案)
9. [完整项目示例](#完整项目示例)
10. [总结与扩展](#总结与扩展)
## Npoi简介与环境配置
### 什么是Npoi
NPOI是.NET平台下的开源Excel操作库,可以读写Office 97-2003格式的Excel文件(.xls)和2007+格式的Excel文件(.xlsx),无需安装Microsoft Office。
### 主要功能特性
- 支持xls/xlsx格式
- 低内存消耗
- 丰富的API接口
- 支持公式计算
- 跨平台运行
### 安装配置
通过NuGet包管理器安装:
```bash
Install-Package NPOI
Install-Package NPOI.OOXML
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel; // for xlsx
using NPOI.HSSF.UserModel; // for xls
using NPOI.SS.Util;
// 创建2007+格式工作簿
IWorkbook workbook = new XSSFWorkbook();
// 创建97-2003格式工作簿
// IWorkbook workbook = new HSSFWorkbook();
ISheet sheet = workbook.CreateSheet("Sheet1");
// 创建行
IRow row = sheet.CreateRow(0);
// 创建单元格并赋值
row.CreateCell(0).SetCellValue("姓名");
row.CreateCell(1).SetCellValue("年龄");
// 添加数据行
IRow dataRow = sheet.CreateRow(1);
dataRow.CreateCell(0).SetCellValue("张三");
dataRow.CreateCell(1).SetCellValue(25);
using (FileStream fs = new FileStream("test.xlsx", FileMode.Create))
{
workbook.Write(fs);
}
Excel中的合并单元格是通过指定起始和结束的行列索引来实现的,NPOI提供了CellRangeAddress
类来处理合并。
// 合并第1行的第1到3列
sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, 2));
// 合并第1到3行的第1列
sheet.AddMergedRegion(new CellRangeAddress(0, 2, 0, 0));
// 创建标题行并合并
IRow titleRow = sheet.CreateRow(0);
titleRow.CreateCell(0).SetCellValue("员工信息表");
sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, 3));
// 创建表头
IRow headerRow = sheet.CreateRow(1);
headerRow.CreateCell(0).SetCellValue("ID");
headerRow.CreateCell(1).SetCellValue("姓名");
// ...其他表头
// 合并多列创建分类标题
IRow categoryRow = sheet.CreateRow(2);
categoryRow.CreateCell(0).SetCellValue("基本信息");
sheet.AddMergedRegion(new CellRangeAddress(2, 2, 0, 2));
categoryRow.CreateCell(3).SetCellValue("联系方式");
sheet.AddMergedRegion(new CellRangeAddress(2, 2, 3, 5));
// 合并多行创建部门分组
IRow deptRow = sheet.CreateRow(3);
deptRow.CreateCell(0).SetCellValue("研发部");
sheet.AddMergedRegion(new CellRangeAddress(3, 6, 0, 0));
// 添加研发部人员数据...
// 创建跨行跨列的合并区域
IRow crossRow = sheet.CreateRow(7);
crossRow.CreateCell(0).SetCellValue("年度汇总");
sheet.AddMergedRegion(new CellRangeAddress(7, 9, 0, 3));
// 根据数据动态合并相同内容的单元格
Dictionary<string, List<int>> sameValueRows = new Dictionary<string, List<int>>();
// 收集需要合并的行
for (int i = 1; i <= 10; i++)
{
IRow currentRow = sheet.GetRow(i);
string cellValue = currentRow.GetCell(0).StringCellValue;
if (!sameValueRows.ContainsKey(cellValue))
{
sameValueRows[cellValue] = new List<int>();
}
sameValueRows[cellValue].Add(i);
}
// 执行合并
foreach (var item in sameValueRows)
{
if (item.Value.Count > 1)
{
int firstRow = item.Value.First();
int lastRow = item.Value.Last();
sheet.AddMergedRegion(new CellRangeAddress(firstRow, lastRow, 0, 0));
}
}
// 第一层表头
IRow header1 = sheet.CreateRow(0);
header1.CreateCell(0).SetCellValue("公司月度报表");
sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, 5));
// 第二层表头
IRow header2 = sheet.CreateRow(1);
header2.CreateCell(0).SetCellValue("销售数据");
sheet.AddMergedRegion(new CellRangeAddress(1, 1, 0, 2));
header2.CreateCell(3).SetCellValue("成本数据");
sheet.AddMergedRegion(new CellRangeAddress(1, 1, 3, 5));
// 第三层表头
IRow header3 = sheet.CreateRow(2);
header3.CreateCell(0).SetCellValue("产品A");
header3.CreateCell(1).SetCellValue("产品B");
// ...其他产品
List<Employee> employees = GetEmployees(); // 获取员工数据
int currentRow = 3;
string lastDept = string.Empty;
int deptStartRow = 3;
foreach (var emp in employees.OrderBy(e => e.Department))
{
if (emp.Department != lastDept)
{
if (currentRow > deptStartRow)
{
// 合并上一个部门的单元格
sheet.AddMergedRegion(new CellRangeAddress(
deptStartRow, currentRow - 1, 0, 0));
}
lastDept = emp.Department;
deptStartRow = currentRow;
}
IRow row = sheet.CreateRow(currentRow);
row.CreateCell(0).SetCellValue(emp.Department);
row.CreateCell(1).SetCellValue(emp.Name);
// ...其他列
currentRow++;
}
// 合并最后一个部门的单元格
if (currentRow > deptStartRow)
{
sheet.AddMergedRegion(new CellRangeAddress(
deptStartRow, currentRow - 1, 0, 0));
}
// 创建交叉报表结构
IRow cornerRow = sheet.CreateRow(0);
cornerRow.CreateCell(0).SetCellValue("项目/月份");
sheet.AddMergedRegion(new CellRangeAddress(0, 1, 0, 0));
// 创建月份行
IRow monthRow = sheet.CreateRow(0);
for (int i = 1; i <= 12; i++)
{
monthRow.CreateCell(i).SetCellValue($"{i}月");
}
sheet.AddMergedRegion(new CellRangeAddress(0, 0, 1, 12));
// 创建季度行
IRow quarterRow = sheet.CreateRow(1);
quarterRow.CreateCell(1).SetCellValue("Q1");
sheet.AddMergedRegion(new CellRangeAddress(1, 1, 1, 3));
// ...其他季度
ICellStyle style = workbook.CreateCellStyle();
IFont font = workbook.CreateFont();
font.FontName = "微软雅黑";
font.FontHeightInPoints = 12;
font.Boldweight = (short)FontBoldWeight.BOLD;
style.SetFont(font);
// 水平居中
style.Alignment = HorizontalAlignment.Center;
// 垂直居中
style.VerticalAlignment = VerticalAlignment.Center;
// 应用样式
titleRow.GetCell(0).CellStyle = style;
ICellStyle mergedStyle = workbook.CreateCellStyle();
mergedStyle.Alignment = HorizontalAlignment.Center;
mergedStyle.VerticalAlignment = VerticalAlignment.Center;
mergedStyle.FillForegroundColor = IndexedColors.Grey25Percent.Index;
mergedStyle.FillPattern = FillPattern.SolidForeground;
// 设置边框
mergedStyle.BorderTop = BorderStyle.Thin;
mergedStyle.BorderBottom = BorderStyle.Thin;
mergedStyle.BorderLeft = BorderStyle.Thin;
mergedStyle.BorderRight = BorderStyle.Thin;
// 应用样式到合并区域
for (int i = 0; i <= 2; i++)
{
IRow row = sheet.GetRow(i) ?? sheet.CreateRow(i);
for (int j = 0; j <= 2; j++)
{
row.CreateCell(j).CellStyle = mergedStyle;
}
}
// 自动调整列宽(合并单元格后特别重要)
for (int i = 0; i < 10; i++)
{
sheet.AutoSizeColumn(i);
// 设置最小宽度
if (sheet.GetColumnWidth(i) < 3000)
{
sheet.SetColumnWidth(i, 3000);
}
}
// 创建基于流的Workbook,限制内存中保留的行数
SXSSFWorkbook workbook = new SXSSFWorkbook(100); // 保留100行在内存中
int batchSize = 500;
for (int i = 0; i < totalCount; i += batchSize)
{
var batchData = data.Skip(i).Take(batchSize);
// 处理批次数据...
}
// 预定义样式字典
Dictionary<string, ICellStyle> styleCache = new Dictionary<string, ICellStyle>();
ICellStyle GetOrCreateStyle(string styleKey)
{
if (!styleCache.ContainsKey(styleKey))
{
ICellStyle style = workbook.CreateCellStyle();
// 根据styleKey配置样式...
styleCache[styleKey] = style;
}
return styleCache[styleKey];
}
// 收集所有合并区域
List<CellRangeAddress> mergedRegions = new List<CellRangeAddress>();
// 添加多个合并区域
mergedRegions.Add(new CellRangeAddress(0, 0, 0, 2));
mergedRegions.Add(new CellRangeAddress(1, 3, 0, 0));
// ...
// 一次性添加
foreach (var region in mergedRegions)
{
sheet.AddMergedRegion(region);
}
// 确保合并区域的单元格都设置了居中样式
ICellStyle centerStyle = workbook.CreateCellStyle();
centerStyle.Alignment = HorizontalAlignment.Center;
centerStyle.VerticalAlignment = VerticalAlignment.Center;
// 应用样式到合并区域的所有单元格
for (int r = region.FirstRow; r <= region.LastRow; r++)
{
IRow row = sheet.GetRow(r) ?? sheet.CreateRow(r);
for (int c = region.FirstColumn; c <= region.LastColumn; c++)
{
row.GetCell(c).CellStyle = centerStyle;
}
}
// 设置合并区域的完整边框
ICellStyle borderStyle = workbook.CreateCellStyle();
borderStyle.BorderTop = BorderStyle.Thin;
borderStyle.BorderBottom = BorderStyle.Thin;
borderStyle.BorderLeft = BorderStyle.Thin;
borderStyle.BorderRight = BorderStyle.Thin;
// 应用样式到合并区域的所有单元格
for (int r = region.FirstRow; r <= region.LastRow; r++)
{
IRow row = sheet.GetRow(r) ?? sheet.CreateRow(r);
for (int c = region.FirstColumn; c <= region.LastColumn; c++)
{
row.GetCell(c).CellStyle = borderStyle;
}
}
public void ExportEmployeeReport(List<Employee> employees, string filePath)
{
// 创建工作簿
IWorkbook workbook = new XSSFWorkbook();
ISheet sheet = workbook.CreateSheet("员工信息");
// 1. 创建标题行
IRow titleRow = sheet.CreateRow(0);
titleRow.CreateCell(0).SetCellValue("公司员工信息汇总表");
sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, 4));
// 设置标题样式
ICellStyle titleStyle = CreateTitleStyle(workbook);
titleRow.GetCell(0).CellStyle = titleStyle;
// 2. 创建表头
IRow headerRow = sheet.CreateRow(1);
string[] headers = { "部门", "工号", "姓名", "职位", "入职日期" };
for (int i = 0; i < headers.Length; i++)
{
headerRow.CreateCell(i).SetCellValue(headers[i]);
}
// 3. 填充数据并处理合并
int rowIndex = 2;
string lastDept = string.Empty;
int deptStartRow = 2;
foreach (var emp in employees.OrderBy(e => e.Department))
{
IRow row = sheet.CreateRow(rowIndex);
// 处理部门合并
if (emp.Department != lastDept)
{
if (rowIndex > deptStartRow)
{
sheet.AddMergedRegion(new CellRangeAddress(
deptStartRow, rowIndex - 1, 0, 0));
}
lastDept = emp.Department;
deptStartRow = rowIndex;
}
// 填充数据
row.CreateCell(0).SetCellValue(emp.Department);
row.CreateCell(1).SetCellValue(emp.EmployeeId);
row.CreateCell(2).SetCellValue(emp.Name);
row.CreateCell(3).SetCellValue(emp.Position);
row.CreateCell(4).SetCellValue(emp.HireDate.ToString("yyyy-MM-dd"));
rowIndex++;
}
// 合并最后一个部门
if (rowIndex > deptStartRow)
{
sheet.AddMergedRegion(new CellRangeAddress(
deptStartRow, rowIndex - 1, 0, 0));
}
// 4. 设置样式
SetDataCellStyles(workbook, sheet, 1, rowIndex - 1, 0, 4);
// 5. 自动调整列宽
for (int i = 0; i < headers.Length; i++)
{
sheet.AutoSizeColumn(i);
}
// 6. 保存文件
using (FileStream fs = new FileStream(filePath, FileMode.Create))
{
workbook.Write(fs);
}
}
private ICellStyle CreateTitleStyle(IWorkbook workbook)
{
ICellStyle style = workbook.CreateCellStyle();
IFont font = workbook.CreateFont();
font.FontName = "微软雅黑";
font.FontHeightInPoints = 16;
font.Boldweight = (short)FontBoldWeight.BOLD;
style.SetFont(font);
style.Alignment = HorizontalAlignment.Center;
style.VerticalAlignment = VerticalAlignment.Center;
return style;
}
private void SetDataCellStyles(IWorkbook workbook, ISheet sheet,
int startRow, int endRow, int startCol, int endCol)
{
ICellStyle style = workbook.CreateCellStyle();
style.Alignment = HorizontalAlignment.Center;
style.VerticalAlignment = VerticalAlignment.Center;
style.BorderTop = BorderStyle.Thin;
style.BorderBottom = BorderStyle.Thin;
style.BorderLeft = BorderStyle.Thin;
style.BorderRight = BorderStyle.Thin;
for (int r = startRow; r <= endRow; r++)
{
IRow row = sheet.GetRow(r);
for (int c = startCol; c <= endCol; c++)
{
row.GetCell(c).CellStyle = style;
}
}
}
CellRangeAddress
定义合并区域免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。