Lesson 2: Event Handling [Visio 2003 SDK Documentation]

The Microsoft Visual Basic® .NET project files that accompany this lesson can be found at \Samples\Tutorial\VBNet\Lesson2.

Table of contents

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 who is creating a new Microsoft® Visio® document.

In this lesson, we will show you how to:

  • Send a notification to the COM add-in when a new document has been created.
  • Modify the Connect class to listen for the DocumentCreated event.

The mechanism for monitoring 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 explain two models of event handling in Visio, connection points and the Visio Event object, and explain how each works within a .NET Framework application context.

The event handling for this tutorial is based on connection points. This is the implementation located in the Lesson2 folder in the sample application installation that accompanies this tutorial. We will first discuss the sample application's implementation of connection points.

After showing the sample applications 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 best suits your needs.

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 sample code that is provided, rather than developing the application is steps, as we did 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 application 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 Visio Application object that it receives by displaying a dialog box showing application information. The Connect class in Lesson 1 has the following private member:

Private visioApplication _
    As Microsoft.Office.Interop.Visio.Application

The first step in the previous implementation of OnConnection was to set this member to the reference received from Visio:

visioApplication = CType(application, _
    Microsoft.Office.Interop.Visio.Application)

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 drop shapes into the new Visio document.

Receiving Application Object Events

However, this code is not yet capable of 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 Microsoft.office.Interop.Visio.Application

There are several things to note about this declaration:

It does not use New because we do not want to manufacture the Visio Application object within the COM add-in; 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 Figure 1.

Events listed in the procedure drop-down list in Visual Studio .NET.

Figure 1. Using Visual Studio .NET to automatically generate an event handler

Clicking the DocumentCreated event in the list box generates the predefined signature for the event handler:

Private Sub visioApplication_DocumentCreated( _
        ByVal currentDocument 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 flowchartCreator As DocumentCreator
     If IsManagedTutorialTemplate(currentDocument) Then
         Try
             flowchartCreator = New DocumentCreator
             flowchartCreator.CreateDrawing(currentDocument)

         Catch err As COMException
             MsgBox("Exception in DocumentCreated: " & _
                 err.Message, , 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 this is the case, returns True. The function looks for a cell that the user has defined in the template's document ShapeSheet®. If we did not perform this check, our drawing creation code would be invoked for every drawing we create in Visio.

Note  You can find the Managed Tutorial.vst file in the Lesson2 folder. Because the Managed Tutorial.vst does not contain any code, you must create a document using this template to see the COM add-in in action.

To create a document from the template
  1. Start Visio. On the File menu, click Choose Drawing Type.
  2. In the New Drawing pane under Template, click On my computer....
  3. Browse to the Managed Tutorial.vst file in the Lesson2 folder.
  4. Click Open. A blank document opens based on the Managed Tutorial.vst file. You should not choose Open from the Visio File menu and select the template name or double-click the file in Windows Explorer; this will open the template itself, and cause the DocumentOpened event to be raised rather than the DocumentCreated event. It will not create the document based on the template that you need.

If the document was created using the correct 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 will just state that this class contains the drawing-creation code. We will examine this code further in Lesson 3. CreateDrawing draws a Process shape in the newly created document.

Visio Version Number Check

From the Lesson 2 files, the DocumentCreated event handler contains the following code to check the version number:

' Make sure that we're running against the correct version of Visio.
     visVersion = GetVisioMajorVersion(visioApplication)

If (visVersion = 0 Or visVersion < MIN_VISIO_VERSION) Then
      MsgBox(ERROR_VISIO_VERSION, , TITLE)

All code samples in this SDK require Visio 2003 (which is version number 11) or higher. Each code sample has a version number check. GetVisioMajorVersion is a helper function defined in Shared.vb.

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 Microsoft.Office.Interop.Visio.Application 
Private resultEvent _
  As Microsoft.Office.Interop.Visio.Event
Private eventSink As IVisEventProc

The declaration of visioApplication is the same as the sample application from Lesson 1. The resultEvent member is the Visio Event object, and must be fully qualified, because Event is a keyword in Visual Basic. 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. The EventSink class will implement the IVisEventProc interface. This interface is defined by Visio, but it must be implemented by clients that monitor for events using Visio Event object. The client implementation of IVEventProc provides Visio with a means of calling into the COM add-in when an event occurs.

Creating an EventSink Class

We need to add a file named EventSink to your project.

To add a new file for the EventSink class
  1. Right-click the tutorialAddin project, click Add, and then click Add Item.
  2. In the Add New Item dialog box, select Class and enter EventSink.vb as the file name. This adds a file named EventSink to your project. Visual Basic adds a simple class definition to the file.
  3. At the top of the file add an import statement for the Visio Primary Interop Assembly and the .Net Framework Interop Services assembly.
Imports Microsoft.Office.Interop.Visio
Imports System.Runtime.InteropServices

Under the class statement, use the Implements keyword to indicate that the class implements the IVisEventProc interfaces defined by Visio. Once you add this statement, Visual Basic adds a function called VisEventProc. The signature for this function comes from the definition of the inteface in Visio type library. Your EventSink class should then resemble the following code.

Public Class EventSink
Implements Microsoft.Office.Interop.Visio.IVisEventProc

Public Function VisEventProc _
    (ByVal nEventCode As Short, ByVal pSourceObj As Object, _
    ByVal nEventID As Integer, ByVal nEventSeqNum As Integer, _
    ByVal pSubjectObj As Object, ByVal vMoreInfo As Object) _
    As Object Implements Office.Interop.Visio.IVisEventProc.VisEventProc

End Function
End Class

We discuss how to implement this function later.

Next, we need to tell Visio what events to monitor for and where to send them. Following is the implementation of the Connect class OnConnection method for a COM add-in that listens to the DocumentCreated event using the Event Object:

Dim applicationEventList As EventList

Try
    ' Do an explicit cast to the Visio Application object so it is
    ' clear that there is a type change in this statement.
     visioApplication = CType(application, _
         Microsoft.Office.Interop.Visio.Application)


    ' Listen to the Document Created Event sourced
    ' from by the Application.
     applicationEventList = visioApplication.EventList
     visioEventSink = New EventSink

     resultEvent = applicationEventList.AddAdvise( _
           CShort(VisEventCodes.visEvtCodeDocCreate), _
              visioEventSink, "", "")

     Catch err As COMException
         MsgBox("Exception in OnConnection: " & _
                err.Message, , TITLE)
     Catch err As InvalidCastException
          MsgBox("Exception in OnConnection: " & _
               err.Message, , TITLE)

End Try

The preceding code adds an Event object to the EventList collection of the Visio Application object.

As long as the resultEvent object is enabled, Visio continues to send events to the add-in. The Com add-in should disable this event in the OnDisconnectEvent method:

Public Sub OnDisconnection( _
    ByVal removeMode As ext_DisconnectMode, _
    ByRef custom As System.Array) _
    Implements IDTExtensibility2.OnDisconnection

    
If Not (resultEvent is Nothing) then
    resultEvent.Enabled = CShort(False)
End If
    
End Sub

To indicate the event that you want to handle, supply its event code as the first parameter to the AddAdvise method. The preceding code specified the event code for the DocumentCreated event. The AddAdvise method also indicates how the object will receive the events. In this example, our code creates an instance of the EventSink class object defined above.

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. VisEventProc is a function that returns Boolean values to Visio in response to QueryCancel events. Visio ignores the return value for all other event types. This lesson does not demonstrate how to handle any QueryCancel events, so our example implementation will return the default value.

We can check the event code that is passed into our event handler to determine which event has been fired. The following implementation handles the DocumentCreated event that we registered for in the AddAdvise call.

Public Function VisEventProc(ByVal nEventCode As Short, _
    ByVal pSourceObj As Object, _
    ByVal nEventID As Integer, _
    ByVal nEventSeqNum As Integer, _
    ByVal pSubjectObj As Object, _
    ByVal vMoreInfo As Object) As Object _
    Implements Office.Interop.Visio.IVisEventProc.VisEventProc

    Try
       ' Only respond to Document created events.
        Dim newDocument As Document

        If (nEventCode = _
          CShort(VisEventCodes.visEvtCodeDocCreate)) Then
            newDocument = CType(pSubjectObj, Document)
            MsgBox(newDocument.FullName, , TITLE)
        End If

     Catch ex As COMException
         MsgBox("Error in VisEventProc: " & ex.Message, , TITLE)
     Catch ex As InvalidCastException
        MsgBox("Error in VisEventProc: " & ex.Message, , TITLE)

     End Try


End Function

When the event code is visEvtCodeDocCreate, the subject object passed to the VisEventProc method is the newly created Document.

Summary: Connection Points and the Visio Event Object

Both of these methods for coding event handling in Visio 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 Visio to send an event message to the client for all events for that type of object. This causes 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, and as the Com add-in runs in the same process as Visio, negatively impact the performance of Visio.