Ty Anderson

Working with Microsoft Word templates: C# sample

This is a continuation of the work we did in my previous article – Populating Word documents with data from external sources. The difference is, we will use a pre-built template for inserting data. Instead of inserting data “willy-nilly”, we need to find where to insert it within the document’s pre-defined structure.

Creating a Word template

Before we write code, we need a document template. After we build the template, we can write code around it because we know its structure. We might, if we are living right, have an idea about how the document will be utilized by users.

For this sample, you need to do the following:

  1. Open Microsoft Word.
  2. Create a blank document.
  3. Perform a Save As command and save the file as .dotx file. You can save to the location of your choice. Write down its location because you will need when you test your code. Name the file DataTemplate.dotx because that’s what I named mine.
  4. Design the first page according the image below:
    First page design
  5. Insert a page break to add another page to the Word document.
  6. Design the page according to the image below:
    Design of the page with bookmarks
  7. Be sure to insert the bookmarks as directed. You can find the bookmark command in Word’s Insert tab. After you add both bookmarks, the bookmark dialog box will look like this:
    Word Bookmark dialog with two added bookmarks
  8. Save the document.

We now have a template document with two pages. Both have two tables but the second page’s tables reside within Word bookmarks. We are ready to write some code.

Write the code

The code for today centers on how to find the tables in the document. The tables act as placeholders for the data we want to insert.

The findTable function

First up, we need a function that finds the desired table. Word doesn’t provide a way for users to name the tables. We can do it in code via the ID property but this is useless to a user building templates.

Therefore, the template builder creates tables and insert a name for it the first cell. We can then loop through all the tables in the document and search for our table. This is what the findTable function does.

private Word.Table findTable(Word.Document doc, string searchText)
{
    if (doc.Tables.Count > 0)
    {
        int iCount = 1;
        Word.Table tbl;
        do
        {
            tbl = (Word.Table)doc.Tables[iCount];
            //Select first cell and check it's value
            string header = tbl.Cell(1, 1).Range.Text;
            if (header.Contains(searchText))
            {
                return tbl;
            }
            iCount++;
            Marshal.ReleaseComObject(tbl);
        } while (iCount < doc.Tables.Count + 1);
    }
    return null;
}

The function accepts a Word document and a string as parameters. It then loops through the document’s tables and finds the table that contains the search string. I decided to use Contains versus == as it is more forgiving.

The getTableByBookmarkName function

Using bookmarks simplifies the code. All we need to do is call the bookmark by name and retrieve the first table within the bookmark’s range. The getTableByBookmarkName cleanly handles this need.

private Word.Table getTableByBookmarkName(Word.Document doc,
    string bookmarkName)
{
    Word.Table tbl = doc.Bookmarks[bookmarkName].Range.Tables[1];
    if (tbl != null)
        return tbl;
    else
        return null;
}

For our purposes, I assume the bookmark contains only table or, if multiple tables exist, that the first table is the one we want. It’s easy enough to change this logic should business requirements dictate otherwise.

Required code changes to the previous article

To incorporate the functions above into our data insertion methods, we need to make a couple of changes. For the purposes of this article, I’ll focus on importing from Access and Outlook. Yes, the previous article imported from SharePoint as well but I think we only need to samples to drive the point.

Import data from Access

The ImportFromAccess method now has a parameter called demoType. This is a string and allow us to specify what method we want to use for finding a table. Do you we want to use the bookmark strategy or the table looping strategy? That’s why demoType exists.

We check demoType‘s value and split accordingly. If the value is “table” we call findTable and loop through the document’s tables. Otherwise, we use the bookmark method by calling getTableByBookmarkName.

private void InsertFromAccess(string demoType)
{
    Word.Table tbl;
    if (demoType == "table")
    {
         tbl = findTable(this.WordApp.ActiveDocument, "Company List");
    }
    else
    {
         tbl = getTableByBookmarkName(this.WordApp.ActiveDocument, "CompanyList");
    }
    if (tbl == null) return;
 
    //We need a counter...
    int i = 1;
 
    //Call the database.
    OleDbConnection cnn = new OleDbConnection();
 
    cnn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" +
        "C:\\Users\\Tyrant\\Desktop\\PopulateWordWithData\\Northwind2007.accdb;" +
        "Persist Security Info=False;";
 
    string sql = "SELECT Top 11 Company FROM Customers ";
    OleDbCommand command = new OleDbCommand(sql, cnn);
    cnn.Open();
    OleDbDataReader reader = command.ExecuteReader();
 
    if (reader.HasRows)
    {
        while (reader.Read())
        {
            i = i + i;
            tbl.Rows.Add();
            tbl.Cell(i, 1).Range.Text = reader[0].ToString();
        }
    }
    Marshal.ReleaseComObject(tbl);
}

With the table in-hand, we query access and insert the query results into the table.

Import from Outlook

InsertFromOutlook works the same as InserFromAccess. We use the demoType parameter to specify how to retrieve the table. After retrieving the table, we open Outlook and insert contact data into the table.

private void InsertFromOutlook(string demoType)
{
    Word.Table tbl;
    if (demoType == "table")
    {
        tbl = findTable(this.WordApp.ActiveDocument, "Company List");
    }
    else
    {
        tbl = getTableByBookmarkName(this.WordApp.ActiveDocument, "CompanyList");
    }
    if (tbl == null) return;
 
    //Add two columns
    if (tbl.Columns.Count == 1)
    {
        //1 Column
        tbl.Columns.Add();
        //2 Columns
        tbl.Columns.Add();
 
        //pretty basic logic...what happens if the table template had 2 columns?
        //with my code...nothing...which would be a problem.
        //it's a sample, so make of it what you wish.
    }
 
    //We need a counter...
    int i = 1;
    //And a table header
    tbl.Cell(i, 1).Range.Text = "Contact Name";
    tbl.Cell(i, 2).Range.Text = "Phone Number";
    tbl.Cell(i, 3).Range.Text = "Email Address";
 
    //start outlook
    Outlook.Application appOL = new Outlook.Application();
    Outlook._NameSpace ns = appOL.Session;
    Outlook.MAPIFolder fldr = 
        ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderContacts);
 
    Outlook.Items items = fldr.Items;
    if (items.Count > 0)
    {
        Outlook.ContactItem buddy;
        int iFldrCount = items.Count;
        int iContacts = 1;
        do
        {
            tbl.Rows.Add();
            i = i + 1;
            buddy = items[iContacts];
            tbl.Cell(i, 1).Range.Text = buddy.FullName;
            tbl.Cell(i, 2).Range.Text = buddy.MobileTelephoneNumber;
            tbl.Cell(i, 3).Range.Text = buddy.Email1Address;
            iContacts++;
            Marshal.ReleaseComObject(buddy);
        }
        while (iContacts < iFldrCount + 1);
    }
    tbl.AutoFitBehavior(Word.WdAutoFitBehavior.wdAutoFitContent);
 
    Marshal.ReleaseComObject(items);
    Marshal.ReleaseComObject(ns);
    Marshal.ReleaseComObject(fldr);
    appOL.Quit();
    Marshal.ReleaseComObject(appOL);
    Marshal.ReleaseComObject(tbl);
}

A key different with this method is we add two columns to the table and set values for the header row.

For production code, the demoType parameter doesn’t make any sense. It sure works to highlight the two strategies however.

***

When it comes to inserting data using a Word document template, the first step is to build the template structure. The next step is to be creative with your code and build a solution around the template’s structure.

Available downloads:

This sample Word add-in was developed using Add-in Express for Office and .net:

Working with Word Templates add-in (C#)

Word add-in development in Visual Studio for beginners:

Post a comment

Have any questions? Ask us right now!