如何让C#生成的自定义Excel匹配SharePoint导出Excel的外观
刚好之前做过类似的需求——用CSOM导出SharePoint列表数据到Excel,要完全复刻原生导出的样式,尤其是那个交替行底纹。折腾了一阵终于搞定了,下面是我验证过的方案,能完美适配任意行数,用OpenXML来实现(毕竟CSOM结合OpenXML是自定义Excel导出的常规操作):
实现思路
SharePoint原生导出的Excel有几个核心样式特点:
- 表头:深蓝色底(RGB #4472C4)、白色加粗字体、细边框
- 数据行:交替行底纹(奇数行白色,偶数行浅灰色 #F5F5F5)、黑色常规字体、细边框
为了适配任意行数,不要逐行设置样式,而是用OpenXML的条件格式功能,通过公式自动识别奇偶行并应用对应样式,这样不管数据有10行还是1000行,都能自动生效。
具体代码实现
首先需要引用DocumentFormat.OpenXml和DocumentFormat.OpenXml.Spreadsheet命名空间,然后分三部分实现:样式表定义、交替行条件格式添加、整合到CSOM导出流程。
1. 定义匹配原生样式的样式表
private static Stylesheet CreateSharePointLikeStylesheet() { var stylesheet = new Stylesheet(); // 字体集合:数据行黑字,表头白字加粗 var fonts = new Fonts(); // 数据行字体(默认黑字11号) fonts.Append(new Font( new Color { Rgb = HexBinaryValue.FromString("000000") }, new FontSize { Val = 11 })); // 表头字体(白字11号加粗) fonts.Append(new Font( new Color { Rgb = HexBinaryValue.FromString("FFFFFF") }, new FontSize { Val = 11 }, new Bold())); // 填充集合:默认白色、表头蓝色、交替行浅灰 var fills = new Fills(); // 默认空填充 fills.Append(new Fill(new PatternFill { PatternType = PatternValues.None })); // 125%灰度填充(OpenXML默认需要) fills.Append(new Fill(new PatternFill { PatternType = PatternValues.Gray125 })); // 表头填充(SharePoint原生蓝色) fills.Append(new Fill(new PatternFill { PatternType = PatternValues.Solid, ForegroundColor = new ForegroundColor { Rgb = HexBinaryValue.FromString("4472C4") }, BackgroundColor = new BackgroundColor { Indexed = 64 } })); // 交替行浅灰填充 fills.Append(new Fill(new PatternFill { PatternType = PatternValues.Solid, ForegroundColor = new ForegroundColor { Rgb = HexBinaryValue.FromString("F5F5F5") }, BackgroundColor = new BackgroundColor { Indexed = 64 } })); // 边框集合:细边框(和原生导出一致) var borders = new Borders(); var thinBorder = new Border( new LeftBorder { Style = BorderStyleValues.Thin }, new RightBorder { Style = BorderStyleValues.Thin }, new TopBorder { Style = BorderStyleValues.Thin }, new BottomBorder { Style = BorderStyleValues.Thin }, new DiagonalBorder()); borders.Append(new Border()); // 默认无边框 borders.Append(thinBorder); // 数据/表头单元格边框 // 单元格格式集合:绑定字体、填充、边框 var cellFormats = new CellFormats(); // 默认格式 cellFormats.Append(new CellFormat { FontId = 0, FillId = 0, BorderId = 0 }); // 表头单元格格式 cellFormats.Append(new CellFormat { FontId = 1, FillId = 2, BorderId = 1, ApplyFont = true, ApplyFill = true, ApplyBorder = true }); // 数据行奇数行格式(白色背景) cellFormats.Append(new CellFormat { FontId = 0, FillId = 0, BorderId = 1, ApplyFont = true, ApplyFill = true, ApplyBorder = true }); // 数据行偶数行格式(浅灰背景) cellFormats.Append(new CellFormat { FontId = 0, FillId = 3, BorderId = 1, ApplyFont = true, ApplyFill = true, ApplyBorder = true }); // 把所有样式部分添加到样式表 stylesheet.Append(fonts); stylesheet.Append(fills); stylesheet.Append(borders); stylesheet.Append(cellFormats); return stylesheet; }
2. 添加交替行条件格式(适配任意行数)
private static void AddAlternatingRowConditionalFormatting(Worksheet worksheet, uint dataStartRow, uint dataEndRow, uint columnCount) { var conditionalFormatting = new ConditionalFormatting(); // 设置格式应用范围:从数据起始行到结束行,覆盖所有列 var rangeRef = new SqRef($"{GetExcelColumnName(1)}{dataStartRow}:{GetExcelColumnName((int)columnCount)}{dataEndRow}"); conditionalFormatting.Append(rangeRef); // 偶数行规则:公式判断行号为偶数,应用浅灰样式 var evenRowRule = new CfRule { Type = CfRuleType.Formula, FormatId = 3, // 对应上面定义的偶数行格式ID Priority = 1 }; evenRowRule.Append(new Formula("MOD(ROW(),2)=0")); conditionalFormatting.Append(evenRowRule); // 奇数行规则:公式判断行号为奇数,应用白色样式 var oddRowRule = new CfRule { Type = CfRuleType.Formula, FormatId = 2, // 对应奇数行格式ID Priority = 2 }; oddRowRule.Append(new Formula("MOD(ROW(),2)=1")); conditionalFormatting.Append(oddRowRule); // 把条件格式添加到工作表 worksheet.Append(conditionalFormatting); } // 辅助方法:将列索引转换为Excel列名(比如1→A,27→AA) private static string GetExcelColumnName(int columnIndex) { string columnName = ""; while (columnIndex > 0) { int remainder = (columnIndex - 1) % 26; columnName = Convert.ToChar('A' + remainder) + columnName; columnIndex = (columnIndex - 1) / 26; } return columnName; }
3. 整合到CSOM导出流程
当你用CSOM获取到SharePoint列表数据后,按以下步骤整合样式:
// 1. 创建Excel文档(示例用OpenXML的SpreadsheetDocument) using (var document = SpreadsheetDocument.Create("ExportedListData.xlsx", SpreadsheetDocumentType.Workbook)) { var workbookPart = document.AddWorkbookPart(); workbookPart.Workbook = new Workbook(); var worksheetPart = workbookPart.AddNewPart<WorksheetPart>(); worksheetPart.Worksheet = new Worksheet(new SheetData()); var sheets = workbookPart.Workbook.AppendChild(new Sheets()); sheets.Append(new Sheet { Id = workbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = "List Data" }); // 2. 添加样式表到文档 var stylesPart = workbookPart.AddNewPart<WorkbookStylesPart>(); stylesPart.Stylesheet = CreateSharePointLikeStylesheet(); stylesPart.Stylesheet.Save(); var sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>(); // 3. 写入表头(第1行,应用表头样式) var headerRow = new Row { RowIndex = 1 }; foreach (var columnName in listColumnNames) // listColumnNames是你的列表字段名集合 { var cell = new Cell { CellValue = new CellValue(columnName), DataType = CellValues.String, StyleIndex = 1 // 对应表头样式ID }; headerRow.Append(cell); } sheetData.Append(headerRow); // 4. 写入数据行(从第2行开始) uint rowIndex = 2; foreach (var listItem in listItems) // listItems是你从CSOM获取的列表项集合 { var dataRow = new Row { RowIndex = rowIndex }; foreach (var value in listItem.FieldValues) { var cell = new Cell { CellValue = new CellValue(value.ToString()), DataType = CellValues.String, StyleIndex = 2 // 先默认应用奇数行样式,后续条件格式会覆盖偶数行 }; dataRow.Append(cell); } sheetData.Append(dataRow); rowIndex++; } // 5. 添加交替行条件格式 AddAlternatingRowConditionalFormatting( worksheetPart.Worksheet, dataStartRow: 2, dataEndRow: rowIndex - 1, columnCount: (uint)listColumnNames.Count); worksheetPart.Worksheet.Save(); workbookPart.Workbook.Save(); }
关键注意事项
- 如果你的SharePoint站点使用了自定义主题,表头的蓝色RGB值可能需要调整,用取色器吸取原生导出Excel的表头颜色即可。
- 使用条件格式而非逐行设置样式,不仅能适配任意行数,还能提升大数据量下的导出性能。
- 代码中的样式ID要严格对应,比如表头样式是
StyleIndex=1,偶数行格式ID是3,别搞混了。
内容的提问来源于stack exchange,提问作者munmun poddar
相关产品推荐
相关产品推荐

