【问题标题】:C# Word Document Replace PlainText with mergefieldC# Word 文档用合并域替换纯文本
【发布时间】:2018-05-15 02:46:00
【问题描述】:

我有一个 word 文档模板和一个 CSV,我想将其邮件合并。

在 word 文档中,如果我想用它来邮件合并,我有用 > 包围的文本,这与我的 csv 中的标题匹配。例如,我的 word 文档中有 <<Salutation>>,我的 csv 中有字段名称 Salutation。

有没有一种简单的方法可以将 > 包围的文本替换为与 CSV 中的标题对应的邮件合并字段?

到目前为止,我用于读取数据的代码是:

Microsoft.Office.Interop.Word.Application _wordApp = new Microsoft.Office.Interop.Word.Application();
        Microsoft.Office.Interop.Word.Document oDoc = _wordApp.Documents.Add(@"C:\Eyre\Template.docx");
        _wordApp.Visible = true;
        oDoc.MailMerge.MainDocumentType = Microsoft.Office.Interop.Word.WdMailMergeMainDocType.wdFormLetters;
        oDoc.MailMerge.OpenDataSource(@"C:\Eyre\CSV.csv", false, false, true);

        oDoc.MailMerge.Destination = Microsoft.Office.Interop.Word.WdMailMergeDestination.wdSendToNewDocument;
        oDoc.MailMerge.Execute(false);
        Microsoft.Office.Interop.Word.Document oLetters = _wordApp.ActiveDocument;
        oLetters.SaveAs2(@"C:\Eyre\letters.docx",
             Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatDocumentDefault);

任何帮助将不胜感激

---编辑---

这似乎让一些人感到困惑。我有一个带有纯文本的 word 模板,例如 Salutation,需要一个 C# 程序,用 csv 中的合并字段替换此纯文本。

【问题讨论】:

    标签: c# ms-word mailmerge


    【解决方案1】:

    这是一个 C# 版本的代码,用于将 Word 文档中的“占位符”替换为合并字段。 (对于寻找 VB 版本的读者,请参阅https://stackoverflow.com/a/50159375/3077495。)

    我的代码使用了一个已经运行的 Word 实例,所以您感兴趣的部分从 foreach (Word.MailMergeDataField...

    查找/替换操作在它们自己的过程 ReplaceTextWithMergeField 中,数据源字段的名称(如 Word 所见!)和搜索的目标范围被传递到该过程。

    注意在此过程中尖括号对是如何附加到数据字段名称的。

    Find/Replace 操作是标准的,重新设置继续搜索数据字段名称的 Range 对象有点不同,因为有必要获取合并字段外部的位置- 插入字段后,范围位于字段代码内。如果不这样做,Find 可能会“无限”地出现在同一个字段中。 (注意:在这种情况下,不是用双尖括号。但是如果有人在没有它们的情况下使用代码,那么就会出现问题。)

    编辑:为了在Shape 对象中查找和替换,这些对象必须单独循环。使用文本换行格式化的任何内容都位于文档的不同层中,并且不属于Document.Content。我在第三个过程中调整了查找过程,以搜索文档的ShapeRange,特别是测试文本框。

        private void btnDataToMergeFields_Click(object sender, EventArgs e)
        {
            getWordInstance();
            if (wdApp != null)
            {
                if (wdApp.Documents.Count > 0)
                {
                Word.Document doc = wdApp.ActiveDocument;
                Word.Range rng = doc.Content;
                Word.ShapeRange rngShapes = rng.ShapeRange;
    
                if (doc.MailMerge.MainDocumentType != Word.WdMailMergeMainDocType.wdNotAMergeDocument)
                    foreach (Word.MailMergeDataField mmDataField in doc.MailMerge.DataSource.DataFields)
                    {
                      System.Diagnostics.Debug.Print(ReplaceTextWithMergeField(mmDataField.Name, ref rng).ToString() 
                          + " merge fields inserted for " + mmDataField.Name);
                      rng = doc.Content;
                      System.Diagnostics.Debug.Print(ReplaceTextWithMergeFieldInShapes(mmDataField.Name, ref rngShapes)
                          + " mergefields inserted for " + mmDataField.Name);
    
                    }
                }
            }
        }
    
        //returns the number of times the merge field was inserted
        public int ReplaceTextWithMergeField(string sFieldName, ref Word.Range oRng)
        {
            int iFieldCounter = 0;
            Word.Field fldMerge;
            bool bFound;
    
            oRng.Find.ClearFormatting();
            oRng.Find.Forward = true;
            oRng.Find.Wrap = Word.WdFindWrap.wdFindStop;
            oRng.Find.Format = false;
            oRng.Find.MatchCase = false;
            oRng.Find.MatchWholeWord = false;
            oRng.Find.MatchWildcards = false;
            oRng.Find.MatchSoundsLike = false;
            oRng.Find.MatchAllWordForms = false;
            oRng.Find.Text = "<<" + sFieldName + ">>";
            bFound = oRng.Find.Execute();
            while (bFound)
            {
                iFieldCounter = iFieldCounter + 1;
                fldMerge = oRng.Fields.Add(oRng, Word.WdFieldType.wdFieldMergeField, sFieldName, false);
                oRng = fldMerge.Result;
                oRng.Collapse(Word.WdCollapseDirection.wdCollapseEnd);
                oRng.MoveStart(Word.WdUnits.wdCharacter, 2);
                oRng.End = oRng.Document.Content.End;
                oRng.Find.Text = "<<" + sFieldName + ">>";
                bFound = oRng.Find.Execute();
            }
            return iFieldCounter;
        }
    
        public int ReplaceTextWithMergeFieldInShapes(string sFieldName,
                                   ref Word.ShapeRange oRng)
        {
            int iFieldCounter = 0;
            Word.Field fldMerge;
            bool bFound;
    
            foreach (Word.Shape shp in oRng)
            {
                if (shp.Type == Office.MsoShapeType.msoTextBox)
                {
                    Word.Range rngText = shp.TextFrame.TextRange;
                    rngText.Find.ClearFormatting();
                    rngText.Find.Forward = true;
                    rngText.Find.Wrap = Word.WdFindWrap.wdFindStop;
                    rngText.Find.Format = false;
                    rngText.Find.MatchCase = false;
                    rngText.Find.MatchWholeWord = false;
                    rngText.Find.MatchWildcards = false;
                    rngText.Find.MatchSoundsLike = false;
                    rngText.Find.MatchAllWordForms = false;
                    rngText.Find.Text = "<<" + sFieldName + ">>";
                    bFound = rngText.Find.Execute();
                    while (bFound)
                    {
                        iFieldCounter = iFieldCounter + 1;
                        fldMerge = rngText.Fields.Add(rngText, Word.WdFieldType.wdFieldMergeField, sFieldName, false);
                        rngText = fldMerge.Result;
                        rngText.Collapse(Word.WdCollapseDirection.wdCollapseEnd);
                        rngText.MoveStart(Word.WdUnits.wdCharacter, 2);
                        rngText.End = shp.TextFrame.TextRange.End;
                        rngText.Find.Text = sFieldName;
                        bFound = rngText.Find.Execute();
                    }
                }
            }
            return iFieldCounter;
        }
    

    【讨论】:

    • 谢谢你,但它似乎并不总是有效。我有一个带有文本 > 的文本框,它不会被拾取,但如果我将它放在文本框外的正文中,它会。有什么想法吗?
    • 我已添加到我的答案中,包括您看到的原因。
    • 非常感谢辛迪,这是一个救星! :)
    【解决方案2】:

    根据您更广泛的要求,有多种方法。如果您将根据需要在 Windows 机器上为简单/小型任务运行该工具,那么 VBA/宏方法可能是最好的,因为您已经拥有了所需的东西。

    另一种方法需要更多的编码和对 DOCX 的理解,但您可以扩展它并在没有 MS Office 库的机器上运行它。由于 DocX 是开放的且基于文本的,您可以将其解压缩处理 XML 内容并重新压缩。有一些陷阱,因为 XML 不是微不足道的。如果您这样做,使用 Word 合并字段(对于程序员而言)比纯文本更好,因为查找字段更简单。纯文本对于使用文档/模板的人来说更好,因为他们不必处理合并字段,但缺点是 XML 处理可能变得更加复杂。模板 &lt;&lt;Salutation&gt;&gt; 中的文本可能不容易找到 XML - 它可能会被分割成几部分。

    另一种解决方案是使用像 Docmosis 之类的东西(一种商业产品 - 请注意我为 Docmosis 工作)。好处是 Docmosis 可以完成替换和更复杂的要求(例如条件和循环结构,PDF 转换)。缺点是您必须学习 API 并安装软件(或调用云端),还要将数据转换成格式以传递给引擎。

    希望对你有帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-24
      • 2014-04-04
      • 2019-12-12
      • 1970-01-01
      • 2013-08-21
      • 2017-11-07
      • 2010-12-26
      相关资源
      最近更新 更多