How do you change the content of a content control in Word 2007 with OpenXml SDK 2.0?

I found a better way to do the above using http://wiki.threewill.com/display/enterprise/SharePoint+and+Open+XML#SharePointandOpenXML-UsingWord2007ContentControls as a reference. Your results may vary but I think this will get you off to a good start:

using (WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(filePath, true)) {
    var sdtRuns = mainDocumentPart.Document.Descendants<SdtRun>()
        .Where(run => run.SdtProperties.GetFirstChild<Tag>().Val.Value == contentControlTagValue);

    foreach (SdtRun sdtRun in sdtRuns) {
        sdtRun.Descendants<Text>().First().Text = replacementText;
    }

    wordprocessingDocument.MainDocumentPart.Document.Save();
}

I think the above will only work for Plain Text content controls. Unfortunately, it doesn't get rid of the content control in the final document. If I get that working I'll post it.

http://msdn.microsoft.com/en-us/library/cc197932.aspx is also a good reference if you want to find a rich text content control. This one talks about adding rows to a table that was placed in a rich text content control.


Your first approach to remove the sdtRun and adding a new one will obviously remove the formatting because you are only adding a Run but not the RunStyle. To preserve the formatting you should create run elements like

new Run( new RunProperties(new RunStyle(){ Val = "MyStyle" }),
                            new Text("Replacement Text"));

Your second approach to replace all Decendants<Text> will work for Plain Text Content Control only because a Rich Text Content Control does not have SdtRun element. Rich Text Content Control is SdtBlock with SdtContent elements. A rich text content control can have multiple paragraphs, multiple Runs and multiple Texts. So your code, sdtRun.Descendants<Text>().First().Text = replacementText, will be flawed for a Rich Text Content Control. There is no one line code to replace the entire text of a rich content control and yet preserve all the formatting.

I did not understand what you mean by "it doesn't get rid of the content control in the final document"? I thought your requirement here is to change the text (content) only by preserving the content control and the formatting.


One EXCELLENT way to work out how to achieve the desired result is to use the document reflector tool that comes with the Open XML SDK 2.0....

For example, you could:

  1. In the Properties dialog for each of the content controls in your document, check the "Remove content control when the contents are edited".
  2. Fill them in and save it as a new doc.
  3. Use the reflector to compare the original and the saved version.
  4. Hit the show/hide code button and it will show you the code required to turn the original into the filled in version.

It's not perfect, but it's amazingly useful. You can also just look directly at the markup of either document and see the changes that filling in the controls caused.

This is a somewhat brittle way to do it because Wordprocessing ML is can be complicated; it's easy to mess it up. For simple text controls, I just use this method:

private void FillSimpleTextCC(SdtRun simpleTextCC, string replacementText)
    {
        // remove the showing place holder element      
        SdtProperties ccProperties = simpleTextCC.SdtProperties;
        ccProperties.RemoveAllChildren<ShowingPlaceholder>();

        // fetch content block Run element            
        SdtContentRun contentRun = simpleTextCC.SdtContentRun;
        var ccRun = contentRun.GetFirstChild<Run>();

        // if there was no placeholder text in the content control, then the SdtContentRun
        // block will be empty -> ccRun will be null, so create a new instance
        if (ccRun == null)
        {
            ccRun = new Run(
                new RunProperties() { RunStyle = null },
                new Text());
            contentRun.Append(ccRun);
        }

        // remove revision identifier & replace text
        ccRun.RsidRunProperties = null;
        ccRun.GetFirstChild<Text>().Text = replacementText;

        // set the run style to that stored in the SdtProperties block, if there was
        // one. Otherwise the existing style will be used.            
        var props = ccProperties.GetFirstChild<RunProperties>();
        if (props != null)
        if (props != null)
        {
            RunStyle runStyle = props.RunStyle;
            if (runStyle != null)
            {
                // set the run style to the same as content block property style.
                var runProps = ccRun.RunProperties;
                runProps.RunStyle = new RunStyle() { Val = runStyle.Val };
                runProps.RunFonts = null;
            }
        }
    }

Hope that helps in some way. :D