I have a document template which I want to dynamically populate using C#. The template contains a repeating section that has a few text boxes and some static text. I want to be able to populate the text boxes and to add new section items when needed.
The code that almost works is as follows:
WordprocessingDocument doc = WordprocessingDocument.Open(@"C:\in\test.docx", true);
var mainDoc = doc.MainDocumentPart.Document.Body
.GetFirstChild<DocumentFormat.OpenXml.Wordprocessing.SdtBlock>()
.GetFirstChild<DocumentFormat.OpenXml.Wordprocessing.SdtContentBlock>();
var person = mainDoc.ChildElements[mainDoc.ChildElements.Count-1];
person.InsertAfterSelf<DocumentFormat.OpenXml.Wordprocessing.SdtBlock>(
(DocumentFormat.OpenXml.Wordprocessing.SdtBlock) person.Clone());
This however, produces a corrupted file because the unique IDs are also duplicated by the Clone method.
Any idea on how to achieve my goal?
Here is some code that shows how you could do this. Note that this removes the existing unique Id (the w:id
element) to ensure this is not repeated.
using WordprocessingDocument doc = WordprocessingDocument.Open(@"C:\in\test.docx", true);
// Get the w:sdtContent element of the first block-level w:sdt element,
// noting that "sdtContent" is called "mainDoc" in the question.
SdtContentBlock sdtContent = doc.MainDocumentPart.Document.Body
.Elements<SdtBlock>()
.Select(sdt => sdt.SdtContentBlock)
.First();
// Get last element within SdtContentBlock. This seems to represent a "person".
SdtBlock person = sdtContent.Elements<SdtBlock>().Last();
// Create a clone and remove an existing w:id element from the clone's w:sdtPr
// element, to ensure we don't repeat it. Note that the w:id element is optional
// and Word will add one when it saves the document.
var clone = (SdtBlock) person.CloneNode(true);
SdtId id = clone.SdtProperties?.Elements<SdtId>().FirstOrDefault();
id?.Remove();
// Add the clone as the new last element.
person.InsertAfterSelf(clone);