使用OpenXML C#动态替换Word文档中的图表数值
使用OpenXML C#动态更新Word图表数据的实现方案
核心思路
Word文档中的图表数据实际存储在嵌入式Excel工作簿中(对应OpenXML里的EmbeddedPackagePart),要动态更新图表,本质就是修改这个嵌入式Excel里的单元格数值,Word会自动关联更新图表展示。
实现步骤
- 加载目标Word文档,遍历文档内所有
ChartPart(每个图表对应一个ChartPart) - 从
ChartPart中获取对应的EmbeddedPackagePart,这就是存储图表数据的Excel包 - 打开Excel包的流,用OpenXML SDK操作Excel工作簿,修改指定单元格的数值
- 将修改后的Excel流写回
EmbeddedPackagePart - 保存并关闭Word文档
完整代码示例
首先需要安装NuGet包:DocumentFormat.OpenXml
using System.IO; using System.Linq; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Spreadsheet; public static void UpdateWordChartData(string wordFilePath, int chartIndex, string sheetName, string cellAddress, decimal newValue) { // 以可写模式打开Word文档 using (var wordDoc = WordprocessingDocument.Open(wordFilePath, true)) { // 获取所有ChartPart,按索引定位目标图表(索引从0开始) var chartParts = wordDoc.MainDocumentPart.ChartParts.ToList(); if (chartIndex >= chartParts.Count) throw new ArgumentOutOfRangeException(nameof(chartIndex), "指定的图表索引不存在"); var targetChartPart = chartParts[chartIndex]; var excelPackagePart = targetChartPart.EmbeddedPackagePart; // 操作嵌入式Excel工作簿 using (var excelStream = new MemoryStream()) { excelPackagePart.GetStream().CopyTo(excelStream); excelStream.Position = 0; using (var excelDoc = SpreadsheetDocument.Open(excelStream, true)) { var workbookPart = excelDoc.WorkbookPart; var sheet = workbookPart.Workbook.Descendants<Sheet>().FirstOrDefault(s => s.Name == sheetName); if (sheet == null) throw new ArgumentException($"找不到名为{sheetName}的工作表"); var worksheetPart = (WorksheetPart)workbookPart.GetPartById(sheet.Id); var cell = GetCell(worksheetPart.Worksheet, cellAddress); // 修改单元格数值 cell.CellValue = new CellValue(newValue.ToString()); cell.DataType = new EnumValue<CellValues>(CellValues.Number); worksheetPart.Worksheet.Save(); } // 将修改后的Excel流写回EmbeddedPackagePart excelStream.Position = 0; excelPackagePart.FeedData(excelStream); } wordDoc.Save(); } } // 辅助方法:根据单元格地址(如"A1")获取单元格对象 private static Cell GetCell(Worksheet worksheet, string cellAddress) { var columnName = new string(cellAddress.TakeWhile(c => char.IsLetter(c)).ToArray()); var rowIndex = int.Parse(new string(cellAddress.SkipWhile(c => char.IsLetter(c)).ToArray())); var sheetData = worksheet.GetFirstChild<SheetData>(); var row = sheetData.Elements<Row>().FirstOrDefault(r => r.RowIndex == rowIndex); if (row == null) { row = new Row { RowIndex = (uint)rowIndex }; sheetData.Append(row); } var cell = row.Elements<Cell>().FirstOrDefault(c => c.CellReference == cellAddress); if (cell == null) { cell = new Cell { CellReference = cellAddress }; row.Append(cell); } return cell; } // 辅助方法:将列名(如"A"、"AB")转换为列索引 private static int ColumnNameToIndex(string columnName) { int index = 0; foreach (char c in columnName.ToUpper()) { index = index * 26 + (c - 'A' + 1); } return index; }
使用示例
比如要修改第1个图表(索引0)里名为Sheet1的工作表中B2单元格的数值为150:
UpdateWordChartData(@"C:\YourDocument.docx", 0, "Sheet1", "B2", 150);
关键注意事项
- 图表数据映射:确保你修改的Excel单元格是图表实际绑定的数据范围,可以打开Word文档右键图表→选择数据,查看数据来源的单元格位置
- 多图表区分:如果需要精准定位特定图表,可以通过
ChartPart的Id或者图表的标题文本(解析ChartSpace里的标题元素)来区分,而不是仅靠索引 - 文件权限:操作前确保Word文档未被其他进程占用,否则会抛出异常
- 数据类型匹配:修改单元格时要注意数据类型(数值、文本等),和原数据类型保持一致,避免图表展示异常
内容的提问来源于stack exchange,提问作者Neerajyadav Kumar
相关产品推荐
相关产品推荐

