DataWright Information Services

Consulting and Resources for Excel and Access

Refreshing Excel data in a Word document, using bookmarks

  • Download a sample here.

If you are using Word to build a report that relies on Excel tables, you have a couple of ways to achieve it. One is to create links to the Excel file, which is OK if the file is not very large or calculation-intensive, and you don’t have too many bookmarks. However, you may find that you have one or more of the following problems:

  • If you do have a large workbook with many links from the Word document, you will often find that the Word file takes forever to respond.
  • Reproducing the Excel formatting in the Word document can be tedious and will most likely need to be updated when you refresh the file

There are articles showing how to push data from Excel to Word using VBA, [see the following], but I hadn’t come across any that used Word as the driver when I had a recent need to do this. The code needed to meet some specific criteria:

  • Updating needed to be fast, and maintain the formatting in the Excel file
  • The user wanted to be able to select the Excel source file at run time
  • The code needed to be triggered using an item on the toolbar.

The approach that I came up with uses bookmarks in the Word document, matched to range names in the Excel file. This requires the following:

  • A Word document, populated with bookmarks where the Excel content will be pasted
  • An Excel file with the source data, with a defined range name for every table that will be used in the Word document
  • A table in the Excel file with 3 columns, laid out as in the example below. This table must have a defined range name. In the code below, it is called Bookmarks, and lives on the Lists sheet of the Excel workbook. The data could look something like this:
Bookmark Sheet Range Name
Bookmark1001 Main Data PL_01
Bookmark1002 Summary Data CF_02

Note: The sheet name is required because Word cannot reference an Excel range without knowing which sheet the range is on. Also, watch out for the range names in Excel. If you like to use 3-letter acronyms for your ranges, you will have problems updating this code to Office 2007 because the native format in 2007 goes out to column XFD, and Excel won't let you use column or cell references as range names.

The code for the procedure is shown below. First, the declarations…

Sub RefreshAllTables()
''==============================================================================
''Purpose: To refresh the current table in a Word document with new data from
'' the corresponding range in an Excel document.
''The code uses bookmarks in the Word document and corresponding named ranges in 
'' Excel. The Excel data is brought in as pictures. This has the advantage that any 
'' formatting in the Excel document is retained, and the dimensions don't change 
'' significantly.
'' Also, bookmarks are simpler to create and maintain because a picture is only a 
'' single character in a Word document. 
''Requires: A table in the Excel file to line up the bookmarks and named ranges
''Created: 23 Oct 2008 by Denis Wright
''==============================================================================
    Dim objExcel As Object, _
        objWbk As Object, _
        objDoc As Document
    Dim sBookmark As String, _
        sWbkName As String
    Dim sRange As String, _
        sSheet As String
    Dim BMRange As Range
    Dim bmk As Bookmark
    Dim i As Integer, _
        j As Integer, _
        k As Integer, _
        bmkCount As Integer
    Dim vNames()
    Dim vBookmarks()
    Dim dlgOpen As FileDialog
    Dim bnExcel As Boolean
    
    On Error GoTo Err_Handle

The FileDialog object lets you pick a file for processing. It is placed within a Do … Loop to ensure that the user picks an Excel file. It could also be modified to ensure that they pick an Excel file with a table called Bookmarks, located on the List sheet.

    
    Set dlgOpen = Application.FileDialog( _
        FileDialogType:=msoFileDialogOpen)
    bnExcel = False
    Do Until bnExcel = True
        With dlgOpen
            .AllowMultiSelect = True
            .Show
            If .SelectedItems.Count > 0 Then
                sWbkName = .SelectedItems(1)
            Else
                MsgBox "Please select a workbook to use for processing"
            End If
        End With
        If InStr(1, sWbkName, ".xls") > 0 Then
            'proceed
            bnExcel = True
        Else
            MsgBox "The file must be a valid Excel file. Try again please..."
        End If
    Loop

The next step is to open or activate the workbook. This section checks to see whether the selected file is already open; if not, it is opened. The error handling routine at the end of the code launches Excel if it is not already running.

    
    Set objDoc = ActiveDocument
    
    'check to see that the Excel file is open. If not, open the file
    'also grab the wbk name to enable switching
    Set objExcel = GetObject(, "Excel.Application")
    
    For i = 1 To objExcel.Workbooks.Count
        If objExcel.Workbooks(i).Name = sWbkName Then
            Set objWbk = objExcel.Workbooks(i)
            Exit For
        End If
    Next

Now we minimize the Excel window and hide the application. There are two arrays to be populated as well. The first uses the Bookmarks table in the Excel file; the second contains the names of all bookmarks in the active Word document. We also set an object reference to the Excel workbook, to simplify the code later on.

    
    If objWbk Is Nothing Then
        Set objWbk = objExcel.Workbooks.Open(sWbkName)
    End If
    
    'minimize the Excel window
    objExcel.WindowState = -4140 'minimized
    
    'switch to Excel, find range name that corresponds to the bookmark
    objExcel.Visible = False
    objWbk.Activate
    vNames = objWbk.Worksheets("Lists").Range("Bookmarks").Value
    
    'loop through the bookmarks
    bmkCount = ActiveDocument.Bookmarks.Count
    ReDim vBookmarks(bmkCount - 1)
    j = LBound(vBookmarks)
    For Each bmk In ActiveDocument.Bookmarks
        vBookmarks(j) = bmk.Name
        j = j + 1
    Next bmk

This section loops through all the bookmarks in the array, retrieving the Excel tables and pasting them into the word document. A few points are worth commenting on:
There are some quirks to using pictures with bookmarks.

  • First, deleting a picture does not delete the bookmark, as it does with text. This meant that two extra steps were required. The bookmark needed to be specifically deleted, and then the code had to backtrack by one character to ensure that the newly re-created bookmark enclosed the picture instead of being placed after the insertion point.
  • Possibly as a consequence of the bookmarks remaining after the picture was deleted, it was not possible to step through the Bookmarks collection for the processing; the routine kept returning to the first bookmark. That is the reason for loading the bookmark names into an array, an processing the contents of the array instead.

Another major frustration is that recording the Paste Special code to insert the Excel table as an enhanced metafile, was ignored by Word, which defaulted to inserting the table as a formatted Word table. This is what we were trying to avoid because it changed the layout of the Word document. The workaround was to use the CopyPicture method in Excel, to guarantee that the data was inserted as a picture. 

    
    For j = LBound(vBookmarks) To UBound(vBookmarks)
        'go to the bookmark
        Selection.GoTo What:=wdGoToBookmark, Name:=vBookmarks(j)
        Set BMRange = ActiveDocument.Bookmarks(vBookmarks(j)).Range
            
        For k = 1 To UBound(vNames)
            If vNames(k, 1) = vBookmarks(j) Then
                sSheet = vNames(k, 2)
                sRange = vNames(k, 3)
                Exit For
            End If
        Next k
            
        'copy data from the range as a picture
        objWbk.Worksheets(sSheet).Range(sRange).CopyPicture 1, -4147
        
        'return to Word and paste
        objDoc.Activate
        BMRange.Select
        Selection.Delete
        'Note: only required if the bookmark encloses a picture.
        'If the bmk held text, deleting the selection removes the bmk too.
        'Under those circumstances the code throws an error.
        'Clunky workaround: tell Word to ignore the error
        On Error Resume Next
        ActiveDocument.Bookmarks(sBookmark).Delete
        On Error GoTo 0
    
        'paste the picture, then move back one character so the new bookmark
        'encloses the pasted picture
        Selection.PasteAndFormat (wdPasteDefault)
        Selection.Move Unit:=wdCharacter, Count:=-1
        
        'now reinstate the bookmark
        objDoc.Bookmarks.Add Name:=vBookmarks(j), Range:=Selection.Range
    
    Next j

The final part is some error handling and a cleanup section to remove the object references, and redisplay the Excel window.

    
Err_Exit:
    'clean up
    Set BMRange = Nothing
    Set objWbk = Nothing
    objExcel.Visible = True
    Set objExcel = Nothing
    Set objDoc = Nothing
    
    MsgBox "The document has been updated"
    
Err_Handle:
    If Err.Number = 429 Then 'excel not running; launch Excel
        Set objExcel = CreateObject("Excel.Application")
        Resume Next
    ElseIf Err.Number <> 0 Then
        MsgBox "Error " & Err.Number & ": " & Err.Description
        Resume Err_Exit
    End If
    
End Sub

This code has been broken up into sections to help with describing its function. If you want to copy it as a single block, it is provided on this page.

To run the code copy it into a new module in your Word document:

  • Alt+F11 to go to the VBA environment
  • Insert > Module, paste the code, then close the VBA environment (Alt+Q)
  • You can trigger the macro by pressing Alt+F8 and double-clicking the macro name. In a later article I will show how to run the code from a custom menu.