3 4
A Microsoft Visio Event object pairs an event with an action—either to run a Visio add-on or to notify an object in your program (also called a sink object or an event sink) that the event occurred. Your solution creates Event objects that describe the events you want to handle and tell Visio the action to take. When that event occurs, the Event object fires, triggering its action.
The first thing you must do is determine the type of Event object that your solution requires. What is your source object? What events do you need to receive? How will your solution respond to the events it does receive? Once you make these decisions, you create an Event object that runs an add-on or receives notifications.
Note
Before you create an Event object, you need to decide the following:
The scope determines the source object whose EventList collection the Event object is added to. Any object that has an EventList collection can be a source of events. For performance reasons, add the Event object to the EventList collection of the lowest object in the hierarchy that can fire the Event object in the scope you want.
To indicate the event you want to handle, supply its event code to the Add or AddAdvise method. Event codes are prefixed with visEvt in the Visio type library. In some cases, an event code is a combination of two or more codes. For example, the event code for the ShapeAdded event is visEvtAdd + visEvtShape. In some cases, you can combine codes to indicate an interest in multiple events with a single Event object. For example, the event code visEvtAdd + visEvtPage + visEvtShape indicates that you're interested in both ShapeAdded and PageAdded.
When an Event object fires, the Visio engine passes the event code for the event that actually occurred, even if the Event object's event code indicates multiple events. To continue the last example, if a page is added, the Visio engine passes the event code visEvtAdd + visEvtPage.
Note
Set evt = evtList.AddAdvise(visEvtAdd + visEvtShape, _ sinkObj, "", "")
The following factors cause this overflow condition:
Because visEvtAdd is a 4-byte value, which Visual Basic and VBA consider positive, visEvtAdd + visEvtShape is treated as 32768+64 = 32832, which is outside the range of legal values for a 2-byte signed quantity
If visEvtAdd is explicitly declared as a 2-byte quantity with the value &H8000, Visual Basic and VBA consider it to be negative, and visEvtAdd + visEvtShape is treated as "32768+64 = "32704, which is in the range of legal 2-byte signed quantities.
To declare visEvtAdd as a 2-byte value in your program, add the following statement:
Global Const visEvtAdd% = &H8000
The action determines which method you use to create the Event object, Add or AddAdvise. After you've decided what the source object should be, you can create the Event object by adding it to the EventList collection of the source object.
For details on the Add and AddAdvise methods, see the Microsoft Visio Developer Reference (on the Help menu, click Developer Reference).
Beginning with Microsoft Visio 2002, you can use event filters to refine the events that you receive in your program. You can filter events by object, cell, ranges of cells, or command ID. Event filtering allows you to tailor the events you listen to and prevents many of the events you don't want to hear from firing.
When an Event object created with the AddAdvise method is added to the EventList collection of a source object, the default behavior is that all occurrences of that event are passed to the event sink. The methods SetFilterObjects, SetFilterSRC, and SetFilterCommands provide a way to ignore (or pay attention to) selected events. Each method specifies an array and a True or False value indicating how to filter events for an object, a cell, ranges of cells, or for a command ID. Set the value to True to listen to events, or False to exclude events.
Note
For an event to successfully pass through an object filter, a cell range filter, or a command filter, it must satisfy the following criteria:
Methods available for implementing event filters
Method | Description |
SetFilterObjects | Provides a way to ignore or listen to selected events based on object type. One-dimensional array, 2 elements per object filter description (object type, True/False) Valid object types to filter on include: visTypePage, visTypeGroup, visTypeShape, visTypeForeignObject visTypeGuide, and visTypeDoc. |
SetFilterSRC | Provides a way to ignore or listen to selected events based on a range of one or more cells. One-dimensional array, 7 elements per cell filter description (begin section, begin row, begin cell, end section, end row, end cell, True/False) |
SetFilterCommands | Provides a way to ignore or listen to selected events based on a range of commands using command ID. One-dimensional array, 3 elements per command filter description (begin command, end comment, True/False) |
The following example shows how you might create an event sink, get and set objects, and filter events that are passed to the event sink:
Option Base 1 Option Explicit 'Event sink Dim vEventSink As adviseMe Dim vEvent As Visio.Event Public Sub SinkEvent() 'Get and set Event objects. Dim vEventList As Visio.EventList 'Get EventList collection for the document. Set vEventList = ActiveDocument.EventList 'Add an advise sink. 'adviseMe is a VB Class Module that implements 'IVisEventProc to process events. Set vEventSink = New adviseMe 'Add the cell changed event to the event list. Set vEvent = vEventList.AddAdvise(visEvtCell + visEvtMod, _ vEventSink, "", "") End Sub Public Sub SetFilter() 'Array for FilterObjects Dim arrFilterObjects(2) As Long 'Array for FilterSRC Const maxSRCs As Integer = 1 Dim arrFilterSRC(maxsrcs * 7) As Integer 'Array for FilterCommands Dim arrFilterCommands(3) As Long 'Set up an array of various objects to listen to. 'Set up the FilterObject array to listen to Shape objects only. arrFilterObjects(1) = visTypeShape arrFilterObjects(2) = True 'Set the filter for the event. vEvent.SetFilterObjects arrFilterObjects 'Set up the FilterSRC array for specific cells to _ 'pay attention to. 'Listen to all cells in the Transform section 'using visCellInval to say any cell. 'Start cell of first range arrFilterSRC(1) = visSectionObject arrFilterSRC(2) = visRowXFormOut arrFilterSRC(3) = visCellInval 'End cell of first range arrFilterSRC(4) = visSectionObject arrFilterSRC(5) = visRowXFormOut arrFilterSRC(6) = visCellInval 'Receive events for the described range. arrFilterSRC(7) = True 'Set the filter for the event. vEvent.SetFilterSRC arrFilterSRC 'Set up the FilterCommands array to block cell 'changed events caused by the Lay Out Shapes command. arrFilterCommands(1) = Visio.VisUICmds.visCmdToolsLayoutShapesDlg arrFilterCommands(2) = Visio.VisUICmds.visCmdToolsLayoutShapesDlg arrFilterCommands(3) = False 'Set the filter for the event. vEvent.SetFilterCommands arrFilterCommands End Sub
You can get the current status of an event filter using the methods GetFilterObjects, GetFilterSRC, GetFilterCommands.
For other details about using event filters, see the method topics prefixed with SetFilter and GetFilter in the Microsoft Visio Developer Reference (on the Help menu, click Developer Reference).
After you create an Event object, you can get information about it by querying its properties (as described in the Microsoft Visio Developer Reference). In addition, the EventInfo property of the Application object provides more information about certain events after they occur. For example, after the ShapesDeleted event fires, you can get the names of the deleted shapes from the EventInfo property.
Because there is only one EventInfo property for potentially many events, you must specify the event you want to handle when you get EventInfo. To do this, pass the event's sequence number (which Visio passes as the third argument when it calls VisEventProc on the corresponding sink object), or pass visEvtIDMostRecent for the most recent event. If there is no additional information for the event you specify, EventInfo returns an empty string.
For details about the information passed by a particular event, search for that event in the Microsoft Visio Developer Reference (on the Help menu, click Developer Reference).
An Event object that runs an add-on is created using the Add method of the EventList collection. To create an Event object that runs an add-on, you invoke the Add method with the following arguments:
When the Event object fires, Visio passes the argument string as command line arguments if the add-on is an exe file, or as the lpCmdLineArgs field of the VAOV2LSTRUCT structure passed to an add-on implemented by a Visio library ( vsl ). For example, the following code creates an Event object that runs an add-on called Showargs.exe and passes the string "/args=Shape added!" as a command line argument. The Event object is added to the EventList collection of the document.
Private Sub Form_Load() Dim eventsObj As Visio.EventList Dim docObj As Visio.Document 'Create a new drawing. 'A Visio instance has already been assigned to g_appVisio. Set docObj = g_appVisio.Documents.Add("") 'Get the EventList collection of this document. Set eventsObj = docObj.EventList 'Add an Event object that will run an add-on when the event _ 'fires. eventsObj.Add visEvtShape + visEvtAdd, visActCodeRunAddon, _ "SHOWARGS.EXE", "/args=Shape added!" End Sub
When a shape is added to any page in the document, the ShapeAdded event fires. The action the event triggers is to run the add-on Showargs.exe , which will identify the shape that was added along with the string "Shape added!".
An Event object that runs an add-on can be stored with a Visio document if the source object has a PersistsEvents property of True. This is sometimes called persisting an event. An Event object can be stored with a document if it meets the following conditions:
Whether a persistable Event object actually persists depends on the setting of its Persistent property. If the Event object is persistable, the Visio instance assumes that it should be stored with the document, so the initial value of its Persistent property is True. If you do not want the Visio instance to store the Event object, set its Persistent property to False.
Note
An Event object that sends a notification is created using the AddAdvise method of the EventList collection and can send a notification to a program that is already running. Creating this kind of Event object differs from creating one that simply runs an add-on in the following ways:
The following diagram shows how a program interacts with objects in Visio to receive event notifications.
Figure 21-3 The interaction between a client event sink and a Visio source object.
In this diagram, pSource is a reference to the source object in the Visio instance. This is used to get a reference to the source object's EventList collection, which is assigned to pEvtList.
The program uses pEvtList.AddAdvise to create the Event object, which is assigned to pEvt. With AddAdvise, the program passes a reference to the sink object that the Visio instance sends the notification to when the Event object fires.
A sink object is a non-Visio object you define to receive the notifications that the Visio instance sends. At a minimum, the sink object must be programmable (that is, it must support the Automation IDispatch interface) and must expose an event procedure named VisEventProc. You can give the sink object whatever additional functionality makes sense for your program. You can structure your solution in many ways. For example, you can create:
Typically, you would set the object's Public property to True, but that isn't required. If you want, you can code predefined methods such as Initialize and Terminate or add your own methods to the class.
Note
In your class module, write an event procedure called VisEventProc to handle notifications when they are received from Visio. Write code in the VisEventProc procedure in whatever way makes sense for your program. Visio does not require you to design your event handler in any particular way. You can choose to use any technique for branching within your procedure, and depending on the number and category of events your program will handle, you might define a different sink object for each event. When an Event object fires, the Visio instance calls the VisEventProc procedure for the corresponding sink object.
The VisEventProc procedure must be declared with the following parameters:
Public Sub VisEventProc( _ 'The event code of the event that caused the Event 'object to fire. eventCode As Integer, _ 'A reference to the source object whose EventList 'collection contains the Event object that fired. sourceObj as object, _ 'The unique identifier of the Event object within its 'EventList collection. Unlike the Index property, the 'identifier does not change as objects are added or 'removed from the collection. You can access the Event 'object from within the VisEventProc procedure by using 'source.EventList.ItemFromID(id). eventID As Long, _ 'The sequence of the event relative to events that have 'fired so far in the instance of Visio. seqNum As Long, _ 'A reference to the subject of the event, which is the 'object to which the event occurred. subjectObj As Object, _ 'Additional information, if any, that accompanies the 'notification. For most events, this argument will be 'Nothing. moreInfo As Variant) As Variant End Sub
Note
To modify existing event handlers so they can handle query events, change the Sub procedure to a Function procedure and return the appropriate value. For details about query events, see the AddAdvise method and event topics prefixed with Query in the Microsoft Visio Developer Reference (on the Help menu, click Developer Reference).
Now that your program has defined your sink object, you will need to create Event objects to include in the EventList collection of the source object whose events you want to receive.
You can use the same instance of the sink object for multiple Event objects, or you can use more than one instance of the sink object.
AddAdvise has two additional arguments. The third argument is reserved for future use and should be a null string ( "" ) . The fourth argument can be a string of arguments for the event handler. The Visio instance assigns these to the Event object's TargetArgs property. When your program receives a notification, VisEventProc can retrieve these arguments from the Event object that called it.
If you use the following Implements statement in your class module, you can click IVisEventProc in the Object box and then click VisEventProc in the Procedure box.
Implements Visio.IVisEventProc
For details about the Implements keyword, see your Visual Basic documentation.
The following VisEventProc procedure uses a Select Case block to check for three events: DocumentSaved, PageAdded, and ShapesDeleted. Other events fall under the default case (Case Else). Each Case block constructs a string (strDumpMsg) that contains the name and event code of the event that fired. Finally, the procedure displays the string in a message box.
Public Sub VisEventProc(eventCode As Integer, _ sourceObj As Object, eventID As Long, seqNum As Long, _ subjectObj As Object, moreInfo As Variant) As Variant Dim strDumpMsg As String 'Find out which event fired. Select Case eventCode Case visEvtCodeDocSave strDumpMsg = "Save(" & eventCode & ")" Case (visEvtPage + visEvtAdd) strDumpMsg = "Page Added(" & eventCode & ")" Case visEvtCodeShapeDelete strDumpMsg = "Shape Deleted(" & eventCode & ")" Case Else strDumpMsg = "Other(" & eventCode & ")" End Select 'Display the event name and code. frmEventDisplay.EventText.Text = strDumpMsg End Sub
For an example of a program that creates Event objects that might call this procedure, see Event Objects that Send Notifications: an Example later in this section.
Note
To modify existing event handlers so they can handle query events, change the Sub procedure to a Function procedure and return the appropriate value. For details about query events, see the AddAdvise method and event topics prefixed with Query in the Microsoft Visio Developer Reference (on the Help menu, click Developer Reference).
For example, let's say that when we created our sink object, we called the class module that we inserted into our project CEventSamp. The following code creates an instance of the sink object CEventSamp and creates Event objects to send notifications of the following events: DocumentSaved, PageAdded, and ShapesDeleted.
'Create an instance of the sink object class CEventSamp. Dim g_Sink As CEventSamp Dim docObj As Visio.Document Private Sub Form_Load() Dim eventsObj As Visio.EventList 'Create an instance of the CEventSamp class. 'g_Sink is global to the form. Set g_Sink = New CeventSamp 'Create a new drawing. 'A Visio instance has already been assigned to g_appVisio. Set docObj = g_appVisio.Documents.Add("") 'Get the EventList collection of this document. Set eventsObj = docObj.EventList 'Add Event objects that will send notifications. 'Add an Event object for the DocumentSaved event. eventsObj.AddAdvise visEvtCodeDocSave, g_Sink, "", "Document Saved..." 'Add an Event object for the ShapesDeleted event. eventsObj.AddAdvise visEvtCodeShapeDelete, g_Sink, "", "Shape Deleted..." 'Add an Event object for the PageAdded event eventsObj.AddAdvise (visEvtPage + visEvtAdd), g_Sink, "", "Page Added..." End Sub
When the PageAdded, ShapesDeleted, or DocumentSaved event fires, the Visio instance calls VisEventProc on the sink object g_sink. For an example of acorresponding VisEventProc procedure, see The VisEventProc Procedure: an Example earlier in this section.
Event objects created with the AddAdvise method persist until:
When the Visio instance terminates, it issues a BeforeQuit event, which the program should handle by releasing all its references to source objects or performing any other cleanup tasks. After the Visio instance issues BeforeQuit, it releases all its references to sink objects in the program.