Search code examples
ms-wordopenxml-sdk

What is the correct way to inject some paragraphs with rich text in a Word content control?


I'm trying to inject some paragraphs in a Word content control using Open Xml SDK.

My code seems to work (paragraphs are visible in the word file). However, control controls cannot be edited. I can only erase the whole content before inserting new content.

How to mimic a manual copy/paste into a content control ?

FYI, here's my code:

var mainPart = package.MainDocumentPart;

var sdtRuns = mainPart.Document.Descendants<SdtElement>().Where(run => run.SdtProperties.GetFirstChild<Tag>().Val.Value == "TagOfMyContentControl");

foreach (var sdtBlock in sdtRuns)
{
    List<Paragraphs> paragraphs = GetParapraphsFromSomewhere();
    var contentControlParagraph = sdtBlock.Descendants<SdtContentBlock>().First();
    contentControlParagraph.RemoveAllChildren();
    contentControlParagraph.Append(paragraphs);
}

mainPart.Document.Save();

PS: I a more general way, is there any resource that explains purpose and hierarchy of Word ML elements?


Solution

  • Your code is close to mimic copy paste into a content control. The following two lines are the lines that are removing all of the paragraphs in your content control:

    var contentControlParagraph = sdtBlock.Descendants<SdtContentBlock>().First();
    contentControlParagraph.RemoveAllChildren();
    

    Instead, you need to determine where you want to place your paragraph and Append it there.

    For example, I have a docx with the rich text content control named CopyPasteCC. This image shows the doc in developer mode. The content control has 3 existing paragraphs.

    enter image description here

    Then, I replace your code with the following:

            var mainPart = document.MainDocumentPart;
    
            var sdtRuns = mainPart.Document.Descendants<SdtElement>().Where(run => run.SdtProperties.GetFirstChild<Tag>().Val.Value == "CopyPasteCC");
    
            sdtRuns.ElementAt(0).Descendants<Paragraph>().ElementAt(1).InsertAfterSelf(
                new Paragraph(new Run(new Text("Hello - this is new Copy Paste paragraph")))
                );
    
            mainPart.Document.Save();
    

    The third line selects all of the paragraphs inside the content control found on the second line. Then it inserts a new paragraph after the second existing paragraph. The following image is the above file after this code has ran:

    enter image description here

    To answer your PS question - there is the formal OpenXML Specification you can try to read. This is not easy reading, but it is the authoritative specification. There is a more general (but dated) free e-book titled OpenXml Explained.

    I reference each of these once a month for information. The first chapter of OpenXml Explained covers WordprocessingML, and on page 39 there is a section on Structured Document Tags which has an overview of content controls.