Lesson 2: Event Handling
Lesson 2: Event Handling
This content is no longer actively maintained. It is provided as is, for anyone who may still be using these technologies, with no warranties or claims of accuracy with regard to the most recent product version or service release.
Download The Lesson 2: Event Handling sample files are available from the Microsoft Download Center.
Topics in this lesson
Scenario
About Event Handling in Visio
Implementation
Handling Events with Connection Points
Using the Visio Event Object
Summary: Connection Points and the Visio Event Object
Scenario
In this lesson we will add functionality to the Component Object Model (COM) add-in that we wrote in Lesson 1: Visio COM Add-in Creation in Visual Basic .NET. This functionality enables the COM add-in to respond to a user creating a new Microsoft® Visio® document.
In this lesson, we will show you how to:
- Enable Visio to send a notification to the COM add-in that a new document has been created.
- Modify the Connect class to listen for the DocumentCreated event.
The mechanism for listening for an application action and calling code in response to the action is called event handling. The receiver (the COM add-in) responds to a message from the sender (Visio). The act of receiving and using the information in this message is called handling the event.
About Event Handling in Visio
We will also explain two models of event handling in Visio, connection points and the Visio Event object, and how each works within a Windows .NET Framework application context.
The event handling for the Managed Code Interop Tutorial is based on connection points. This is the implementation located in the Lesson2 folder in the provided sample application installation that accompanies this tutorial. We will first discuss the sample application�s implementation of connection points.
Using the Visio Event object is a slightly different method of receiving events from Visio. After showing the sample application�s implementation of connection points, we will demonstrate how the same functionality could have been implemented using the Visio Event object. The code examples in the "Using the Visio Event Object" section of this lesson provide the most important details. A full implementation of a COM add-in using Visio Event objects is not provided in the sample application installation.
By comparing the two implementations, you can decide which method suits your needs. For more information about using connection points for event handling, see the topic Writing Code Behind Events in Developing Microsoft Visio Solutions in the Microsoft Visio 2002 SDK. The Visio Event object is discussed in detail in the topic MSHelp:link keywords="DVS_21_Event_Handling_858.htm" TABINDEX="0">Visio Event Objects, also in Developing Microsoft Visio Solutions.
Implementation
To see the implementation of the sample application for this lesson, open the solution using Microsoft® Visual Studio® .NET. The solution is found in the Lesson2 folder and is named Lesson2.sln. For this lesson, we will focus on the provided sample code, rather than developing the application step-by-step as in Lesson 1.
Handling Events with Connection Points
As we described in Lesson 1, our Visual Basic .NET implementation of the OnConnection method has the following signature:
Public Sub OnConnection( _
ByVal applicationObject As Object, _
ByVal connectMode As Extensibility.ext_ConnectMode, _
ByVal addInInstance As Object, _
ByRef custom As System.Array) _
Implements Extensibility.IDTExtensibility2.OnConnection
The implementation of OnConnection makes use of the Application object that it receives by displaying a dialog box showing its fully qualified type name. The Connect class in Lesson 1 has the following private member:
Private visioApplication As Object
The first step to the previous implementation of OnConnection was to set this member to the reference received from Visio:
visioApplication = CType(applicationObject, _
Application)
By using reflection, we discovered that this cast provides us with an instance of the Visio Application object. In this lesson we use the Visio Application object to listen for Visio events. Once the COM add-in is notified of a newly created document event, it makes a call to the Visio Automation interface so that our COM add-in can draw shapes in the new Visio document.
Receiving Application object events
This code itself isn�t sufficient, however, to begin receiving Application object events. To register as a listener, you must declare a class-level variable of the Application object type within the Connect class as follows:
Private WithEvents visioApplication As Application
There are several things to note about this declaration:
- We must be specific about type (no As Object declarations).
- It does not use New because we don�t want to manufacture the Visio Application object within the COM add-in; rather, we will need a reference back to the originator of the DocumentCreated event.
- It uses the WithEvents modifier to enable the Connect class to receive events from Visio.
Now that visioApplication is declared to receive events, you can see all its events in the procedure drop-down list box in Visual Studio .NET, as shown in the following figure.
Figure 1. Using Visual Studio .NET to automatically generate an event handler (click to enlarge)
Clicking the DocumentCreated event in the list box generates the predefined signature for the event handler:
Private Sub visioApplication_DocumentCreated( _
ByVal document As Document) _
Handles visioApplication.DocumentCreated
The Handles keyword specifies which event is handled. Notice that we receive a reference to the newly created Visio Document object. We pass this reference to a new object called DocumentCreator. The DocumentCreator class implements the drawing code. The following code shows the remainder of the DocumentCreated event handler implementation:
Dim documentCreator As DocumentCreator
If IsManagedTutorialTemplate(document) Then
Try
documentCreator = New DocumentCreator()
documentCreator.CreateDrawing(document)
Catch err As Exception
MsgBox("Exception in DocumentCreated: " & _
err.Message, , ERROR_TITLE)
End Try
End If
Checking the document template
The first task our code performs is to call a helper function named IsManagedTutorialTemplate, which has been added to the Connect class for Lesson 2. This function determines if the document was created using the Managed Tutorial.vst template, and if so, returns True. The function looks for a user-defined cell defined in the template�s document ShapeSheet®. If we didn�t perform this check, our drawing creation code would be invoked for every drawing we create in Visio.
If the document was created using the right template, the event handler creates a new instance of the DocumentCreator class. The DocumentCreator class contains a single public method, CreateDrawing, which uses the Document object received when the event is handled. For now we�ll just state that this class contains the drawing-creation code; we will examine this code in Lesson 3. CreateDrawing draws a Process shape in the newly created document.
Using the Visio Event Object
The other event handling mechanism provided by Visio uses the Event object. Conceptually, handling events using the Event object is similar to using connection points. However, the implementation is quite different.
We can see the differences by examining the following sample code. To use the Event object, we define the following members for the Connect class:
Private visioApplication As Application
Private resultEvent As Event
Private eventSink As IVisEventProc
The declaration of visioApplication is the same as in the sample application from Lesson 1. The resultEvent member is the Visio Event object. The resultEvent member allows our code to specify which events to listen for. The eventSink member will be assigned to an instance of the EventSink class. As you can see in the preceding declaration, this class implements the IVisEventProc interface, which provides Visio with a means of calling into the COM add-in when an event occurs.
Next, we need to tell Visio what events to listen for and where to send them. Following is the implementation of the Connect class�s OnConnection method for this application:
Dim eventList As EventList
Try
visioApplication = CType(applicationObject, _
Application)
eventSink = New EventSink()
' Listen to the MarkerEvent event.
eventList = visioApplication.EventList
resultEvent = eventList.AddAdvise(VisEventCodes.visEvtApp _
+ VisEventCodes.visEvtMarker, eventSink, "", "")
End If
Catch err As Exception
MsgBox("Exception in OnConnection: " & _
err.Message, , ERROR_TITLE)
End Try
The preceding code adds an Event object to the EventList collection of the Visio Application object.
To indicate the event you want to handle, supply its event code as the first parameter to the AddAdvise method. The preceding code specifies two event codes: visEvtApp indicates that we are listening for Application object scope events, and visEvtMarker indicates that we are listening for Application marker events.
The AddAdvise method also indicates how the object will receive the events. In this example, our code creates an instance of an EventSink object. Now we can write an event handling procedure to respond to the event. All Event object events are handled by the same procedure, the VisEventProc method of the IVisEventProc interface. Our EventSink class implements the IVisEventProc interface. The signature of the VisEventProc event handler procedure is shown in the following code:
Private Function IVisEventProc_VisEventProc( _
eventCode As Short, _
source As Object, _
eventID As Integer, _
eventSeqNum As Integer, _
subject As Object, _
moreInfo As Object) As Object _
Implements IVisEventProc.VisEventProc
We can check the event code that is passed into our event handler using the following code:
' Only respond to MarkerEvent events.
If (eventCode = (VisEventCodes.visEvtApp _
+ VisEventCodes.visEvtMarker)) Then
visioApplication = CType(applicationObject, _
Application)
' Check if this MarkerEvent event was raised by a document
' created with the sample template.
context = visioApplication.EventInfo( _
VisEventCodes.visEvtIdMostRecent)
If (Not(context Is Nothing) AND ParseEventContext( _
context, commandID, documentIndex, _
pageIndex, shapeNameID)) Then
' Determine which command was called and handle it.
Select Case commandID
Case DOCUMENT_CREATE_CMDID
HandleDocumentCreate( _
visioApplication, documentIndex)
Case DOCUMENT_OPEN_CMDID
HandleDocumentOpen( _
visioApplication, documentIndex)
Case SHAPERMA_CONNECTS_CMDID
HandleRightMouseClick( _
visioApplication, documentIndex, _
pageIndex, shapeNameID)
End Select
End If
End If
By examining the EventInfo property of the Application object and looking for the last event raised (specified by visEvtIdMostRecent), we can determine what event was received.
We are specifically looking for MarkerEvent events. The MarkerEvent event is raised by the document�s event handler, which handles the DocumentCreated event. When a new document is created, the event handler queues up a new MarkerEvent event as follows:
Application.QueueMarkerEvent _
"SDKFLW /cmd=" & DOC_OPEN_CMDID _
& " /Doc=" & doc.Index
The message passed in the MarkerEvent event string indicates the index of the newly created document. The event handler in the COM add-in parses this string (ParseEventContext, not shown). If the event handler receives a string of the preceding format, it calls HandleDocumentCreate (not shown). This triggers the code to draw the flowchart.
Summary: Connection Points and the Visio Event Object
Both methods for coding event handling in Visio, connection points and the Visio Event object, follow a publish/subscribe model. Visio is the publisher and the COM add-in is the subscriber. The implementations of these methods differ in how a subscription is registered.
The connection point implementation using WithEvents is simpler to understand and has support in the Visual Studio .NET IDE for ease of programming. However, using the Visio Event object does have the advantage of being able to specify which events are received. Specifying WithEvents causes the Visio object to receive all events for that type of object. Raising events results in cross-process calls, which are further marshaled and unmarshaled by COM Interop. For objects that receive a high frequency of events, this can slow the response of the COM add-in.
Casting the received Application object provided us with a way to handle Visio events. We also now have a way to manipulate Visio objects within our COM add-in. We�ll look at this in more detail in Lesson 3, Data Input.