Yet another newsgroup post caught my eye today. I’m not sure if I answered the guy’s actual question, but I had fun creating this example anyway.
Today’s sample shows you how to create a table of contents. This particular version lists the pages in a Visio document in a drop-down combo box that sits on a Visio page. Just select an item from the list, and Voila! You are sent to the corresponding page in the document!
Anatomy of a Table of Contents Solution
The code for making this all happen is fairly straight-forward and is listed at the end of this post. I thought I’d point out a few interesting items before leaving you to slosh through it all.
Controls on Visio Pages (!?!?)
Yes, you can add controls to Visio pages! They end up behaving like shapes and like controls. It’s very odd, neat, and flexible!
To add a combo box control to a page, follow these steps:
- You need to activate the Developer tool bar by right-clicking on a blank area of Visio’s toolbar strip.
- Click the Insert Control button on this toolbar
- Choose Microsoft Forms 2.0 Combo Box from the list in the dialog box
- You should see your combo box, sitting in the middle of the page.
You can edit it’s properties and code by right-clicking the control and choosing Combo Box Object > Properties or Combo Box Object > View Code.
You can stop and restart the VBA code by clicking on the Design Mode button in the Developer toolbar. This is the button with the little blue triangle. This stops and restarts the VBA project.
The code to get a list of all pages is fairly easy. The interesting things are the Visio events need to be tracked. In VBA, we have a built-in document object: ThisDocument, which has a whole set of eventsÂ pre-wired for our use.Â In the code below, you’ll see that I’ve called for an update of the combo box’s list items for these events:
RunModeEntered does double-duty. It fires when you click that Design Mode button on and off, but also when the Visio document loads. This saves us from having to wire the DocumentOpened and DocumentCreated events, and also allows us to test without closing the document and re-opening it.
When a page gets deleted, we see the BeforePageDeleted event. But a simple updating of all the pages in the document at this time would be the wrong thing to do, since that page isn’t gone yet. (it’s a Before event!) So the code in this event stores the name of the about-to-be-deleted page in a module-level variable called m_deletedPageName. The update-combo-box code then simply avoids adding that page’s name to the list.
The Code (Finally!)
Here’s the VBA code that you’ll find in the document:
‘// Combo Box Table of Contents
‘// Visio Guy
‘// November 8th, 2006
‘// A variable for tracking pages that are
‘// about to be deleted.
Private m_deletedPageName As String
‘// This flag stops the code from jumping back
‘// to the index page every time the combo box
‘// is updated.
Private m_editingList As Boolean
Private Sub Document_RunModeEntered(ByVal doc As IVDocument)
‘// This event fires when you click the
‘// run-mode/design-mode button on the
‘// Developer Toolbar, as well as when
‘// this document gets opened or created
‘// (created = a copy is opened)
Private Sub Document_BeforePageDelete(ByVal Page As IVPage)
‘// Since this event fires before the page
‘// is actually gone, we’ll save it’s name
‘// so that m_updatePageList can ignore it.
m_deletedPageName = Page.Name
‘// Reset m_deletedPageName to nothing:
m_deletedPageName = vbNullString
Private Sub Document_PageAdded(ByVal Page As IVPage)
Private Sub Document_PageChanged(ByVal Page As IVPage)
‘// Occurs when page-name or background is changed.
Private Sub cmbPages_Change()
If m_editingList Then Exit Sub
‘// Switch the active window’s page
‘// to correspond to the user’s choice
‘// in the combo-box. Do nothing if there’s
‘// an error (i.e. the user types in a bad
‘// page name)
On Error GoTo Err
ActiveWindow.Page = cmbPages.Text
Private Sub m_updatePageList()
m_editingList = True
‘// Update the combo-box with a list of
‘// all foreground pages in this document.
Dim collPgs As Collection
Dim sPageName As Variant
Set collPgs = m_getPageList()
‘// Add all pages in collPgs, except a page
‘// that might have just been deleted. Such
‘// a page is stored in m_deletedPageNameName.
For Each sPageName In collPgs
If (StrComp(sPageName, m_deletedPageName, _
vbTextCompare) <> 0) Then
cmbPages.Text = cmbPages.ContainingPage.Name
m_editingList = False
Private Function m_getPageList() As Collection
‘// Return a collection of all foreground
‘// page names in this document.
Dim collPgs As New Collection
Dim pg As Visio.Page
For Each pg In ThisDocument.Pages
‘// Add non-background pages to the list:
If Not (pg.Background) Then
‘// Return the collection-list of page names:
Set m_getPageList = collPgs
More on Table of Contents in Visio
Dan Brown’s greenonions
A nice code sample for creating a table of contents in a Visio document.
John Marshall’s VBA Information
This page has another code sample. Just search for Table Of.