If you’ve dabbled with the VBA project that resides in Visio documents, then you’ve likely stumbled upon the Document object’s DocumentOpened event. This is a great place for running any initialization code that you might require, but there’s a better way…
When you search the events that are fired by the built-in Document object, there are two obvious choices for initialization code that jump out at you: DocumentCreated and DocumenOpened. DocumentCreated fires when a copy of a document is opened (which is what happens by default when you open a Visio template.) DocumentOpened fires, of course, when you open a Visio document.
A better choice, however is RunModeEntered, as we’ll soon see.
The Obvious Startup Events
You can paste the following two subroutines into a Visio document’s VBA project, save the document, then test by opening a copy of the document, or by opening the original version:
Private Sub Document_DocumentCreated(ByVal doc As IVDocument) '// This procedure runs when a Visio document is '// created. '// Ie: when a copy of a document is opened, or a '// template (.vst) is opened. In fact, a .vst file '// is simply anormal Visio document that happens '// to open a copy as a copy by default. That's the '// essential difference between a .vst and a .vsd. Call MsgBox("Document_DocumentCreated") End Sub Private Sub Document_DocumentOpened(ByVal doc As IVDocument) '// This procedure runs when a Visio document is '// opened. Call MsgBox("Document_DocumentOpened") End Sub
Enter RunModeEntered
The trouble with the two events we’ve discussed becomes evident when you start to (frequently) test your code. You have to keep closing and opening the documents to test the code! Since the initialization code for open-original vs. open-copy is often identical, it makes sense to use the less-well-known event: RunModeEntered.
Document_RunModeEntered is also an event that hangs off of the Document object, just like the other two events we just talked about. The great thing about RunModeEntered is that it fires when you toggle the VBA Design Mode button. The button shows a little blue triangle with pencil and ruler. The button is a toggle button, and it looks like this:
Design Mode On
Design Mode Off
When design mode is off, VBA code won’t execute, so you can “live in peace” and perform certain operations without having to worry about code executing while you are adjusting controls on Windows forms, or Visio shapes and masters.
You’ll find this button in two places:
- Visio’s Developer toolbar
- VBA’s Standard toolbar
When you toggle the Design Mode button back to “on”, then the Document_RunModeEntered event fires, and any code in this Sub will execute! No more closing and opening your document just to test! You can paste the Sub below into your VBA project and give it a go.
Private Sub Document_RunModeEntered(ByVal doc As IVDocument) '// This procedure runs when a Visio document is '// created or opened. It also runs when the VBA '// "Design Mode"/ "Exit Design Mode" button is '// pressed. '// '// Since this procedure runs when a document is '// opened or created, it serves as a more convenient '// place for calling your initialization code, '// since you can simply click the little blue '// triangle from within the VBA editor, or from '// Visio's "Developer" toolbar. Call MsgBox("Document_RunModeEntered") End Sub
RunModeEntered is also convenient for resetting a diagram that “has gone wrong.” Of course, we’d all like to write perfect code, but unfortunately, problems do arise. It may be acceptable to tell your users to “Show the Developer toolbar and toggle the Design Mode button off, then on” to reset the diagram. Not an optimal solution, but perhaps a quicker fix than taking users into the VBA project or other icky possibilities.
RunModeEntered isn’t the only solution. You could just write an initialization subroutine, say “m_init”, and call that from DocumentOpened or DocumentCreated. You can put your cursor inside of this subroutine and press F5 to run the code without having to close and re-open the document. But here agian, RunModeEntered can be more convenient, because you can restart the code from the Visio drawing interface. No Alt+Tab-bing back to VBA.
TODO
Information about persistent events that are stored with the document might top-off this article rather nicely.
Persistent Events are events that are stored in a Visio document, but not as VBA code. They can be used to start add-ons or add-ins on document open/create. There is no easy user-interface for editing these events, so they can only be accessed via code.
Examples include the Org Chart Wizard template, and the Cross-functional Flowchart template, both of which present the users with custom-code dialog boxes on open.
See:
- Document Object > EventList property in the Visio Developer’s Reference help file
- Microsoft Office Visio Persistent Events Tool that is included with the Visio 2007 SDK
Mark Nelson (MS) says
Good tip, Chris!
bernardes says
Try this too:
Public Sub ShowNames()
‘Declare object variables as Visio object types.
Dim vsoPage As Visio.Page
Dim vsoDocument As Visio.Document
Dim vsoDocuments As Visio.Documents
Dim vsoPages As Visio.Pages
‘Iterate through all open documents.
Set vsoDocuments = Application.Documents
For Each vsoDocument In vsoDocuments
‘Print the drawing name in the Visual Basic Editor
‘Immediate window.
Debug.Print vsoDocument.FullName
‘Iterate through all pages in a drawing.
Set vsoPages = vsoDocument.Pages
For Each vsoPage In vsoPages
‘Print the page name in the Visual Basic Editor
‘Immediate window.
Debug.Print Tab(5); vsoPage.Name
Next
Next
End Sub
André Luiz Bernardes
https://sites.google.com/site/vbabernardes/blogs
A&A® – Work smart, not hard.
Blog Office VBA | Blog Excel | Blog Access
Visio Guy says
Well, um, for those reading the comments, Bernardes’ code prints the name of every document that is open in Visio, along with the name of every page in every document.
Perhaps he didn’t quite understand what this article was about, but thanks anyway.
Say “Hi” to Walter, Bernardes!
Dave WIlson says
How do I make it run? Put the code in and nothing happens.
Thanks.
Dave
Dave WIlson says
Ok, I got it running.
Visio Guy says
@Dave,
Phew!
Dev says
Hey,
Is there a way to have a macro implemented within Visio without creating a COM Add-In?
I have a lot of VBA code, including some Forms, which I don’t want to have to re-create in VS.
So yeah, wondering if its possible to have a macro load on startup of Visio?
Visio Guy says
Hi Dev,
One thing you can do is move your macros to a “code stencil” – just a stencil that is used for holding VBA.
This keeps the code from being copied with every document that is created. Of course, documents need to have the stencil open in order to benefit from the code. You’ll also have to re-think reference to ThisDocument, which will now be the stencil, and probably not what you want. (Use ActiveDocument in most cases, instead.)
You can also specify a Startup path. Go to: File > Options > Advanced (scroll way down to the bottom) > General > File Locations > Startup.
I put a stencil and a drawing in their, but they didn’t open on startup. I put a shortcut to Notepad2.exe, and nothing happened. But when I coped Notepad2.exe to the startup directory, it DID open when Visio started. You might be able to put a VB script file in the startup directory that would open a library of helpful VBA functions (contained in a stencil),
Dev says
Thanks Visio Guy! That is exactly what I did, put the macro within a stencil. So useful, so portable now.
ChrisB says
Hi Chris,
Thanks again for the help you’ve already given (Master Shapes, Sub-selecting shapes, etc., etc.).
The final (possibly) part of my challenge is to programatically change the default double-click behaviour of a shape dropped onto my ‘Process’ page – logically the following code should work (and has been tested in step mode to prove it works:
Private Sub Document_ShapeAdded(ByVal Shape As IVShape)
Dim mastObj As Master
Dim VApp As Application
Dim pgObj As Page
Set VApp = Visio.Application
'Get the Master property of the shape.
Set mastObj = Shape.Master
'Check whether the shape has a master. If not, the shape was created locally.
If Not (mastObj Is Nothing) Then
'Check whether the master is "Square".
If Left(mastObj.Name, 12) = "Script Block" Or Left(mastObj.Name, 5) = "Table" Then
Set pgObj = VApp.ActiveDocument.Pages(1)
pgObj.Shapes(mastObj.Name).CellsSRC(visSectionObject, visRowEvent, _
visEvtCellDblClick).FormulaU = "RUNMACRO(""Tech_Design.Module2.Process_Shape"")"
Debug.Print "Setting Double-Click to 'Run Macro' for '" & mastObj.Name & "'"
End If
End If
End Sub
When I drop a table shape on the form, it does appear to run (uninterrupted mode) as my debug log shows:
Checking Debug Mode...
Debug Mode = True
Setting Double-Click to 'Run Macro' for 'Table'
The same is true for the Script Blocks – however, when I then look at the default behaviour of the new object, it is not set to ‘Run Macro’ but ‘Edit Shapes Text’.
I know it’s probably obvious, but what am I missing here?
Thanks again,
Chris
make says
Hi all,
i have found this thread, and i want to ask you for help.
I am trying to use command buttons in visio to hide different layers. But i want to use them also to hide some of other command buttons or textfields buttons. The thing is, if i add the specific command button to the hidden layer sometimes it doesnt hide. when i enter/exit design mode, it hides corectly.(like a refresh or what)
so i wanted to do a macro, which will just enter/exit design mode to refresh the diagram.
I thought i can use the “RunModeEntered” function, but it doesnt trigger when i click on the design button to enter design mode, it triggers only to leave design mode.
So i dont know how to use this function correctly.
Please help.