Primitive MailMerge using just delimited field names


Obviously the correct way for our app to generate a Word document based on a template is to encourage the admin users (who will look after the templates) to embed the correct merge fields or bookmarks in the template document.

However in the past we have found our typical admin user who ordinarily doesn't use MailMerge or any of the other "advanced" features in Word is significantly put off by having to use merge fields. We have tried doing it for them, producing documentation, lots of screenshots etc. But it's all "fiddly" for them.

They have dozens of templates (all just different kinds of really simple letters), and will want to modify them reasonably frequently.

What they would really like is to be able to just mark fields with a simple delimiter like a curly brace, which effectively marks a homemade merge field to our app (though Word is oblivious to its significance) as in:


Dear {CustomerSurname}


Then we can just pick up the field(s) with several lines of code as in:

w = New Word.Application d = w.Documents.Open(...) Dim mergedContent As String = d.Content.Text mergedContent = mergedContent.Replace("{CustomerSurname}", Customer.Surname) mergedContent = mergedContent.Replace("{CustomerPostcode}", Customer.Postcode) d.Content.Text = mergedContent

This feels crude, but beautifully simple (for the end user).

Has anyone else gone down this route? Anything wrong with it? We would advise them not to use the "{" and "}" character elsewhere in the normal text of the document, but that's not really a significant limitation.

Speed? Wrapping the merge field across two lines? Other problems?


This is a part of my code that I used to find and replace. I tried your code first but that didn't work. This is based upon the VBA code that Word generates when you record a macro.

range = document.Range() With range.Find .Text = "{" & name & "}" .Replacement.Text = NewValueHere .Forward = True .Wrap = Word.WdFindWrap.wdFindContinue .Format = False .MatchCase = True .MatchWholeWord = False .MatchWildcards = False .MatchSoundsLike = False .MatchAllWordForms = False End With range.Find.Execute(Replace:=Word.WdReplace.wdReplaceAll)

I used this code because the documents needed to be compatible with the Office binary file formats. If you can use the Office 2007 format you don't need to have Word installed. Just unzip the document, find the word\document.xml file, do a String.Replace("[OldValue]", "New value"), save the file and zip it back to one package (docx file). The code I displayed here is pretty slow because I'm automating Word. Opening Word, editing 2 documents with 6 fields and closing the app takes 4 seconds on my pc.


What if the user does want to use curly braces? I think you should provide a way to escape them for example /{/} or {{}} etc.

You need to make sure that your replace logic is case insensitive for example both {CustomerSurname} and {Customersurname} should be allowed to represent the same field. May be even optionally allow spaces between words like {Customer surname}.


  • jQuery UI Slider - Change background color ONLY between handlers/sliders/markers
  • jQuery UI Slider - Change background color ONLY between handlers/sliders/markers
  • Creating Range from constant Ints
  • How to add CUDA 7.0 acceleration to existing project?
  • LINQ to JSon response
  • How to use IN in sql query when table has key , values pairs
  • What is the ComposableAttribute in WinRT?
  • Leave parent window active when child opens
  • Switch to original buffer after chasing tags in Emacs
  • Jackson Parser can't read backslash quotation marks in String
  • How to change the color of a check mark or 'more' arrow on a WinForms context menu?
  • What happens technically when a file is required in Ruby?
  • ST3 swap priority of tab function within a snippet (nested snippets)
  • Question mark icons showing up for quotation marks when there's a UTF-8 character encoding
  • Sql Server 2008 sp_executesql syntax help - I think my quotes are not correct
  • Reading XML into Datatable gives incorrect DateTime when the time has Time Zone info
  • 'doc_del_count' bigger than 'doc_count' on CouchDB
  • c# winform DrawToBitmap offscreen
  • Excel VBA URLDownloadToFile Error for https Ressource
  • Creating a Multidimensional, Associative Array in VBScript
  • Changing Jupyter Notebook start up folder by modifying “start in” not working any more
  • What's the syntax to inherit documentation from another indexer?
  • Why the SequenceFile is truncated?
  • DIV instruction jumping to random location?
  • Conversion from string “a” to type 'Boolean' is not valid
  • R - Combining Columns to String Based on Logical Match
  • Read text file and split every line in MSBuild
  • DirectX11 ClearRenderTargetViewback with transparent buffer?
  • Can I make an Android app that runs a web view in Chrome 39?
  • Return words with double consecutive letters
  • how to add data labels for bar graph in matlab
  • Windows forms listbox.selecteditem displaying “System.Data.DataRowView” instead of actual value
  • Acquiring multiple attributes from .xml file in c#
  • How to CLICK on IE download dialog box i.e.(Open, Save, Save As…)
  • LevelDB C iterator
  • Linking SubReports Without LinkChild/LinkMaster
  • apache spark aggregate function using min value
  • How can I remove ASP.NET Designer.cs files?
  • Sorting a 2D array using the second column C++
  • java string with new operator and a literal