How to dynamically bind data to Content Controls in a Microsoft Word document by using OpenXml and C#.

 

Create a new Microsoft Word document add two content controls with the names:

  • Employee_Firstname
  • Employee_Lastname

Like:

image

 

Employee_Lastname

 

image

 

Now when you run the unittest below, the following the result will be:

 

image

 

 

namespace EndToEndTest
{
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.CustomXmlDataProperties;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
[TestClass]
public class RliResearch
{
private List<string> _tagNames = new List<string>();
private List<string> _uniqueTagNames = new List<string>();
[TestMethod]
public void Test_with_duration()
{
var watch = new System.Diagnostics.Stopwatch();
watch.Start();
InitialiseDatabinding();
watch.Stop();
System.Console.WriteLine(watch.Elapsed.TotalMilliseconds);
}
public void InitialiseDatabinding()
{
using (WordprocessingDocument doc = WordprocessingDocument.Open(@"C:\Temp\Test.docx", true))
{
CustomXmlPart customXmlPart = doc.MainDocumentPart.CustomXmlParts.FirstOrDefault();
if (customXmlPart == null)
{
customXmlPart = doc.MainDocumentPart.AddCustomXmlPart(CustomXmlPartType.CustomXml);
}
if (customXmlPart.CustomXmlPropertiesPart == null)
{
customXmlPart.AddNewPart<CustomXmlPropertiesPart>();
}
var propertiesPart = customXmlPart.CustomXmlPropertiesPart;
if(propertiesPart.DataStoreItem == null)
{
var dataStoreItem = new DataStoreItem() { ItemId = Guid.NewGuid().ToString("B") };
propertiesPart.DataStoreItem = dataStoreItem;
}
string dataStoreId = customXmlPart.CustomXmlPropertiesPart.DataStoreItem.ItemId;
var names = new List<string>();
foreach (var cc in doc.ContentControls())
{
SdtProperties props = cc.Elements<SdtProperties>().FirstOrDefault();
if (props != null)
{
string name = string.Empty;
try
{
Tag tag = props.Elements<Tag>().FirstOrDefault();
if (tag != null)
{
name = tag.Val.ToString();
names.Add(name);
tag.Val = name;
DocumentFormat.OpenXml.Wordprocessing.SdtAlias alias = props.Elements<DocumentFormat.OpenXml.Wordprocessing.SdtAlias>().FirstOrDefault();
if (alias != null)
{
alias.Val = name;
}
// Update databinding
DocumentFormat.OpenXml.Wordprocessing.DataBinding dataBinding = props.Elements<DocumentFormat.OpenXml.Wordprocessing.DataBinding>().FirstOrDefault();
if (dataBinding == null)
{
dataBinding = new DataBinding();
dataBinding.XPath = string.Format("/Root[1]/{0}[1]", name);
dataBinding.StoreItemId = dataStoreId;
props.Append(dataBinding);
}
else
{
dataBinding.XPath = string.Format("/Root[1]/{0}[1]", name);
dataBinding.StoreItemId = dataStoreId;
}
}
}
catch (Exception ex)
{
throw new ApplicationException(string.Format("Exception found during processing of tag [{0}].", name), ex);
}
}
}
List<string> uniqueTagNames = names.Distinct(StringComparer.CurrentCultureIgnoreCase).OrderBy(x => x).ToList();
XDocument xdoc = CreateXmlDocument("Root", uniqueTagNames);
SaveCustomXmlPart(customXmlPart, xdoc);
}
}
/// <summary>
/// Create a XDocument, with a root element based on the given "rootName", containing child elements based on the given "tagnames".
/// </summary>
public XDocument CreateXmlDocument(string rootName, List<string> tagNames)
{
var document = new XDocument(
new XDeclaration("1.0", "utf-8", null),
new XElement(rootName,
tagNames.Select(x => 
{
XElement xe = new XElement(x, string.Empty);
if(string.Compare(x, "Employee_Firstname", true) == 0)
{
xe = new XElement(x, "Johny");
}
if (string.Compare(x, "Employee_Lastname", true) == 0)
{
xe= new XElement(x, "Droptables");
}
return xe;
}
)
)
);
return document;
}
public void SaveCustomXmlPart(CustomXmlPart part, XDocument xdoc)
{
using (var stream = part.GetStream(FileMode.Create, FileAccess.ReadWrite))
{
// Reset stream position to 0, to prevent errors.
stream.Position = 0;
using (XmlWriter xw = XmlWriter.Create(stream))
{
xdoc.Save(xw);
}
}
}
}
/// <summary>
/// Code from: http://openxmldeveloper.org/blog/b/openxmldeveloper/archive/2011/04/11/137383.aspx
/// </summary>
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;
}
}
}

2 Comments

  1. patrick

    Hi,
    I tried the piece of code it works but when i do the same to a document generated ( having content control produced by macro) it fails. I am unable to open the docuemnt. it throws some some in docuemt.xml. Please help me.

    Regards,
    Patrick CJ

  2. Sangeetha

    the above code works fine but when combo box and Activex radio buttons are tobe traced its not working. Please give a solution for that.

Leave a Reply

Your email address will not be published. Required fields are marked *