I've recently been working on a similar solution to what @stfno.me's answer has above, however I've updated it to account for a number of areas it doesn't account for.
After referencing the original Microsoft Learn guide here, I noticed a couple of things:
A Word document can have multiple headers defined, which internally result in multiple header XML files. Each of these represents the first, default and even headers respectively, which are set via the options in Word for creating different headers for odd/even pages and a different first page header. The examples I've seen only do a .FirstOrDefault on the HeaderParts of a document, which means only one of those headers is being accounted for.
The code I found isn't easy to follow, and requires some separation. I took the liberty of creating 'bite-sized' methods to allow them to be easily called.
I also found that I wanted to handle the case of documents potentially having existing headers/footers on them, or none at all, and thus always pre-emptively delete the headers/footers before copying the source material over.
I have noticed an issue in this regard where there's a possibility of the HeaderReference of the older headers remaining in the document, which is unusual since I do find all references of HeaderReferences in the SectionProperties and remove them, but shouldn't be a common problem.
The below methods are my solution, and can be simply updated to use a FooterPart instead of a HeaderPart to apply the same changes to the footer. I literally copy/pasted these functions and found + replaced 'Header' with 'Footer'.
For simplicity - 'target' refers to the target document that needs a header and footer, 'source' refers to the document that has the header and footer, but no other content.
Hope this saves someone else the pain of figuring this out
The top-level method for replacing a header:
public void ReplaceHeaders(string sourceFilePath, string targetFilePath) {
using (WordprocessingDocument sourceDocument = WordprocessingDocument.Open(sourceFilePath, false))
using (WordprocessingDocument targetDocument = WordprocessingDocument.Open(targetFilePath, true))
{
MainDocumentPart mainPart = targetDocument.MainDocumentPart;
DeleteHeaders(targetDocument);
CreateOrUpdateHeaders(sourceDocument, targetDocument);
targetDocument.Save();
}
}
Delete Headers method:
private void DeleteHeaders(WordprocessingDocument document)
{
MainDocumentPart mainPart = document.MainDocumentPart;
mainPart.DeleteParts(mainPart.HeaderParts);
IEnumerable<SectionProperties> sectPrs = mainPart.Document.Body.Elements<SectionProperties>();
foreach (var sectPr in sectPrs)
{
sectPr.RemoveAllChildren<HeaderReference>();
}
}
CreateOrUpdateHeaders method:
private void CreateOrUpdateHeaders(WordprocessingDocument sourceDocument, WordprocessingDocument targetDocument)
{
MainDocumentPart targetMainPart = targetDocument.MainDocumentPart;
MainDocumentPart sourceMainPart = sourceDocument.MainDocumentPart;
foreach (var sourceHeaderPart in sourceMainPart.HeaderParts)
{
var sourceHeaderReference = sourceDocument.MainDocumentPart.Document.Descendants<HeaderReference>()
.First(hr => hr.Id.Value == sourceDocument.MainDocumentPart.GetIdOfPart(sourceHeaderPart));
var sourceHeaderReferenceType = sourceHeaderReference.Type;
var targetHeaderReferences = targetMainPart.Document.Descendants<HeaderReference>();
HeaderPart targetHeaderPart = targetMainPart.AddNewPart<HeaderPart>();
var targetHeaderPartId = targetMainPart.GetIdOfPart(targetHeaderPart);
targetHeaderPart.FeedData(sourceHeaderPart.GetStream());
SectionProperties sectionProperties = targetMainPart.Document.Body.Elements<SectionProperties>().First();
PageMargin pageMargin = new PageMargin() { Header = 0, Footer = 0, Gutter = (UInt32Value)0U }; // Not necessary, used to remove default spacing above header and below footer
sectionProperties.Append(new HeaderReference() { Type = sourceHeaderReferenceType, Id = targetHeaderPartId });
sectionProperties.Append(pageMargin);
CopyHeaderContentParts(sourceHeaderPart, targetHeaderPart);
}
}
CopyHeaderContentParts method (taken from @stfno.me's answer):
private void CopyHeaderContentParts(HeaderPart sourceHeaderPart, HeaderPart targetHeaderPart)
{
var listToAdd = new List<KeyValuePair<Type, Stream>>();
foreach (var idPart in sourceHeaderPart.Parts)
{
var part = sourceHeaderPart.GetPartById(idPart.RelationshipId);
if (part is ImagePart)
{
targetHeaderPart.AddNewPart<ImagePart>("image/png", idPart.RelationshipId);
listToAdd.Add(new KeyValuePair<Type, Stream>(typeof(ImagePart), part.GetStream()));
}
else if (part is DiagramStylePart)
{
targetHeaderPart.AddNewPart<DiagramStylePart>(idPart.RelationshipId);
listToAdd.Add(new KeyValuePair<Type, Stream>(typeof(DiagramStylePart), part.GetStream()));
}
else if (part is DiagramColorsPart)
{
targetHeaderPart.AddNewPart<DiagramColorsPart>(idPart.RelationshipId);
listToAdd.Add(new KeyValuePair<Type, Stream>(typeof(DiagramColorsPart),
part.GetStream()));
}
else if (part is DiagramDataPart)
{
targetHeaderPart.AddNewPart<DiagramDataPart>(idPart.RelationshipId);
listToAdd.Add(new KeyValuePair<Type, Stream>(typeof(DiagramDataPart), part.GetStream()));
}
else if (part is DiagramLayoutDefinitionPart)
{
targetHeaderPart.AddNewPart<DiagramStylePart>(idPart.RelationshipId);
listToAdd.Add(new KeyValuePair<Type, Stream>(typeof(DiagramStylePart), part.GetStream()));
}
else if (part is DiagramPersistLayoutPart)
{
targetHeaderPart.AddNewPart<DiagramPersistLayoutPart>(idPart.RelationshipId);
listToAdd.Add(new KeyValuePair<Type, Stream>(typeof(DiagramPersistLayoutPart),
part.GetStream()));
}
}
var i = 0;
foreach (var idPart in targetHeaderPart.Parts)
{
var part = targetHeaderPart.GetPartById(idPart.RelationshipId);
if (part.GetType() == listToAdd[i].Key)
{
part.FeedData(listToAdd[i].Value);
}
i++;
}
}