Tuesday 30 October 2012

CSharp: Word Mail Merge with data

Question:
How to merge data into a Word document using the mail merge functionality?
Using mail merge in MS Word with a data file.


Answer:


Follow these steps:
  1. You need a MS Word document with merge fields in it. Let's say we've got two merge fields: "name" and "town".
  2. Create a console project and add a reference to Microsoft.Office.Interop.Word.dll. My dll is version 14.
  3. We have our data in a dictionary. But you cannot pass a dictionary to Word's mail merger object. So we write the names and values to a text file.
  4. Finally we ask Word to open our file that contains the merge fields and merge it with the data file. The result file is saved under a new name.
And that is all. It still took me 2 hours to figure it out.
Here is the merger code
private void MailMergeWithData(string pTemplFileName, string pWorkingName, Dictionary<string, string> pValTable)
{
    if (!File.Exists(pTemplFileName))
    {
        Console.WriteLine("templatePath does not exist : " + pTemplFileName);
        return;
    }

    Application wordApp = null;
    Document wordDoc = null;
    Document wordDoc2 = null;
    string tempDataFile = "c:\\tmp\\data_doc.txt";

    try
    {
        wordApp = new Application();
        wordDoc = wordApp.Documents.Open(pTemplFileName);
        MailMerge mm = wordDoc.MailMerge;
        //wordApp.Visible = true;
                
        CreateDataFile(tempDataFile, pValTable);

        // use unicode if you use more than ascii
        mm.OpenDataSource(tempDataFile, WdOpenFormat.wdOpenFormatUnicodeText, false, true, false, false);
        mm.Destination = WdMailMergeDestination.wdSendToNewDocument;
        mm.Execute(false);

        // the merged doc becomes the active document
        if (wordApp.ActiveDocument != null)
        {
            wordDoc2 = wordApp.ActiveDocument;
            // you need to use a word save format constant to do a save as.
            object wdFormat = WdSaveFormat.wdFormatDocument;
            wordDoc2.SaveAs(pWorkingName, wdFormat);
            wordDoc2.Saved = true;
        }

    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    finally
    {
        if (wordDoc != null)
        {
            wordDoc.Close(false);
            wordDoc = null;
        }
        if (wordDoc2 != null)
        {
            wordDoc2.Close(false);
            wordDoc2 = null;
        }
        wordApp.Quit(false);
        wordApp = null;

        File.Delete(tempDataFile);
    }
}


And this it the data file writer code
private void CreateDataFile(string fName, Dictionary<string, string> valTable)
{
    StringBuilder sbHeader = new StringBuilder();
    StringBuilder sbValues = new StringBuilder();
    string header = "";
    string values = "";

    foreach(KeyValuePair<string, string> item in valTable)
    {
        sbHeader.Append(item.Key);
        sbHeader.Append("\t");

        sbValues.Append(item.Value);
        sbValues.Append("\t");
    }
    header = sbHeader.ToString().Trim('\t');
    values = sbValues.ToString().Trim('\t');

    using (FileStream fs = File.OpenWrite(fName))
    {
        using (StreamWriter file = new StreamWriter(fs, Encoding.UTF8))
        {
            file.WriteLine(header);
            file.WriteLine(values);
            file.Close();
        }
    }

}


No comments: