none
How Do I Populate Content Controls in Word 2010 using XML file RRS feed

  • Question

  • I've been researching for about 2 weeks now and have not been able to find a way to use an XML file to populate content controls. I have been through all the tutorials on msdn and can make them work as presented. Most of the msdn examples look like my 2nd code snippet below and just say "...usually this is from a file or database..." but never present how to get it from a file.

    My code seems to work but then when I open the Word Document the content controls have not been populated...Here is the code that I am using: 

    static void Main(string[] args)
            {
                string docOutPath = @"C:\Users\Christopher\Desktop\BookData\TestReportBetaEnd.docx";
                string cXML = @"C:\Users\Christopher\Desktop\BookData\TestReport2.xml";
        
                Console.WriteLine("Starting up Word Template updater...");
                //get path to the template and set the output destination
                string docTemplatePath = @"C:\Users\Christopher\Desktop\BookData\TestReportBeta.docx";
    
                //create a copy of the template so that the orginal is not overwritten
                File.Copy(docTemplatePath, docOutPath);
    
                using (WordprocessingDocument doc = WordprocessingDocument.Open(docOutPath, true))
                { 
                    MainDocumentPart main = doc.MainDocumentPart;
                    main.DeleteParts<CustomXmlPart>(main.CustomXmlParts);
                    StreamReader streamReader = new StreamReader(cXML);
                    string str = streamReader.ReadToEnd();
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(str);
                    
                    foreach (CustomXmlPart customXmlpart in main.CustomXmlParts)
                    {
                        CustomXmlPart customXml = main.AddCustomXmlPart(CustomXmlPartType.CustomXml);
                        using (StreamWriter ts = new StreamWriter(customXml.GetStream()))
                        {
                        ts.Write(xmlDoc);
                        }
                    }
                }
                Console.WriteLine("File Complete...");
                Console.ReadLine();
            }

    If I use the code like the code below it populates the Content Controls, but is not useful outside of the example I pulled it from:

                    //create XML string matching schema custom XML path
                   string newXml = "<root>" +
                        "<FINDING>Adobe Flash Player contains multiple...</FINDING>" +
                        "<STATUS>Open</STATUS>" +
                        "<THREATLEVEL>High</THREATLEVEL>" +
                        "<RECOMMENDATION>Update Flash Player to...</RECOMMENDATION>" +
                        "<DEVICEAFFECTED>aPC</DEVICEAFFECTED>" +
                        "<SCANNER>RETINA FINDING</SCANNER>" +
                        "</root>";
    
                    MainDocumentPart main = doc.MainDocumentPart;
                    main.DeleteParts<CustomXmlPart>(main.CustomXmlParts);
                    
                    //add and write new XML part
                    CustomXmlPart customXml = main.AddCustomXmlPart(CustomXmlPartType.CustomXml);
                    using (StreamWriter ts = new StreamWriter(customXml.GetStream()))
                        {
                            ts.Write(newXml);
                        }

    Any help would be great. Thanks in advance. 



    • Edited by CoronersDisciple Wednesday, September 12, 2012 11:07 PM shortened the example code
    • Moved by Forrest GuoModerator Thursday, September 13, 2012 5:35 AM Open XML question (From:Developing Apps for Office)
    Wednesday, September 12, 2012 11:03 PM

Answers

  • Tom,

    I GOT IT!!! :)

    How I got there. 

      • I had actually already read your Eric White post above- there is a less verbose and the blog does not account for a user having the .xml file already (it creates one that is updated)
      • Your other post got me looking at .zip part of the file, everything was happening they way it was designed except...
      • When the program ran using the "using" statement you provided above (also here, as well as, the other examples I tried):
                using (WordprocessingDocument doc = WordprocessingDocument.Open(docOutPath, true))
                {
                    MainDocumentPart mdp = doc.MainDocumentPart;
                    if (mdp.CustomizationPart != null)
                    {
                        mdp.DeleteParts<CustomXmlPart>(mdp.CustomXmlParts);
                    }
                    CustomXmlPart cxp = mdp.AddCustomXmlPart(CustomXmlPartType.CustomXml);
                    FileStream fs = new FileStream(cXML, FileMode.Open);
                    cxp.FeedData(fs);
                    //mdp.Document.Save();
                }

    it would add the CustomXMLPart and the relationship as expected. However, it would add to the existing CustomXmlParts that were already in the package. So if I started with item1.xml, after the code I had item1.xml and item2.xml.

    So, in amigae to you I changed "mdp.CustomizationPart" to "mdp.CustomXmlParts". This deleted the current item1.xml and the only file in the "CustomXml" folder is item2.xml. 

    Here is the full functioning Console Application: 

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using DocumentFormat.OpenXml;
    using DocumentFormat.OpenXml.Packaging;
    using DocumentFormat.OpenXml.Wordprocessing;
    using System.IO;
    
    namespace BookData
    {
        class Program
        {
    
            static void Main(string[] args)
            {
                string template = @"C:\Users\Christopher\Desktop\BookData\TestReportBeta.docx";
                string outFile = @"C:\Users\Christopher\Desktop\BookData\TestReportBetaEND.docx";
                string xmlPath = @"C:\Users\Christopher\Desktop\BookData\TestReport.xml";
    
                // convert template to document
                File.Copy(template, outFile);
    
                using (WordprocessingDocument doc = WordprocessingDocument.Open(outFile, true))
                {
                    MainDocumentPart mdp = doc.MainDocumentPart;
                    if (mdp.CustomXmlParts != null)
                    {
                        mdp.DeleteParts<CustomXmlPart>(mdp.CustomXmlParts);
                    }
                    CustomXmlPart cxp = mdp.AddCustomXmlPart(CustomXmlPartType.CustomXml);
                    FileStream fs = new FileStream(xmlPath, FileMode.Open);
                    cxp.FeedData(fs);
                    mdp.Document.Save();
                } 
            }
        }
    }

    Monday, September 17, 2012 9:32 PM

All replies

  • Hi CoronersDisciple,

    Thanks for posting in the MSDN Forum.

    Please try following code, it works fine on my side.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using DocumentFormat.OpenXml.Packaging;
    using System.IO;
    
    namespace ConsoleApplication5
    {
        class Program
        {
            [STAThread]
            static void Main(string[] args)
            {
                OpenFileDialog OFD = new OpenFileDialog();
                OFD.Multiselect = false;
                OFD.Title = "Open Word Document";
                OFD.Filter = "Word Document|*.docx;*.domx";
                OFD.ShowDialog();
                string docPath = OFD.FileName;
                OFD.Title = "Opne Xml Document";
                OFD.Filter = "Xml Document|*.xml";
                OFD.ShowDialog();
                string xmlPath = OFD.FileName;
                using (WordprocessingDocument wpd = WordprocessingDocument.Open(docPath, true))
                {
                    MainDocumentPart mdp = wpd.MainDocumentPart;
                    if (mdp.CustomizationPart != null)
                    {
                        mdp.DeleteParts<CustomXmlPart>(mdp.CustomXmlParts);
                    }
                    CustomXmlPart cxp = mdp.AddCustomXmlPart(CustomXmlPartType.CustomXml);
                    FileStream fs = new FileStream(xmlPath, FileMode.Open);
                    cxp.FeedData(fs);
                    mdp.Document.Save();
                }
                Console.ReadKey();
            }
        }
    }

    I hope it can help you.

    Have a good day,

    Tom


    Tom Xu [MSFT]
    MSDN Community Support | Feedback to us

    Friday, September 14, 2012 7:05 AM
    Moderator
  • Tom,

    Thank you for your response. I have made the modification to my code but it still isn't working. I have the following files:

    • One Word.docx that is only the content controls
    • One Word1.docx that is the content controls mapped to a schema
    • And the XML code below:
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <VulernabilityRow>
    	<STATUS>OPEN</STATUS>
    	<FINDING>Adobe Flash Player....</FINDING>
    	<RECOMMENDATIONS>Update Flash Player...</RECOMMENDATIONS>
    	<DEVICEAFFECTED>aPC</DEVICEAFFECTED>
    	<SCANNER>RETINA FINDING</SCANNER>
    </VulernabilityRow>

    Is there anything you can think of, with any of the test files, that I may have to do in addition to my coding?

    Friday, September 14, 2012 7:30 PM
  • Hi - you may have already read the following but it might help:

    http://blogs.msdn.com/b/brian_jones/archive/2009/01/05/taking-advantage-of-bound-content-controls.aspx

    Cheers...pc

    Friday, September 14, 2012 11:23 PM
  • Thanks for the replay Patrick. I had not read that particular article but I had read several like that, including this one: http://seroter.wordpress.com/2009/12/23/populating-word-2007-templates-through-open-xml/ 

    I have a document with the content controls mapped using the Content Control Toolkit per the instructions in my source. The code works great how it is described in the blog...

    Probably my inexperience with WordprocessingML and Content Controls, but it seems like it should be pretty easy to replace the string contents from the blog with an Xml file, but I am clearly missing something. :(

    I found something interesting this morning:

    After I run this code (Tom's coded just modified since I am running console not a form) 

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xml;
    using DocumentFormat.OpenXml;
    using DocumentFormat.OpenXml.Packaging;
    using DocumentFormat.OpenXml.Wordprocessing;
    using DocumentFormat.OpenXml.CustomXmlDataProperties;
    using System.IO;
    using System.Xml.Linq;
    
    namespace BookData
    {
        class Program
        {
            [STAThread]
            static void Main(string[] args)
            {
                string docOutPath = @"C:\Users\Christopher\Desktop\BookData\TestReportBetaEND.docx";
                string cXML = @"C:\Users\Christopher\Desktop\BookData\TestReport2.xml";
                AddNewPart(docOutPath, cXML);
    
                Console.WriteLine("File Complete...");
                Console.ReadLine();
            }
    
            public static void AddNewPart(string docOutPath, string cXML)
            {
                Console.WriteLine("Starting up Word Template updater...");
                //get path to the template and set the output destination
                string docTemplatePath = @"C:\Users\Christopher\Desktop\BookData\TestReportBeta.docx";
    
                //create a copy of the template so that the orginal is not overwritten
                File.Copy(docTemplatePath, docOutPath);
    
                using (WordprocessingDocument wpd = WordprocessingDocument.Open(docOutPath, true))
                {
                    MainDocumentPart mdp = wpd.MainDocumentPart;
                    if (mdp.CustomizationPart != null)
                    {
                        mdp.DeleteParts<CustomXmlPart>(mdp.CustomXmlParts);
                    }
                    CustomXmlPart cxp = mdp.AddCustomXmlPart(CustomXmlPartType.CustomXml);
                    FileStream fs = new FileStream(cXML, FileMode.Open);
                    cxp.FeedData(fs);
                    mdp.Document.Save();
                }
            }
        }
    }

    The changed to the .docx to .zip of the file created (in this case TestReportBetaEND) and I have three itemX.xml files (item1.xml, item2.xml, item3.xml). Item3.xml is populated with the content of my xml file...but I don't understand why I do not see those contents in my Word document? 

    item1 and item2.xml are identical and were added after I mapped the content controls using the Toolbox.

    • Edited by CoronersDisciple Saturday, September 15, 2012 12:58 PM added new info from troubleshooting
    Saturday, September 15, 2012 12:29 PM
  • Hi CoronersDisciple,

    I think following snippet will help you. As the snippet which I provide you only add the customXml Part and didn't binding it to the ContentControl. Following information will help you to approach it.

    http://openxmldeveloper.org/blog/b/openxmldeveloper/archive/2006/06/04/mapping-content-controls-to-custom-xml-parts-using-notepad.aspx and http://blogs.msdn.com/b/ericwhite/archive/2008/10/19/creating-data-bound-content-controls-using-the-open-xml-sdk-and-linq-to-xml.aspx

    Have a good day,

    Tom


    Tom Xu [MSFT]
    MSDN Community Support | Feedback to us

    Monday, September 17, 2012 6:45 AM
    Moderator
  • Thank you Tom. These looking promising :) I read them and work at it some more tonight and let you know. 

    Have a good day

    Chris

    Monday, September 17, 2012 5:55 PM
  • Tom,

    I GOT IT!!! :)

    How I got there. 

      • I had actually already read your Eric White post above- there is a less verbose and the blog does not account for a user having the .xml file already (it creates one that is updated)
      • Your other post got me looking at .zip part of the file, everything was happening they way it was designed except...
      • When the program ran using the "using" statement you provided above (also here, as well as, the other examples I tried):
                using (WordprocessingDocument doc = WordprocessingDocument.Open(docOutPath, true))
                {
                    MainDocumentPart mdp = doc.MainDocumentPart;
                    if (mdp.CustomizationPart != null)
                    {
                        mdp.DeleteParts<CustomXmlPart>(mdp.CustomXmlParts);
                    }
                    CustomXmlPart cxp = mdp.AddCustomXmlPart(CustomXmlPartType.CustomXml);
                    FileStream fs = new FileStream(cXML, FileMode.Open);
                    cxp.FeedData(fs);
                    //mdp.Document.Save();
                }

    it would add the CustomXMLPart and the relationship as expected. However, it would add to the existing CustomXmlParts that were already in the package. So if I started with item1.xml, after the code I had item1.xml and item2.xml.

    So, in amigae to you I changed "mdp.CustomizationPart" to "mdp.CustomXmlParts". This deleted the current item1.xml and the only file in the "CustomXml" folder is item2.xml. 

    Here is the full functioning Console Application: 

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using DocumentFormat.OpenXml;
    using DocumentFormat.OpenXml.Packaging;
    using DocumentFormat.OpenXml.Wordprocessing;
    using System.IO;
    
    namespace BookData
    {
        class Program
        {
    
            static void Main(string[] args)
            {
                string template = @"C:\Users\Christopher\Desktop\BookData\TestReportBeta.docx";
                string outFile = @"C:\Users\Christopher\Desktop\BookData\TestReportBetaEND.docx";
                string xmlPath = @"C:\Users\Christopher\Desktop\BookData\TestReport.xml";
    
                // convert template to document
                File.Copy(template, outFile);
    
                using (WordprocessingDocument doc = WordprocessingDocument.Open(outFile, true))
                {
                    MainDocumentPart mdp = doc.MainDocumentPart;
                    if (mdp.CustomXmlParts != null)
                    {
                        mdp.DeleteParts<CustomXmlPart>(mdp.CustomXmlParts);
                    }
                    CustomXmlPart cxp = mdp.AddCustomXmlPart(CustomXmlPartType.CustomXml);
                    FileStream fs = new FileStream(xmlPath, FileMode.Open);
                    cxp.FeedData(fs);
                    mdp.Document.Save();
                } 
            }
        }
    }

    Monday, September 17, 2012 9:32 PM
  • Tom, 

    For your help, I have also posted the code for someone with a form that has one button called btnGenerate, using the openfiledialog

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using DocumentFormat.OpenXml.Packaging;
    using System.IO;
    using System.Threading;
    
    
    namespace TestReportCreator_Beta
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            [STAThread]
            private void btnGenerate_Click(object sender, EventArgs e)
            {
                string outFile = @"C:\Users\Christopher\Desktop\BookData\TestReportBetaEND.docx";
                
                OpenFileDialog OFD = new OpenFileDialog();
                OFD.Multiselect = false;
                OFD.Title = "Open Word Document";
                OFD.Filter = "Word Document|*.docx;*.domx";
                OFD.ShowDialog();
                string docPath = OFD.FileName;
                OFD.Title = "Opne Xml Document";
                OFD.Filter = "Xml Document|*.xml";
                OFD.ShowDialog();
                string xmlPath = OFD.FileName;
    
                // convert template to document
                File.Copy(docPath, outFile);
    
                using (WordprocessingDocument doc = WordprocessingDocument.Open(outFile, true))
                {
                    MainDocumentPart mdp = doc.MainDocumentPart;
                    if (mdp.CustomXmlParts != null)
                    {
                        mdp.DeleteParts<CustomXmlPart>(mdp.CustomXmlParts);
                    }
                    CustomXmlPart cxp = mdp.AddCustomXmlPart(CustomXmlPartType.CustomXml);
                    FileStream fs = new FileStream(xmlPath, FileMode.Open);
                    cxp.FeedData(fs);
                    mdp.Document.Save();
                } 
            }
        }
    }

    Thank you for your help!

    Monday, September 17, 2012 9:44 PM
  • Hi CoronersDisciple,

    I'm glad to hear you have solved it. Thanks for share your solution here. It's really beneficial for other community members to see how you solved it.

    Have a good day,

    Tom


    Tom Xu [MSFT]
    MSDN Community Support | Feedback to us

    Tuesday, September 18, 2012 5:08 AM
    Moderator