使用DocumentFormat.OpenXml编辑Excel模板时添加新样式且不覆盖原有样式的方法
DocumentFormat.OpenXml编辑Excel模板时添加新样式且不覆盖原有样式的方法
我太懂你踩的这个坑了——直接给stylesPart.Stylesheet赋值新对象,等于把模板里原有的所有样式(包括字体、填充、边框、单元格格式)全清空了,这就是为什么换个模板就失效的原因。咱们要做的是在原有样式的基础上追加新格式,而不是推倒重建整个样式表。
核心修改思路
不要新建Stylesheet,而是复用模板原有的样式表节点,只追加我们需要的新格式(比如日期、欧元格式),同时保证所有节点的计数属性和实际元素数量匹配,避免Excel报错。
具体代码修改
1. 复用原有StyleSheet,不要新建
先把模板原有的样式表取出来,所有操作都基于它:
var stylesPart = workbook.WorkbookPart.WorkbookStylesPart; var stylesheet = stylesPart.Stylesheet; // 取模板自带的样式表,不是new!
2. 追加自定义数字格式(欧元),不覆盖原有格式
自定义格式的ID要从164开始(0-163是Excel内置格式),还要避免重复添加:
// 处理自定义欧元数字格式 var numberingFormats = stylesheet.NumberingFormats ?? new NumberingFormats(); // 检查模板是否已存在该格式,避免重复追加 if (!numberingFormats.Elements<NumberingFormat>().Any(nf => nf.NumberFormatId == 164)) { numberingFormats.AppendChild(new NumberingFormat { NumberFormatId = 164, FormatCode = "#,##0\\ \"€\"" }); } // 如果模板原来没有NumberingFormats节点,把它加到样式表里 if (stylesheet.NumberingFormats == null) { stylesheet.AppendChild(numberingFormats); } // 必须更新Count属性,否则Excel打开会提示文件损坏 numberingFormats.Count = (uint)numberingFormats.Elements<NumberingFormat>().Count();
3. 在原有CellFormats基础上追加新的单元格格式
新格式要追加在模板原有格式的后面,用originalCellFormatCount记录原有数量,方便计算新格式的索引:
var cellFormats = stylesheet.CellFormats; int originalCellFormatCount = (int)cellFormats.Count.Value; // 记录原有格式数量 // 追加1:短日期格式(用Excel内置ID 14) cellFormats.AppendChild(new CellFormat { NumberFormatId = 14, ApplyNumberFormat = true, FormatId = 0 // 复用模板默认的基础样式(字体、填充、边框) }); // 追加2:欧元格式(用我们刚加的自定义ID 164) cellFormats.AppendChild(new CellFormat { NumberFormatId = 164, ApplyNumberFormat = true, FormatId = 0 }); // 追加3:带时间的日期格式(内置ID 22) cellFormats.AppendChild(new CellFormat { NumberFormatId = 22, ApplyNumberFormat = true, FormatId = 0 }); // 更新CellFormats的Count属性,这步是关键! cellFormats.Count = (uint)cellFormats.Elements<CellFormat>().Count();
4. 调整StyleIndex取值,对应新追加的格式
因为新格式是追加在原有格式后面的,所以要用之前记录的originalCellFormatCount计算索引,不要硬编码:
// 提前计算好新样式的索引,避免硬编码出错 uint shortDateStyleIndex = (uint)originalCellFormatCount; uint euroStyleIndex = (uint)originalCellFormatCount + 1; uint dateTimeStyleIndex = (uint)originalCellFormatCount + 2; // 后面给单元格设置样式时用这些变量 if (t == CellValues.Date) cell.StyleIndex = (dtwt ?? false) ? dateTimeStyleIndex : shortDateStyleIndex; if (mt != null) cell.StyleIndex = (mt ?? 0) == 164 ? euroStyleIndex : 0; // 0是模板原有的默认样式
必须注意的细节
- 更新Count属性:NumberingFormats、CellFormats这些节点的
Count属性必须和实际元素数量一致,否则Excel打开文件会提示损坏。 - 自定义格式ID唯一:自定义
NumberingFormatId必须从164开始,且不要和模板里已有的自定义ID重复(可以遍历原有NumberingFormats找最大ID再加1)。 - 复用基础样式:我们的新格式用
FormatId = 0,对应模板原有的默认单元格样式,这样就不用重新定义字体、填充、边框,不会破坏模板原有样式。
把你原有的那段重建Stylesheet的代码,换成上面的复用式代码,再调整StyleIndex的取值,两个端点的模板原有样式就都能保留,同时新的日期、欧元格式也能正常应用了!




