How do you mail merge a word document in c#

Ryan Searle picture Ryan Searle · Aug 7, 2015 · Viewed 21.1k times · Source

What I'm trying to achieve

In my c# application I would like to generate a report (word document) from data in my application, I figured that the best way to do this would be to perform something like a mail merge using the data source from my application.

What I've tried

  1. I tried following this Mail Merge into word however this uses GemBox which you need to pay for
  2. I have tried using Microsoft.Office.Interop.Word however I fell short when I didn't know how to reference the saved template document:

    Dictionary<string, string> MailMerge = new Dictionary<string, string>()
        {
            { "ID", "123" },
            { "Name", "Test" },
            { "Address1", "Test" },
            { "Address2", "Test" },
            { "Address3", "Test" },
            { "Address4", "Test" },
            { "PostCode", "Test" },
            { "Year End", "Test" },
            { "SicCode", "123" },
        };
    
        Document doc = new Document();
        doc.MailMerge.Execute(MailMerge);
    

Summary

I'm looking for some guidance as to what to research further as I believe there must be a 'standard' way of doing this.

Answer

David Leitner picture David Leitner · Aug 7, 2015

This is quite simple by using Microsoft.Office.Interop.Word. Here is a simple step by step tutorial on how to do this.

The code to replace a mergefield with a string is like this:

public static void TextToWord(string pWordDoc, string pMergeField, string pValue)
{
    Object oMissing = System.Reflection.Missing.Value;
    Object oTrue = true;
    Object oFalse = false;
    Word.Application oWord = new Word.Application();
    Word.Document oWordDoc = new Word.Document();
    oWord.Visible = true;
    Object oTemplatePath = pWordDoc;
    oWordDoc = oWord.Documents.Add(ref oTemplatePath, ref oMissing, ref oMissing, ref oMissing);
    foreach (Word.Field myMergeField in oWordDoc.Fields)
    {
        Word.Range rngFieldCode = myMergeField.Code;
        String fieldText = rngFieldCode.Text;
        if (fieldText.StartsWith(" MERGEFIELD"))
        {
            Int32 endMerge = fieldText.IndexOf("\\");
            Int32 fieldNameLength = fieldText.Length - endMerge;
            String fieldName = fieldText.Substring(11, endMerge - 11);
            fieldName = fieldName.Trim();
            if (fieldName == pMergeField)
            {
                myMergeField.Select();
                oWord.Selection.TypeText(pValue);
            }
        }
    }
}

originally posted here and here

In case you wish to use a dictionary to replace many fields at once use the code below:

public static void TextToWord(string pWordDoc, Dictionary<string, string> pDictionaryMerge)
    {
        Object oMissing = System.Reflection.Missing.Value;
        Object oTrue = true;
        Object oFalse = false;
        Microsoft.Office.Interop.Word.Application oWord = new Microsoft.Office.Interop.Word.Application();
        Microsoft.Office.Interop.Word.Document oWordDoc = new Microsoft.Office.Interop.Word.Document();
        oWord.Visible = true;
        Object oTemplatePath = pWordDoc;
        oWordDoc = oWord.Documents.Add(ref oTemplatePath, ref oMissing, ref oMissing, ref oMissing);

        foreach (Microsoft.Office.Interop.Word.Field myMergeField in oWordDoc.Fields)
        {
            Microsoft.Office.Interop.Word.Range rngFieldCode = myMergeField.Code;
            String fieldText = rngFieldCode.Text;
            if (fieldText.StartsWith(" MERGEFIELD"))
            {
                Int32 endMerge = fieldText.IndexOf("\\");
                Int32 fieldNameLength = fieldText.Length - endMerge;
                String fieldName = fieldText.Substring(11, endMerge - 11);
                fieldName = fieldName.Trim();
                foreach (var item in pDictionaryMerge)
                {
                    if (fieldName == item.Key)
                    {
                        myMergeField.Select();
                        oWord.Selection.TypeText(item.Value);
                    }
                }
            }
        }
    }