Access: Table of Contents Report

By Helen Feddema

Access versions: 2007-2013
Level:  Intermediate

Introduction

Word has a sophisticated Table of Contents feature that automatically creates a table of contents from designated headings in a document.  Access reports don’t have such a feature, but I managed to create one using VBA code, custom database properties and some Word features.

The Orders Report

The sample database for this article, Table of Contents Report (AA 236).accdb, has tables from the old Northwind sample database, and a standard stepped Orders report:

This is a long report, so it would be handy to have a table of contents listing the page for each Customer ID.  Here is a high-level description of how I did it:

  1. Export the report to Word RTF format.
  2. Using VBA code working with the RTF document and a recordset of CustomerIDs for customers who have orders, search for each CustomerID and get the number of the page it is on, and save the CustomerID and page number to a table.
  3. Create a subreport (rsubTOC) whose record source is a query based on the table filled with current Customer ID and page number values.
  4. Make a copy of rptOrders (rptOrdersWithTOC), and insert the subreport into the report header.
  5. Make a complex expression to display Roman numerals for the TOC page numbers, and Arabic numerals (starting with 1) for the regular page numbers.

Here is the first page of the report with a Table of Contents:

And here is the first page of the body of the report:

The procedure listed below does the work of saving the page numbers to the table used as the subreport’s record source:

Public Function GetReportPages()
 On Error GoTo ErrorHandler
    'Close existing RTF file if necessary
    strCurrentPath = Application.CurrentProject.Path & "\"
    strWordRTFFile = strCurrentPath & "Orders.rtf"
    Debug.Print "Word RTF file: " & strWordRTFFile

 On Error Resume Next
    Set doc = appWord.Documents(strWordRTFFile)

    If Not doc Is Nothing Then
       doc.Close savechanges:=wdDoNotSaveChanges
    End If

 On Error GoTo ErrorHandler

    'Export Orders report to Word RTF file
    strReport = "rptOrders"
    DoCmd.OutputTo objecttype:=acOutputReport, _
       objectname:=strReport, _
       outputformat:=acFormatRTF, _
       outputfile:=strWordRTFFile, _
       autostart:=False

    Set doc = appWord.Documents.Open(strWordRTFFile)
    doc.Select

    appWord.Selection.WholeStory

    'Clear old TOC records
    strTOCTable = "tblTOCPageNos"
    strSQL = "DELETE * FROM " & strTOCTable
    DoCmd.SetWarnings False
    DoCmd.RunSQL strSQL

    'Set up recordsets of customer codes to search for and table
    'for storing Customer IDs and page numbers
    strQuery = "qryCustomerIDs"
    Set rstSource = CurrentDb.OpenRecordset(strQuery)
    Set rstTOC = CurrentDb.OpenRecordset(strTOCTable)

    appWord.Visible = True

    'Turn off spelling and grammar checking
    With appWord.Options
       .CheckGrammarAsYouType = False
       .CheckGrammarWithSpelling = False
       .ContextualSpeller = False
       .CheckSpellingAsYouType = False
    End With

    Do While Not rstSource.EOF
       strCustomerID = rstSource![CustomerID]
       With rstTOC
          .AddNew
          ![CustomerID] = strCustomerID

          'Find Customer ID in Word document
          appWord.Selection.Find.ClearFormatting

          With appWord.Selection.Find
             .Text = strCustomerID
             .Replacement.Text = ""
             .Forward = True
             .Wrap = wdFindStop
             .Format = True
             .MatchCase = False
             .MatchWholeWord = True
             .MatchWildcards = False
             .MatchSoundsLike = False
             .MatchAllWordForms = False
          End With

          appWord.Selection.Find.Execute
          Set sel = appWord.Selection

         'Retrieve page number from Word document
          intPageNumber = sel.Information(wdActiveEndPageNumber)
          Debug.Print "Customer ID " & strCustomerID _
             & " page no.:  " & CStr(intPageNumber)
          ![PageNumber] = intPageNumber
          .Update
       End With

       appWord.Selection.WholeStory
       rstSource.MoveNext
    Loop

See download code below for entire code including error handler.

The expression that creates the page numbers references several invisible textboxes in the report footer (the yellow ones – that is my convention for invisible controls):

The page number expression is:

 =IIf(([Page]>[txtTOCPages].[Value])=True,("Page " & [txtCorrectedPageNo].[Value] & " of " & [txtCorrectedNumPages].[Value]),"Page " & RomanNo([txtPage].[Value]))

As is often the case on Access reports, I sometimes found it necessary to reference the value of a textbox, rather than the actual value that is the control source of the textbox.  Here are the control sources of the txtCorrectedPageNo and txtCorrectedNumPages textboxes referenced in the above expression:

 =Val([txtPage].[Value])-Val([txtTOCPages].[Value])
 =Val([txtPages].[Value])-Val([txtTOCPages].[Value])

txtTOCPages has this control source:

 =GetProperty("TOCPages","")

This custom database property is saved from this procedure, which is run from the report’s Open event, which also puts up a message box asking if you want to recreate the Table of Contents, and runs the GetReportPages procedure if you click Yes.

 Public Function GetTOCPages() As Integer

 On Error GoTo ErrorHandler

    'Get number of TOC pages and save to a custom database property
    strReport = "rsubTOC"
    DoCmd.OpenReport reportname:=strReport, _
       View:=acViewPreview
    Set rpt = Reports(strReport)
    intTOCPages = CInt(rpt![txtNumPages].Value)
    Debug.Print "TOC Pages: " & intTOCPages
    DoCmd.Close objecttype:=acReport, _
       objectname:=strReport

    strPropertyName = "TOCPages"
    lngDataType = dbInteger
    Call SetProperty(strPropertyName, lngDataType, intTOCPages)


 ErrorHandlerExit:
    Exit Function

 ErrorHandler:
    MsgBox "Error No: " & Err.Number _
       & " in GetTOCPages procedure; " _
       & "Description: " & Err.Description
    Resume ErrorHandlerExit
 End Function

References

The code in the sample database needs the following references (in addition to the default references):

Microsoft Word 14.0 Object Library

Microsoft Forms 2.0 Object Library

Microsoft Scripting Runtime

If you import code or objects into a database of your own, you may need to set one or more of these references.  The version number may differ, depending on your Office version; check the version you have.  References are set in the References dialog, opened from the VBA window.  For more information on working with references, see Access Archon #107, Working with References.

Supporting Files

The zip file containing this article, in Word 97-2003 format, plus the supporting file(s), may be downloaded from the Access Archon page of my Web site, as accarch236.zip, which is the last entry in the table of Access Archon columns for Access Watch.

Document Name: Table of Contents Report (AA 236).accdb

Document Type:  Access 2007-2010 database (can also be used in higher versions of Access)

Place in: Wherever you want

Want More?

Office Watch has the latest news and tips about Microsoft Office.  Delivered once a week.