Combo Box Table of Contents
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.
Events
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
- PageAdded
- BeforePageDeleted
- PageChanged
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 '// http://www.visguy.com/ '// 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) Call m_updatePageList End Sub 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 Call m_updatePageList '// Reset m_deletedPageName to nothing: m_deletedPageName = vbNullString End Sub Private Sub Document_PageAdded(ByVal Page As IVPage) Call m_updatePageList End Sub Private Sub Document_PageChanged(ByVal Page As IVPage) '// Occurs when page-name or background is changed. Call m_updatePageList End Sub 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 Exit Sub Err: End Sub 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() cmbPages.Clear '// 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 Call cmbPages.AddItem(sPageName) End If Next cmbPages.Text = cmbPages.ContainingPage.Name m_editingList = False End Sub 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 Call collPgs.Add(pg.Name) End If Next '// Return the collection-list of page names: Set m_getPageList = collPgs End Function
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.





Very helpful
Looks really usefull for those large files. But I get this error when I go into run mode or try to click the combo box. Sorry if its a total newbie question!
- Runtime error ’424′:
Object required
And when I debug it goes to the line
cmbPages.Clear
in the Private Sub m_updatePageList() function
any help appreciated
Ok sorry, total newbie question after all. The Combo box needs to have the name ‘cmbPages’. Thanks Again
Chris in the forum there was a posting regarding reorganizing the pages order, is it possible to use
ActiveDocument.Pages.Item(2).Index = (3)
with the combolist to do that?
Thanks
Rod.
Hi Rod,
If you say, have a three page document, then try this:
visio.ActiveDocument.Pages.Item(3).Index = 2then the third page will move to the second location. The old page 2 will now be the third page in the document.
As far as the combobox goes, I’m not sure exactly what you want to do.
There *might* be a “sorted” property for the combo box that allows alphabetical sorting, and the .Add method might support a position index. These might be of use to you. But I haven’t looked at this in awhile.
Thanks! Great post!!