Link to home
Start Free TrialLog in
Avatar of tampsystems
tampsystemsFlag for United States of America

asked on

OpenXML Iterate and Replace

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

Avatar of nulliusinverba
nulliusinverba
Flag of Australia image

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.
Avatar of tampsystems

ASKER

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
ASKER CERTIFIED SOLUTION
Avatar of nulliusinverba
nulliusinverba
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial