Introduction
This article tells how to add dynamic content controls to an existing
Word document at run-time. I assume users have the basic knowledge of office
Open XML SDK 2.0.
Code Walkthrough
- Create a
Word document template which has two content controls.
Open Microsoft
Word. Click on 'Developer' ribbon. Then choose 'Plain text content control'.
It will add a content control on the document. Click on Properties. This will open Content Properties window. Let us tag the first control as "cc1".
Similarly create another control and tag it as "cc2". In design mode the two controls would look like
Save the file as word template ("Doc1.dotx").
- Include
the Doc1.dotx file in the project, right click on it and set Build action as Content & Copy to output directory as Copy if Newer,
- Create two lists that would contain static and dynamic content control tag names.
private static List<string> staticContentControls = new List<string>() { "cc1", "cc2" };
private static List<string> dynamicContentControls = new List<string>() { "cc3", "cc4",
"cc5" };
- In the main method,
static void Main(string[] args)
{
TemplateToDocument("Doc1.dotx", "Doc1.docx");
File.Copy("Doc1.docx", "Doc2.docx");
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open("Doc2.docx", true))
{
AddContent(wordDoc);
CreateNewContentControls(wordDoc);
AddContent(wordDoc);
wordDoc.MainDocumentPart.Document.Save();
}
}
- TemplateToDocument will convert Doc1.dotx to Doc1.docx.
MemoryStream presentationStream = null;
using (Stream tplStream = File.Open(templateFile, FileMode.Open, FileAccess.Read))
{
presentationStream = new MemoryStream((int)tplStream.Length);
tplStream.Copy(presentationStream);
presentationStream.Position = 0L;
}
using (WordprocessingDocument wordPackage =
WordprocessingDocument.Open(presentationStream, true))
{
wordPackage.ChangeDocumentType(
DocumentFormat.OpenXml.WordprocessingDocumentType.Document);
MainDocumentPart docPart = wordPackage.MainDocumentPart;
docPart.AddExternalRelationship("http://schemas.openxmlformats.org/" +
"officeDocument/2006/relationships/attachedTemplate",
new Uri(templateFile, UriKind.RelativeOrAbsolute));
docPart.Document.Save();
}
File.WriteAllBytes(documentFile, presentationStream.ToArray());
The AddContent
method will add a sample text to the content placeholder (I have chosen the sample text to be the tag name of the placeholder itself).
private static void AddContent(WordprocessingDocument wordDoc)
{
var sdtBlocks = wordDoc.MainDocumentPart.Document.Body.ChildElements.OfType<SdtBlock>();
foreach (SdtBlock block in sdtBlocks)
{
if (block.SdtProperties != null)
{
Tag blockTag = block.SdtProperties.ChildElements.OfType<Tag>().ElementAt(0);
if (blockTag != null)
if (staticContentControls.Contains(blockTag.Val.Value) ||
dynamicContentControls.Contains(blockTag.Val.Value))
block.AddContent(blockTag.Val.Value);
}
}
}
CreateNewControls
would loop through the dynamicContentControls
list, create new
SdtBlock
objects and add them to the Body
object of the Document
.
dynamicContentControls.ForEach(cc =>
{
Paragraph breakParagraph = new Paragraph();
SdtBlock sdtBlock = new SdtBlock();
SdtProperties sdtProperties = new SdtProperties();
Tag tag = new Tag() { Val = cc };
SdtContentBlock sdtContentBlock = new SdtContentBlock();
Paragraph paragraph = new Paragraph();
Run run = new Run()
{
RunProperties = new RunProperties()
{
RunStyle = new RunStyle()
{
Val = "PlaceholderText"
}
}
};
Text text = new Text();
text.Text = "Click here to enter text.";
run.Append(text);
paragraph.Append(run);
sdtContentBlock.Append(paragraph);
sdtBlock.Append(sdtProperties);
sdtBlock.Append(sdtContentBlock);
wordDoc.MainDocumentPart.Document.Body.Append(breakParagraph);
wordDoc.MainDocumentPart.Document.Body.Append(sdtBlock);
});
- Finally, call the
Save()
of Document Body
object to save the changes done to the document.
wordDoc.MainDocumentPart.Document.Save();
I have attached a sample solution. Please do have a look.