Solved

OpenXML Iterate and Replace

Posted on 2011-03-24
3
3,432 Views
Last Modified: 2012-05-11
How do you iterate through all the content controls?  I have a document with several RichText content controls but the process is not finding all the content controls within the document.  It appears that it is only finding content controls that are on the root and not ones that are within the body text.

Also, once I find a content control how do I replace the text?

This should be easy right?

public static void MyMethod(string docfile)
        {
            using (WordprocessingDocument doc = WordprocessingDocument.Open(docfile, true))
            {
                var docPart = doc.MainDocumentPart;
                // Find the first content control whose Alias property matches the supplied name.
                var sdts = docPart.Document.Descendants<SdtBlock>();
                foreach (var sdt in sdts)
                {
                    //todo it is not finding all content controls, it appeas they can't be with the body text
                    if (sdt.SdtContentBlock.InnerText == "ReplaceMeText")
                    {
                        //todo how to replace text: sdt.SdtContentBlock.InnerText = "MyNewText";

                        System.Diagnostics.Debug.WriteLine("Found" + sdt.SdtContentBlock.InnerText);
                    }

                }
            }
        }

Open in new window

0
Comment
Question by:tampsystems
  • 2
3 Comments
 
LVL 1

Expert Comment

by:nulliusinverba
ID: 35212916
I had this problem too.

I found that some of the content controls were of type "StdBlock", but some others were of type "SdtRun".

Best thing to do it is to firstly check out the Open XML SDK Productivity Tool, which opens your document and reflects the code.  Then you can drill down to where you know the controls are and see what type of element they are.

I also made myself a little console app that analyses a file and tells me how many blocks and runs there are, and what their control name is.  It's real simple.  THere's probably much better ways to do this, but I'm new to this too, and it works for me.  Here's my analysis program:

 
class Program
    {
        static void Main(string[] args)
        {
            // (1) Objects used throughout
            string fileName = @"C:\...\Letter.dotx"; // file to open
            int hb = 0; // for count of StdBlocks in header
            int hr = 0; // for count of SrdRuns in header
            int bb = 0; // for count of StdBlocks in body
            int br = 0; // for count of StdRuns in body
            List<string> hBlocks = new List<string>(); // to hold list of aliases of all blocks in header
            List<string> hRuns = new List<string>(); // to hold list of aliases of all runs in header
            List<string> bBlocks = new List<string>(); // to hold list of aliases of all blocks in body
            List<string> bRuns = new List<string>(); // to hold list of aliases of all runs in body

            // (2) Create the memory stream
            byte[] templateBytes = System.IO.File.ReadAllBytes(fileName);
            using (MemoryStream templateStream = new MemoryStream())
            {
                templateStream.Write(templateBytes, 0, (int)templateBytes.Length);

                // (3) Open the template as a wordprocessing document
                using (WordprocessingDocument outDoc = WordprocessingDocument.Open(templateStream, true))
                {
                    MainDocumentPart mainPart = outDoc.MainDocumentPart;

                    // (4) Analyse the header parts, count the number of runs and blocks, and write the string names to the relevant list
                    var hp = mainPart.HeaderParts;
                    foreach (var hd in hp)
                    {
                        foreach (SdtElement sdt in hd.Header.Descendants<SdtElement>().ToList())
                        {
                            string type = sdt.GetType().Name;
                            SdtAlias alias = sdt.Descendants<SdtAlias>().FirstOrDefault();
                            string sdtTitle = alias.Val.Value;
                            if (alias != null)
                            {
                                if (type == "SdtBlock")
                                {
                                    hb++;
                                    hBlocks.Add(sdtTitle);
                                }
                                else if (type == "SdtRun")
                                {
                                    hr++;
                                    hRuns.Add(sdtTitle);
                                }
                            }
                        }
                    }

                    // (5) Analyse the body parts, count the number of runs and blocks, and write the string names to the relevant list
                    foreach (SdtElement sdt in mainPart.Document.Descendants<SdtElement>().ToList())
                    {
                        string type = sdt.GetType().Name;
                        SdtAlias alias = sdt.Descendants<SdtAlias>().FirstOrDefault();
                        string sdtTitle = alias.Val.Value;

                        if (alias != null)
                        {
                            if (type == "SdtBlock")
                            {
                                bb++;
                                bBlocks.Add(sdtTitle);
                            }
                            else if (type == "SdtRun")
                            {
                                br++;
                                bRuns.Add(sdtTitle);
                            }
                        }
                    }
                                     
                }

            }

            // (6) Write the results of the analysis to the Console
            Console.WriteLine("The header part contains the following.\n\r\t{0} elements are StdRun:", hr.ToString());
            foreach (string x in hRuns)
            {
                Console.WriteLine("\t\t{0}", x);
            }
            Console.WriteLine("\t{0} elements are StdBlock:", hb.ToString());
            foreach (string x in hBlocks)
            {
                Console.WriteLine("\t\t{0}", x);
            }
            Console.WriteLine("\n\rThe body part contains the following.\n\r\t{0} elements are StdRun:", br.ToString());
            foreach (string x in bRuns)
            {
                Console.WriteLine("\t\t{0}", x);
            }
            Console.WriteLine("\t{0} elements are StdBlock:", bb.ToString());
            foreach (string x in bBlocks)
            {
                Console.WriteLine("\t\t{0}", x);
            }
        }
    }

Open in new window


Hope this helps.

Cheers,

Tim.
0
 

Author Comment

by:tampsystems
ID: 35216581
I tried your code but it appears that all of the SdtAlias were null.
                       
SdtAlias alias = sdt.Descendants<SdtAlias>().FirstOrDefault();

if (alias != null) //always null
0
 
LVL 1

Accepted Solution

by:
nulliusinverba earned 500 total points
ID: 35363182
Sorry mate - wish that had worked for you.

So what's going on is going to be a function of the (your) Word document which is being analysed by the code.

Best you could do is use the productivity tool to drill down to your content controls and see where they fit into the heirarchy of StdElement, SdtBlock, SdtRun, etc...

The productivity tool really is your best friend.
0

Featured Post

DevOps Toolchain Recommendations

Read this Gartner Research Note and discover how your IT organization can automate and optimize DevOps processes using a toolchain architecture.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
Many times as a report developer I've been asked to display normalized data such as three rows with values Jack, Joe, and Bob as a single comma-separated string such as 'Jack, Joe, Bob', and vice versa.  Here's how to do it. 
This tutorial demonstrates a quick way of adding group price to multiple Magento products.
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

914 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now