简体   繁体   中英

Openxml in C# updating only the first MERGEFIELD in a paragraph

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?

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM