Reputation: 35106
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
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
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