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;
}
}
}

Leave a Reply

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

2 comments

  1. patrick says:

    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 says:

    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.