Saturday, April 2, 2016

Find and Replace Placeholders in a Word Template

We have a template file that we use as a baseline in our report generation (see here for help creating a new Word document from a template). The template file has some placeholders for title, subtitle, author, etc. I wanted to be able to dynamically replace those with content when the rest of the document was generated. This took me way longer than I expected to figure out so here is hopefully some info to help the next person. First I found the relevant section in the XML (the Open XML Productivity Tools are a great help for this).
<w:sdt>
  <w:sdtPr>
    <w:rPr>
      <w:sz w:val="96" />
      <w:szCs w:val="96" />
    </w:rPr>
    <w:alias w:val="Title" />
    <w:tag w:val="" />
    <w:text />
  </w:sdtPr>
  <w:sdtEndPr />
  <w:sdtContent>
    <w:r w:rsidR="00981BCE">
      <w:rPr>
        <w:sz w:val="96" />
        <w:szCs w:val="96" />
      </w:rPr>
      <w:t>[Document title]</w:t>
    </w:r>
  </w:sdtContent>
</w:sdt>
Then with a bunch of trial and error I figured out how to find and replace the '[Document title]' placeholder.
// MainDocumentPart, root Document and Body already exist just access them
var mainPart = wordDocument.MainDocumentPart;
var document = mainPart.Document;
var body = document.Body;
 
// Find all of <w:sdt> elements
foreach (var sdtRun in body.Descendants<SdtRun>()) {
    // Look for the Title placeholder.
    var alias = sdtRun.Descendants().OfType<SdtAlias>().First();
    if (alias != null && alias.Val == "Title") {
        //replace Title placeholder
        var content = sdtRun.GetFirstChild<SdtContentRun>();
        var run = content.GetFirstChild<Run>();
        var text = run.GetFirstChild<Text>();
        text.Text = "Report Title";
    }
}