Solved

OpenXML Iterate and Replace

Posted on 2011-03-24
3
3,486 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

Master Your Team's Linux and Cloud Stack!

The average business loses $13.5M per year to ineffective training (per 1,000 employees). Keep ahead of the competition and combine in-person quality with online cost and flexibility by training with Linux Academy.

Question has a verified solution.

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

Today I had a very interesting conundrum that had to get solved quickly. Needless to say, it wasn't resolved quickly because when we needed it we were very rushed, but as soon as the conference call was over and I took a step back I saw the correct …
Real-time is more about the business, not the technology. In day-to-day life, to make real-time decisions like buying or investing, business needs the latest information(e.g. Gold Rate/Stock Rate). Unlike traditional days, you need not wait for a fe…
This Micro Tutorial hows how you can integrate  Mac OSX to a Windows Active Directory Domain. Apple has made it easy to allow users to bind their macs to a windows domain with relative ease. The following video show how to bind OSX Mavericks to …
Two types of users will appreciate AOMEI Backupper Pro: 1 - Those with PCIe drives (and haven't found cloning software that works on them). 2 - Those who want a fast clone of their boot drive (no re-boots needed) and it can clone your drive wh…

785 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