C# Word 文档用 mergefield 替换 PlainText
C# Word Document Replace PlainText with mergefield
我有一个 word 文档模板和一个 CSV 文件,我想将其与邮件合并。
在 word 文档中,我用 <<>> 包围了文本,如果我想将它用于邮件合并,这与我的 csv 中的 headers 相匹配。例如,我的 word 文档中有 <<Salutation>>
,而我的 csv 中有字段名称 Salutation。
是否有一种简单的方法可以将 <<>> 包围的文本替换为与其在 CSV 中的 header 相对应的邮件合并字段?
我目前用于读取数据的代码是:
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 的合并字段替换这个纯文本。
根据您更广泛的要求,有多种方法。如果您 运行 在您的 Windows 机器上完成简单/小型任务所需的工具,那么 VBA/macro 方法可能是最好的,因为您已经拥有所需的东西。
另一种方法需要更多编码和对 DOCX 的理解,但您可以在没有 MS Office 库的机器上扩展它并运行它。由于 DocX 是开放的并且是基于文本的,您可以解压缩它处理 XML 内容并重新压缩。有一些陷阱,因为 XML 不是微不足道的。如果您这样做,使用 Word Merge Fields 比纯文本更好(对于程序员),因为查找字段更简单。纯文本更适合使用 Document/Template 的人,因为他们不必处理合并字段,但缺点是 XML 处理会变得更加复杂。模板 <<Salutation>>
中的文本可能不容易找到 XML - 它可以拆分成多个部分。
另一个解决方案是使用类似 Docmosis 的东西(商业产品 - 请注意我为 Docmosis 工作)。好处是 Docmosis 可以完成替换和更复杂的要求(例如条件和循环结构,PDF 转换)。缺点是你必须学习 API 并安装软件(或调用云),并将你的数据转换为一种格式以传递给引擎。
希望对您有所帮助。
这是一个 C# 版本的代码,用于用合并字段替换 Word 文档中的 "placeholders"。 (对于寻找 VB 版本的读者,请参阅 。)
我的代码使用了一个已经 运行 的 Word 实例,所以您感兴趣的部分从 foreach (Word.MailMergeDataField
...
开始
Find/Replace 操作在它们自己的过程中 ReplaceTextWithMergeField
,数据源字段的名称(如 Word 所见!)和搜索的目标范围被传递到该过程。
请注意在此过程中如何将尖括号对附加到数据字段名称。
Find/Replace动作是标准的,重新设置Range
继续搜索数据字段名的对象有点不同,因为需要获取位置在 合并字段之外 - 插入字段后,范围在字段代码内。如果不这样做,Find 可能会在同一字段中结束 "infinitely"。 (注意:在这种情况下,没有双尖括号。但是如果有人在没有它们的情况下使用代码,那么问题就会出现。)
编辑:为了在 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;
}
我有一个 word 文档模板和一个 CSV 文件,我想将其与邮件合并。
在 word 文档中,我用 <<>> 包围了文本,如果我想将它用于邮件合并,这与我的 csv 中的 headers 相匹配。例如,我的 word 文档中有 <<Salutation>>
,而我的 csv 中有字段名称 Salutation。
是否有一种简单的方法可以将 <<>> 包围的文本替换为与其在 CSV 中的 header 相对应的邮件合并字段?
我目前用于读取数据的代码是:
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 的合并字段替换这个纯文本。
根据您更广泛的要求,有多种方法。如果您 运行 在您的 Windows 机器上完成简单/小型任务所需的工具,那么 VBA/macro 方法可能是最好的,因为您已经拥有所需的东西。
另一种方法需要更多编码和对 DOCX 的理解,但您可以在没有 MS Office 库的机器上扩展它并运行它。由于 DocX 是开放的并且是基于文本的,您可以解压缩它处理 XML 内容并重新压缩。有一些陷阱,因为 XML 不是微不足道的。如果您这样做,使用 Word Merge Fields 比纯文本更好(对于程序员),因为查找字段更简单。纯文本更适合使用 Document/Template 的人,因为他们不必处理合并字段,但缺点是 XML 处理会变得更加复杂。模板 <<Salutation>>
中的文本可能不容易找到 XML - 它可以拆分成多个部分。
另一个解决方案是使用类似 Docmosis 的东西(商业产品 - 请注意我为 Docmosis 工作)。好处是 Docmosis 可以完成替换和更复杂的要求(例如条件和循环结构,PDF 转换)。缺点是你必须学习 API 并安装软件(或调用云),并将你的数据转换为一种格式以传递给引擎。
希望对您有所帮助。
这是一个 C# 版本的代码,用于用合并字段替换 Word 文档中的 "placeholders"。 (对于寻找 VB 版本的读者,请参阅
我的代码使用了一个已经 运行 的 Word 实例,所以您感兴趣的部分从 foreach (Word.MailMergeDataField
...
Find/Replace 操作在它们自己的过程中 ReplaceTextWithMergeField
,数据源字段的名称(如 Word 所见!)和搜索的目标范围被传递到该过程。
请注意在此过程中如何将尖括号对附加到数据字段名称。
Find/Replace动作是标准的,重新设置Range
继续搜索数据字段名的对象有点不同,因为需要获取位置在 合并字段之外 - 插入字段后,范围在字段代码内。如果不这样做,Find 可能会在同一字段中结束 "infinitely"。 (注意:在这种情况下,没有双尖括号。但是如果有人在没有它们的情况下使用代码,那么问题就会出现。)
编辑:为了在 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;
}