Iterating through all Content Controls in an Open XML WordprocessingML Document

Sometimes you want to iterate over all content controls in a WordprocessingML document. You may want to search for a content control with a specific tag, or you may want to process all content controls of a specific type. This blog post shows how to iterate over all content controls.

One key point is that if you want to be thorough about this, you need to search for content controls in all parts where content controls can exist. They can exist in the following parts:

  • Main document part
  • Header parts (there can be more than one header part)
  • Footer parts (there can be more than one footer part)
  • End note part (there can be zero or one end note part)
  • Foot note part (there can be zero or one foot note part)

The best way to proceed is to write a method that returns a collection of all content controls for a given part, and then write another method that returns a collection of all content controls for all parts. The most expressive way to write these methods is to write them as extension methods. This allows us to use those methods by simply ‘dotting’ into the extension method. Following is an example that includes the two extension methods, as well as a bit of code to exercise them.

I’ve attached an example document to this post that includes content controls in the main document part, in a header, in a footer, in an end note, and in a foot note.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

public static class ContentControlExtensions
{
    public static IEnumerable<OpenXmlElement> ContentControls(
        this OpenXmlPart part)
    {
        return part.RootElement
            .Descendants()
            .Where(e => e is SdtBlock || e is SdtRun);
    }

    public static IEnumerable<OpenXmlElement> ContentControls(
        this WordprocessingDocument doc)
    {
        foreach (var cc in doc.MainDocumentPart.ContentControls())
            yield return cc;
        foreach (var header in doc.MainDocumentPart.HeaderParts)
            foreach (var cc in header.ContentControls())
                yield return cc;
        foreach (var footer in doc.MainDocumentPart.FooterParts)
            foreach (var cc in footer.ContentControls())
                yield return cc;
        if (doc.MainDocumentPart.FootnotesPart != null)
            foreach (var cc in doc.MainDocumentPart.FootnotesPart.ContentControls())
                yield return cc;
        if (doc.MainDocumentPart.EndnotesPart != null)
            foreach (var cc in doc.MainDocumentPart.EndnotesPart.ContentControls())
                yield return cc;
    }
}

class Program
{
    static void Main(string[] args)
    {
        using (WordprocessingDocument doc =
            WordprocessingDocument.Open(“Test.docx”, false))
        {
            foreach (var cc in doc.ContentControls())
            {
                SdtProperties props = cc.Elements<SdtProperties>().FirstOrDefault();
                Tag tag = props.Elements<Tag>().FirstOrDefault();
                Console.WriteLine(tag.Val);
            }
        }
    }

Download Document Test.docx