How to use OpenXML generate a new file by word template file? I need to implement a function in my work, which is to use C# programming to realize OpenXML to fill Word template documents to generate new documents. The purpose of this is to write database design specifications when delivering software products to customers. Each company has its own format, its own header settings, etc. Take a look at the directory structure of the Word template document.
How to use OpenXML generate a new file
In the template document, the physical design department puts the design structure of all the tables in the entire database. (Of course, I am writing a job and only export the selected tables to generate, because some tables cannot be placed in the design document.), in [Physical Design] In the column, I reserved some tags, mainly to find the paragraph programmatically, and then replace the content programmatically. For example, the reserved tag is [$Physical Design$], As shown below:
As for how to leave the mark, it depends on your preference, it doesn’t matter, it is equivalent to a placeholder. Therefore, the preparatory work we need to do is to prepare your Word template, and then set the mark (placeholder). Next enter the topic.
C# programming to achieve this function
The code and tools in this article are developed using VS2019. Before starting, you need to add a reference to DocumentFormat.OpenXml through NuGet.
Add the following namespace to the code:
using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing;
How to use OpenXML generate a new file by word template file?
After preparation, we start to write the core code, first open the word template file.
Open word template
Open the word template and directly call the open method of the WordprocessingDocument object. In practice, it is best to open in the memory stream (MemoryStream). The advantage of this is that it will not damage your original word template file. If you call it directly to open the file With the open method of the version, when the program is executed, the template file will be automatically saved. Next time you use it, the program will make an error because the mark you reserved is lost.
byte[] byteArray = File.ReadAllBytes("template.docx"); using (var stream = new MemoryStream()) { stream.Write(byteArray, 0, byteArray.Length); using (WordprocessingDocument doc = WordprocessingDocument.Open(stream, true)) { } }
How to use OpenXML generate a new file by word template file?
Find marked paragraphs
After opening the template file in the memory stream, to find the placeholder paragraphs we marked, we only need to parse the paragraph (Paragraph) containing the placeholders we set from the descendant elements of the root element. As shown in the following code:
var p = doc. MainDocumentPart . RootElement . Descendants < Paragraph >() . FirstOrDefault ( x = > x. InnerText == "$Physical Design$" ) ; if (p != null) { var text = p.Descendants<Text>().FirstOrDefault(); text. Text = $ "Table label: {exportList.First().Name}" ; }
How to use OpenXML generate a new file by word template file?
The above code replaces [$Physical Design$] again. Replace with the name of the first table we want to export. The final renderings will be put later.
Insert new paragraph
The real document is very complicated, and you need to insert a new paragraph. If you call the Append method directly, it will be appended to the end of the document. It’s not what I want, let’s see how to do it.
var tableName = p.Clone() as Paragraph; tableName.Descendants<Text>().FirstOrDefault().Text = $"表名: {exportList.First().TableDesc}"; doc.MainDocumentPart.Document.Body.InsertAfter(tableName, p);
How to use OpenXML generate a new file by word template file?
Inserting a new paragraph is achieved through the third line of code above. It has 2 parameters. The first is the word document element you want to insert, not necessarily limited to the paragraph. The second parameter is the quoted word document element, which means Inserted after the element, the InsertAfter naming has already explained everything.
OpenXML generates word form
The form is the main body of this article. The following code uses the API provided by OpenXML to create a Word form.
var table = new Table(new TableProperties( new TableWidth { Width = "5000", Type = TableWidthUnitValues.Pct }, new TableBorders( new TopBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 1 }, new BottomBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 1 }, new LeftBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 1 }, new RightBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 1 }, new InsideHorizontalBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 1 }, new InsideVerticalBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 1 } ) ));
How to use OpenXML generate a new file by word template file?
The above is just an empty table, there are no rows and columns yet, the following code is to add rows and columns to the table.
TableRow tr = new TableRow(); TableCell tc1 = new TableCell( new TableCellProperties(new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center }, new Shading { Fill = "DBE5F1" }), new Paragraph(new ParagraphProperties(new Justification() { Val = JustificationValues.Center }), new Run(new Text($"字段标号")) { RunProperties = new RunProperties(new Bold(), new FontSize { Val = "24" }, new RunFonts { Ascii = "宋体", HighAnsi = "宋体", EastAsia = "宋体" }) })); tr.Append(tc1); TableCell tc2 = new TableCell( new TableCellProperties(new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center }, new Shading { Fill = "DBE5F1" }), new Paragraph(new ParagraphProperties(new Justification() { Val = JustificationValues.Center }), new Run(new Text("字段名称")) { RunProperties = new RunProperties(new Bold(), new FontSize { Val = "24" }, new RunFonts { Ascii = "宋体", HighAnsi = "宋体", EastAsia = "宋体" }) })); tr.Append(tc2); TableCell tc3 = new TableCell( new TableCellProperties(new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center }, new Shading { Fill = "DBE5F1" }), new Paragraph(new ParagraphProperties(new Justification() { Val = JustificationValues.Center }), new Run(new Text("字段类型")) { RunProperties = new RunProperties(new Bold(), new FontSize { Val = "24" }, new RunFonts { Ascii = "宋体", HighAnsi = "宋体", EastAsia = "宋体" }) })); tr.Append(tc3); TableCell tc4 = new TableCell( new TableCellProperties(new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center }, new Shading { Fill = "DBE5F1" }), new Paragraph(new ParagraphProperties(new Justification() { Val = JustificationValues.Center }), new Run(new Text("字段长度")) { RunProperties = new RunProperties(new Bold(), new FontSize { Val = "24" }, new RunFonts { Ascii = "宋体", HighAnsi = "宋体", EastAsia = "宋体" }) })); tr.Append(tc4); table.Append(tr);
How to use OpenXML generate a new file by word template file?
OpenXML fills the Word template document to generate a new document
When realizing the export function, there are usually multiple tables. In a real project, there are hundreds of tables that need to be exported. Finally, insert these tables into the placeholders of Word in turn.
doc.MainDocumentPart.Document.Body.InsertAfter(table, quoted word element);
Complete core code
byte[] byteArray = File.ReadAllBytes("template.docx"); using (var stream = new MemoryStream()) { stream.Write(byteArray, 0, byteArray.Length); using (WordprocessingDocument doc = WordprocessingDocument.Open(stream, true)) { var p = doc.MainDocumentPart.RootElement.Descendants<Paragraph>().FirstOrDefault(x => x.InnerText == "$物理设计$"); if (p != null) { var text = p.Descendants<Text>().FirstOrDefault(); text.Text = $"表标号: {exportList.First().Name}"; } var tableName = p.Clone() as Paragraph; tableName.Descendants<Text>().FirstOrDefault().Text = $"表名: {exportList.First().TableDesc}"; doc.MainDocumentPart.Document.Body.InsertAfter(tableName, p); Table t = null; for (int i = 0; i < exportList.Count(); i++) { var dbTable = exportList.ElementAt(i); var subP1 = p.Clone() as Paragraph; subP1.Descendants<Text>().FirstOrDefault().Text = $"表标号: {dbTable.Name}"; var subP2 = p.Clone() as Paragraph; subP2.Descendants<Text>().FirstOrDefault().Text = $"表名: {dbTable.TableDesc}"; if (i > 0) { doc.MainDocumentPart.Document.Body.InsertAfter(subP1, t); doc.MainDocumentPart.Document.Body.InsertAfter(subP2, subP1); } var table = new Table(new TableProperties( new TableWidth { Width = "5000", Type = TableWidthUnitValues.Pct }, new TableBorders( new TopBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 1 }, new BottomBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 1 }, new LeftBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 1 }, new RightBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 1 }, new InsideHorizontalBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 1 }, new InsideVerticalBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 1 } ) )); TableRow tr = new TableRow(); TableCell tc1 = new TableCell( new TableCellProperties(new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center }, new Shading { Fill = "DBE5F1" }), new Paragraph(new ParagraphProperties(new Justification() { Val = JustificationValues.Center }), new Run(new Text($"字段标号")) { RunProperties = new RunProperties(new Bold(), new FontSize { Val = "24" }, new RunFonts { Ascii = "宋体", HighAnsi = "宋体", EastAsia = "宋体" }) })); tr.Append(tc1); TableCell tc2 = new TableCell( new TableCellProperties(new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center }, new Shading { Fill = "DBE5F1" }), new Paragraph(new ParagraphProperties(new Justification() { Val = JustificationValues.Center }), new Run(new Text("字段名称")) { RunProperties = new RunProperties(new Bold(), new FontSize { Val = "24" }, new RunFonts { Ascii = "宋体", HighAnsi = "宋体", EastAsia = "宋体" }) })); tr.Append(tc2); TableCell tc3 = new TableCell( new TableCellProperties(new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center }, new Shading { Fill = "DBE5F1" }), new Paragraph(new ParagraphProperties(new Justification() { Val = JustificationValues.Center }), new Run(new Text("字段类型")) { RunProperties = new RunProperties(new Bold(), new FontSize { Val = "24" }, new RunFonts { Ascii = "宋体", HighAnsi = "宋体", EastAsia = "宋体" }) })); tr.Append(tc3); TableCell tc4 = new TableCell( new TableCellProperties(new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center }, new Shading { Fill = "DBE5F1" }), new Paragraph(new ParagraphProperties(new Justification() { Val = JustificationValues.Center }), new Run(new Text("字段长度")) { RunProperties = new RunProperties(new Bold(), new FontSize { Val = "24" }, new RunFonts { Ascii = "宋体", HighAnsi = "宋体", EastAsia = "宋体" }) })); tr.Append(tc4); table.Append(tr); foreach (var item in dbTable.Fields) { tr = new TableRow(); tc1 = new TableCell( new TableCellProperties(new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center }), new Paragraph(new ParagraphProperties(new Justification() { Val = JustificationValues.Center }), new Run(new Text(item.FieldName)) { RunProperties = new RunProperties(new FontSize { Val = "24" }, new RunFonts { Ascii = "宋体", HighAnsi = "宋体", EastAsia = "宋体" }) })); tr.Append(tc1); tc1 = new TableCell( new TableCellProperties(new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center }), new Paragraph(new ParagraphProperties(new Justification() { Val = JustificationValues.Left }), new Run(new Text(item.FieldDesc)) { RunProperties = new RunProperties(new FontSize { Val = "24" }, new RunFonts { Ascii = "宋体", HighAnsi = "宋体", EastAsia = "宋体" }) })); tr.Append(tc1); tc1 = new TableCell( new TableCellProperties(new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center }), new Paragraph(new ParagraphProperties(new Justification() { Val = JustificationValues.Center }), new Run(new Text(item.DbTypeChinese)) { RunProperties = new RunProperties(new FontSize { Val = "24" }, new RunFonts { Ascii = "宋体", HighAnsi = "宋体", EastAsia = "宋体" }) })); tr.Append(tc1); tc1 = new TableCell( new TableCellProperties(new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center }), new Paragraph(new ParagraphProperties(new Justification() { Val = JustificationValues.Center }), new Run(new Text(item.DBLength.Trim("()".ToCharArray()))) { RunProperties = new RunProperties(new FontSize { Val = "24" }, new RunFonts { Ascii = "宋体", HighAnsi = "宋体", EastAsia = "宋体" }) })); tr.Append(tc1); table.Append(tr); } if (i == 0) doc.MainDocumentPart.Document.Body.InsertAfter(table, tableName); else doc.MainDocumentPart.Document.Body.InsertAfter(table, subP2); t = table; } doc.SaveAs(saveFileDialog1.FileName).Dispose(); Alert("导出完毕,请手动更新该文档的目录,操作方式:选中目录->鼠标右键->更新域,保存即可!", callback: () => { System.Diagnostics.Process.Start(saveFileDialog1.FileName); }); } }
How to use OpenXML generate a new file by word template file?
Export renderings
The following figure shows the effect of the exported word document. One drawback is that the catalog of documents needs to be updated manually. Of course, you can write a word macro to automatically update the word document when you open it, but I haven’t studied these contents.
Openxml reference address: https://docs.microsoft.com/en-us/office/open-xml/open-xml-sdk