0

I have approximately 10 MERGEFIELD in a document that I'm trying to replace the Text with some value. Here's the code.

using (WordprocessingDocument document = WordprocessingDocument.Open(destinationFileName, true))
            {
                document.ChangeDocumentType(DocumentFormat.OpenXml.WordprocessingDocumentType.Document);
                MainDocumentPart docPart = document.MainDocumentPart;
                docPart.AddExternalRelationship("http://schemas.openxmlformats.org/officeDocument/2006/relationships/attachedTemplate", new Uri(destinationFileName, UriKind.RelativeOrAbsolute));
                docPart.Document.Save();

                IEnumerable<FieldChar> fldChars = document.MainDocumentPart.RootElement.Descendants<FieldChar>();
                if (fldChars == null) { return; }

                string fieldList = string.Empty;
                FieldChar fldCharStart = null;
                FieldChar fldCharEnd = null;
                FieldChar fldCharSep = null;
                FieldCode fldCode = null;
                string fldContent = String.Empty;
                int i = 1;
                foreach(var fldChar in fldChars)
                {

                    System.Diagnostics.Debug.WriteLine(i + ": " + fldChar);
                    i++;

                    string fldCharPart = fldChar.FieldCharType.ToString();
                    System.Diagnostics.Debug.WriteLine("Field Char Length: " + fldChar.Count());
                    System.Diagnostics.Debug.WriteLine("Field Char part: " + fldCharPart);

                    switch(fldCharPart)
                    {    
                        case "begin": // start of the field
                            fldCharStart = fldChar;
                            System.Diagnostics.Debug.WriteLine("Field Char Start: " + fldCharStart);
                            // get the field code, which will be an instrText element
                            // either as sibling or as a child of the parent sibling

                            fldCode = fldCharStart.Parent.Descendants<FieldCode>().FirstOrDefault();
                            System.Diagnostics.Debug.WriteLine("Field Code: " + fldCode);

                            if (fldCode == null)
                            {
                                fldCode = fldCharStart.Parent.NextSibling<Run>().Descendants<FieldCode>().FirstOrDefault();
                                System.Diagnostics.Debug.WriteLine("New Field Code: " + fldCode);
                            }

                            if (fldCode != null && fldCode.InnerText.Contains("MERGEFIELD"))
                            {
                                fldContent = getFieldValue(query, prescriber, fldCode.InnerText);
                                fieldList += fldContent + "\n";
                                System.Diagnostics.Debug.WriteLine("Field content: " + fldContent);

                            }
                            break;

                        case "end": // end of the field
                            fldCharEnd = fldChar;
                            System.Diagnostics.Debug.WriteLine("Field char end: " + fldCharEnd);
                            break;

                        case "separate": // complex field with text result
                            fldCharSep = fldChar;
                            break;

                        default:
                            break;  

                    }

                    if((fldCharStart != null) && (fldCharEnd != null))
                    {
                        if(fldCharSep != null)
                        {
                            Text elemText = (Text)fldCharSep.Parent.NextSibling().Descendants<Text>().FirstOrDefault();
                            elemText.Text = fldContent;
                            System.Diagnostics.Debug.WriteLine("Element text: " + elemText);

                            // Delete all field chas with their runs
                            DeleteFieldChar(fldCharStart);
                            DeleteFieldChar(fldCharEnd);
                            DeleteFieldChar(fldCharSep);
                            fldCode.Remove();
                        }
                        else
                        {
                            Text elemText = new Text(fldContent);
                            fldCode.Parent.Append(elemText);
                            fldCode.Remove();
                            System.Diagnostics.Debug.WriteLine("Element Text !sep: " + elemText);

                            DeleteFieldChar(fldCharStart);
                            DeleteFieldChar(fldCharEnd);
                            DeleteFieldChar(fldCharSep);
                        }

                        fldCharStart = null;
                        fldCharEnd = null;
                        fldCharSep = null;
                        fldCode = null;
                        fldContent = string.Empty;
                    }
                }
                System.Diagnostics.Debug.WriteLine("Field list: " + fieldList);
            }

It works to some extent. The problem is when there are more than one field in a paragraph. I have about 4 merge fields in one paragraph in this document, and one field in each paragraph after that. Only the first merge field in the paragraph is being updated and the rest fields in the paragraphs is untouched. Then, it moves to the next paragraph and looks for the field. How can I fix this?

user1828605
  • 1,723
  • 1
  • 24
  • 63

1 Answers1

2

Looks like you are over complicating a simple Mailmerge replacement. Instead of looping through paragraphs you could rather get all mailmerge fields within a document and replace them.

    private const string FieldDelimeter = " MERGEFIELD ";

    foreach (FieldCode field in doc.MainDocumentPart.RootElement.Descendants<FieldCode>())
    {
         var fieldNameStart = field.Text.LastIndexOf(FieldDelimeter, System.StringComparison.Ordinal);
         var fieldName = field.Text.Substring(fieldNameStart + FieldDelimeter.Length).Trim();

         foreach (Run run in doc.MainDocumentPart.Document.Descendants<Run>())
         {
               foreach (Text txtFromRun in run.Descendants<Text>().Where(a => a.Text == "«" + fieldName + "»"))
               {                            
                     txtFromRun.Text = "Replace what the merge field here";
               }
         }
     } 


    doc.MainDocumentPart.Document.Save();

doc is of type WordprocessingDocument.

This will replace all merge fields regardless of the amount of fields in a paragraph.

SpaceApple
  • 1,309
  • 1
  • 24
  • 46
  • This didn't work. It doesn't replace the fields with the value. The fieldName shows up on mine as `fieldName \* MERGEFORMAT`, could it have anything to do with it? But even when I replaced "<<" + fieldName + ">>" with `\* MERGEFORMAT`, it still won't go in to the second foreach loop. Any suggestions? – user1828605 Jul 29 '16 at 21:47
  • This is the correct answer. I had made a mistake while extracting the field name. I didn't do Trim() which caused a space after the fieldname to not match the text. Even after doing the Trim() and Debugging, I was still getting error because I had apparently done substring again on the the trimmed data. It's working now. Thanks for showing me how to do it. I feel like I understand it better now. – user1828605 Aug 01 '16 at 15:49
  • 1
    How "fldSimple" in MERGEFIELD can be handled? I have case in which hyperlink is being generated using MERGEFIELD. – par May 31 '17 at 05:46