trailmax
trailmax

Reputation: 35106

Get DocumentPart from OpenXmlElement when working with Word document in OpenXML

I'm modifying a docx template with OpenXML API and run into a problem.

I need to insert an image into a certain place - that place is defined by a Content Controll element that can be in the main part of document, header of footer.

I'm getting content controll like this:

static IEnumerable<TElement> GetDecendants<TElement>(OpenXmlPart part) where TElement : OpenXmlElement
{
    var result = part.RootElement
        .Descendants()
        .OfType<TElement>();

    return result;
}

Later down the pipeline I need to insert an image to the correct part of the document via this

internal static OpenXmlElement InsertImage(OpenXmlPart documentPart, Stream stream, string fileName, int imageWidth, int imageHeight)
{
    // actual implementation that is tested and works
}

Now my problem is that when I discover a ContentControl element that needs to be replaced by an image, I don't have a reference to documentPart - I only have reference so SdtRun or SdtBlock.

Is there a way to navigate to documentPart from SdtRun? I've checked .Parent but could not find a way to go from OpenXmlElement to OpenXmlPart - these are in different hierarchies.

Upvotes: 3

Views: 2669

Answers (2)

Taterhead
Taterhead

Reputation: 5951

I recommend the below method. It uses Ancestor to avoid recursion and takes advantage of the short-circuiting Null-conditional Operators from C# 6.

    internal static OpenXmlPart GetMainDocumentPart(OpenXmlElement xmlElement)
    {
        return
        xmlElement?.Ancestors<Document>()?.FirstOrDefault()?.MainDocumentPart as OpenXmlPart ??
        xmlElement?.Ancestors<Footer>()?.FirstOrDefault()?.FooterPart as OpenXmlPart ??
        xmlElement?.Ancestors<Header>()?.FirstOrDefault()?.HeaderPart as OpenXmlPart;
    }

Upvotes: 5

trailmax
trailmax

Reputation: 35106

Having gone through the source code of OpenXML I have found a method that did what I needed. Only it was marked internal and I could not use it in my code.

So I came up with this:

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System;


internal static class XmlElementHelpers
{
    internal static OpenXmlPart GetDocumentPart(this OpenXmlElement xmlElement)
    {
        if (xmlElement == null)
        {
            return null;
        }
        if (xmlElement is Document document)
        {
            return document.MainDocumentPart;
        }

        if (xmlElement is Header header)
        {
            return header.HeaderPart;
        }

        if (xmlElement is Footer footer)
        {
            return footer.FooterPart;
        }

        return GetDocumentPart(xmlElement.Parent);
    }
}

Upvotes: 1

Related Questions