Event Handling by Stencil

Started by DavidJT, March 13, 2013, 06:24:41 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

DavidJT

I am new to writing VBA code in Visio but have done so in other applications.  I am attempting to create drawings that pull objects off a master stencil onto the canvas, an action that would prompt the user to supply a name, the supplied value would perform a database dip to retrieve information to populate into the shapes data fields.  Different objects would have different database dips to pull back different information (example the "name" of an Ethernet box would retrieve location, rack placement, ip address, etc...)

I would like the code that is executed to be stored with the stencil rather than the drawing so that it doesn't matter what drawing the stencil is attached to, the information is retrieved and populated on the instance of the object when dropped onto the canvas.  My attempts to set up an event handler only seem to work when the code is attached to the drawing and not when attached to the stencil.

Does it make sense for the code related to an object that has be created on the master to be stored in the stencil rather than the drawing?

Thanks

Jumpy

Placing of the code depends several factors, some you mentioned.

Problem with code in stencil is that code in drawing doesn't know the other code, so events placed in the drawing can't call code in the stencil. To make this work you have to add a reference to the stencil in the drawing like you would add a reference to Excel-library or Word-library etc.
That is cumbersome as you can only use drawings/templates that have the reference to your stencil.

Here is another way, that works without the reference:
To call a macro placed in the stencil, you could place the CALLTHIS-function in the EventDrop-Cell of the shapesheet of the shape that calls the code.

Visio Guy

A problem that you're likely running into is that you're putting event code in ThisDocument, but that ThisDocument is actually the stencil, so nothing is happening.

You can call Module code directly, using Jumpy's suggestion. The CALLTHIS ShapeSheet function can be placed in the EventDrop, EventDblClick or right-click Actions cells for any shape. For example:



'// ShapeSheet formula for a shape with a custom right-click Action:
'// MCallThisFunctions is a VBA Module
'// CodeProjectInStencil is the overall VBA project name for the stencil
'//
'//  Actions.DoSomething.Action =
'//                        CALLTHIS("MCallThisFunctions.ShowMessage","CodeProjectInStencil")

'// Module: MCallThisFunctions
Public Sub ShowMessage(byref visShp as Visio.Shape)

  Call MsgBox("You right-clicked the shape: " & visShp.NameID & " that has this text: " & visShp.Text)

End Sub



If you need to trap events, then the stencil needs to create some sort of object, and initialize it with the document you are working on:



'// Class: CSolutionDocument
'//
'// Your solution could have a collection of solution documents, if multiple
'// documents are open at the same time. The stencil's code could handle
'//  it all using a collection of CSolutionDocument objects.

Private WithEvents m_visDoc As Visio.Document

'// Pseudo constructor that code outside this class can
'// call to initialize this class with a Visio document
'// variable. This is just to mimic a .NET constructor:
Public Sub New_VBA(ByRef visDoc As Visio.Document)
  Set m_visDoc = visDoc
End Sub

Private Sub Class_Initialize()
  '// Check that Visio.ActiveDocument isn't the stencil!
  Set m_visDoc = Visio.ActiveDocument
End Sub
Private Sub Class_Terminate()
  '// Cleanup:
  Set m_visDoc = Nothing
End Sub

Private Sub m_visDoc_ShapeAdded(ByVal visShp As IVShape)

  '// A shape was dropped on a page in m_visDoc. Do something
  '// based on the master type:

  If (visShp.Master Is Nothing) Then Exit Sub

  If (visShp.Master.Name = "Rack") Then
    Call m_initRackShape(visShp)
  End If

  If (visShp.Master.Name = "Server") Then
    Call m_initServerShape(visShp)
  End If
 
End Sub



The above class self-initializes with the active document, but you can also explicitly initialize and instance:



Dim d as CSolutionDocument
Set d = New CSolutionDocument

'// Give d a Visio document to hook events to:
Call d.New_VBA(Visio.ActiveDocument)
'// Or something like:
'// Call d.New_VBA( Visio.Documents.Item(3) )


'// Now d is off and running! If shapes are added to the document
'// held in d, then m_visDoc_ShapeAdded will execute!

For articles, tips and free content, see the Visio Guy Website at http://www.visguy.com
Get my Visio Book! Using Microsoft Visio 2010

DavidJT

Thank you for the assistance.  I had some trouble making this work until I noticed in your example that the parameters to CALLTHIS needed to be CALLTHIS("Module.Procedure", "Project").  Using CALLTHIS("Procedure", "Project") was not working for me.

Jumpy

@Visio Guy: Could you explain your second solution a little more? Where do you place the code / the classmodule? Everything in the stencil? In ThisDocument of the stencil? Or has to be sth. in the drawing?
Do I understand this right: That way you can call code from the stencil from the drawing or from events of the drawing without the need to place a reference to the stencil?


DavidJT

My main goal in writing code in the stencil was to have the ability to create ojects that have actions / responses that will be consistent to whatever document they are attached to.  The CALLTHIS functionality has helped with this goal but a subsequent function will be to have the stencil create a reference to itself in the document that it is attached to.  This will then not only make routines written in the stencil available to the CALLTHIS function but will make all of the routines available to the document.  Don't know how to do that yet but my focus isn't quite there yet either.

TwoBeAss

#6
Quote from: Visio Guy on March 14, 2013, 02:27:22 PM


If you need to trap events, then the stencil needs to create some sort of object, and initialize it with the document you are working on:



'// Class: CSolutionDocument
'//
'// Your solution could have a collection of solution documents, if multiple
'// documents are open at the same time. The stencil's code could handle
'//  it all using a collection of CSolutionDocument objects.

Private WithEvents m_visDoc As Visio.Document

'// Pseudo constructor that code outside this class can
'// call to initialize this class with a Visio document
'// variable. This is just to mimic a .NET constructor:
Public Sub New_VBA(ByRef visDoc As Visio.Document)
  Set m_visDoc = visDoc
End Sub

Private Sub Class_Initialize()
  '// Check that Visio.ActiveDocument isn't the stencil!
  Set m_visDoc = Visio.ActiveDocument
End Sub
Private Sub Class_Terminate()
  '// Cleanup:
  Set m_visDoc = Nothing
End Sub

Private Sub m_visDoc_ShapeAdded(ByVal visShp As IVShape)

  '// A shape was dropped on a page in m_visDoc. Do something
  '// based on the master type:

  If (visShp.Master Is Nothing) Then Exit Sub

  If (visShp.Master.Name = "Rack") Then
    Call m_initRackShape(visShp)
  End If

  If (visShp.Master.Name = "Server") Then
    Call m_initServerShape(visShp)
  End If
 
End Sub



The above class self-initializes with the active document, but you can also explicitly initialize and instance:



Dim d as CSolutionDocument
Set d = New CSolutionDocument

'// Give d a Visio document to hook events to:
Call d.New_VBA(Visio.ActiveDocument)
'// Or something like:
'// Call d.New_VBA( Visio.Documents.Item(3) )


'// Now d is off and running! If shapes are added to the document
'// held in d, then m_visDoc_ShapeAdded will execute!



Quite an old topic, but exactly what i am trying to do...
I added a new Class CSolutionDocument to my stencil and during DocumentOpen Event i try to initialize with the active Document..
Debug Prints the right name, an i dont know where to investigate further...

Private Sub Document_DocumentOpened(ByVal doc As IVDocument)
Debug.Print "Doc open"
Dim visDoc As Visio.Document
Dim A As New CSolutionDocument
Set visDoc = Visio.ActiveDocument
Debug.Print visDoc.Name
Call A.New_VBA(visDoc)
End Sub


But the events were not triggered, any ideas or hints for me ?

Thanks in advance...

EDIT: solved on my own.. The declaration of the var needs to go out of the documentopen event...