A recent forum post brings forward an interesting question on extending Viso solution code to stencils. The ability to insert code into a stencil brings significant additional functionality to some Visio Solutions. The question "how do I call a sub from an external module?" of course is started by adding a reference to it in from the Tools menu in the vba editor. Then you just call your sub using standard notation "ado_data.modAccessData.LoadAccessNetData" (ado_data=stencil). The fun part begins when you want to manage this reference functionality in-line to allow some flexibility on options that you may/or maynot want your users to have access to. This gets us into vbProjects.
These routines assist in managing projects and references in-line
'
' requires program files\common files\microsoft shared\vba6\vbe6ext.olb
' as a reference
'
' Private Const strImptExptStencil = "imptexpt.vss"
'
' testReference(strCurrDocName, "imptexpt")
'
Private Function testReference(strProject As String, _
strModule As String, _
Optional blnRemove As Boolean = False) _
As Integer
Dim intReturn As Integer
intReturn = -1
Dim vbideVBE As vbide.Vbe
Set vbideVBE = ThisDocument.Application.Vbe
Dim currProjects As VBProjects
Set currProjects = vbideVBE.VBProjects
Dim currProject As VBProject
Dim strTgtFile As String
strTgtFile = ThisDocument.Path & strModule
Dim strBaseFile As String
strBaseFile = ThisDocument.Path & strProject
Dim strBuild As String
Dim strTemp As String
Dim intX As Integer
Dim intY As Integer
Dim refRemove As vbide.Reference
For intX = 1 To currProjects.Count
strBuild = currProjects.Item(intX).FileName
If LCase(strBuild) = LCase(strBaseFile) Then
Set currProject = currProjects.Item(intX)
For intY = 1 To currProject.References.Count
strTemp = currProject.References.Item(intY).Name
If LCase(strTemp) = LCase(strModule) Then
intReturn = intY
If blnRemove = True Then
Set refRemove = currProject.References.Item(intY)
currProject.References.Remove refRemove
End If
End If
Next intY
End If ' test for item name
Next intX
testReference = intReturn
Exit Function
ErrHandler:
MsgBox Err.Description
testReference = intReturn
End Function
'
' get a list of projects that we are currently using
'
Private Function getCurrProjects() As VBProjects
Dim vbideVBE As vbide.Vbe
Set vbideVBE = ThisDocument.Application.Vbe
Dim currProjects As VBProjects
Set currProjects = vbideVBE.VBProjects
Set getCurrProjects = currProjects
End Function
'
' what project are we running as the base document
'
Private Function getCurrBaseProject() As VBProject
Dim vbideVBE As vbide.Vbe
Set vbideVBE = ThisDocument.Application.Vbe
Dim currProjects As VBProjects
Set currProjects = vbideVBE.VBProjects
Dim currProject As VBProject
Dim strBaseFile As String
strBaseFile = ThisDocument.Path & ThisDocument.Name
Dim strBuild As String
Dim strTemp As String
Dim intX As Integer
For intX = 1 To currProjects.Count
strBuild = currProjects.Item(intX).FileName
If LCase(strBuild) = LCase(strBaseFile) Then
Set currProject = currProjects.Item(intX)
End If ' test for base name
Exit For
Next intX
Set getCurrBaseProject = currProject
Exit Function
ErrHandler:
MsgBox Err.Description
Set getCurrBaseProject = Nothing
End Function
'
' check to see that a project has been loaded
'
Private Function testCurrProjects _
(ByRef currProjects As VBProjects, _
ByVal strModule As String) _
As Integer
Dim intReturn
intReturn = -1
Dim intX As Integer
Dim intY As Integer
Dim strBuild As String
For intX = 1 To currProjects.Count
strBuild = currProjects.Item(intX).Name
If LCase(strBuild) = LCase(strModule) Then
intReturn = intX
Exit For
End If ' test for item name
Next intX
testCurrProjects = intReturn
Exit Function
ErrHandler:
MsgBox Err.Description
testCurrProjects = intReturn
End Function
This code demonstrates how it might be applied to putting support into a visio vb project dynamically
Private Sub addImptExptSupport()
Dim ImptExpt As Visio.Document
' go check the property to see if it is already loaded
If ThisDocument.pImptExptLoaded = True Then Exit Sub
Dim docStencil As Visio.Document
Dim strPath As String
strPath = ThisDocument.Path
Dim fs As FileSystemObject
Set fs = CreateObject("scripting.filesystemobject")
Dim strFile As String
Dim strStencil As String
strStencil = ""
On Error GoTo ErrHandler
Dim strCurrDocName As String
strCurrDocName = ThisDocument.Name
strStencil = strImptExptStencil
strFile = strPath & strImptExptStencil
Set docStencil = Application.Documents(strImptExptStencil)
If docStencil Is Nothing Then
If fs.FileExists(strFile) = False Then
MsgBox "Stencil " & strImptExptStencil & " not in directory " & strPath
Exit Sub
Else
' add the stencil and the reference
Application.Documents.OpenEx strFile, _
(CInt(VisOpenSaveArgs.visOpenDocked) + _
CInt(VisOpenSaveArgs.visAddHidden))
DoEvents
getCurrBaseProject.References.AddFromFile strFile
DoEvents
MsgBox "Please save/close document and then reload (imptexpt = new load)"
DoEvents
End If ' stencil not found
Else
If testReference(strCurrDocName, "imptexpt") = False Then
getCurrBaseProject.References.AddFromFile strFile
MsgBox "Please save/close document and then reload (imptexpt = new load)"
DoEvents
End If
End If 'stencil not loaded
Exit Sub
ErrHandler:
If Err.Number = -2032465760 Then
DoEvents
Resume Next
End If
MsgBox "Load ImptExpt Failed, stencil = " & strStencil
MsgBox Err.Description
End Sub
HTH,
al
or even how to tie code to a shape so that drawing is sent....code goes with it....or even preventing certain shapes from using code. It has always been unclear to me how code is managed in relation to drawing and shape....would hate to send 50 bizzilion modules to a user as part of a simple diagram of of 5 shapes.
The original Visio did allow some code to be in a shapesheet, but frankly that's a security nightmare so it had to go. I understand not wanting to send a lot of code along with a very simple diagram.
As Chris pointed out in one of his discussions, when working with complex Visio solutions dotnet is the way a developer should consider going. However. I like to prototype in VBA so putting code into stencils and then programmatically controlling references and loaded stencils becomes a poor man's way of working with a dotnet kinda environment. The application that I sandbox most with is a datacenter rack drawing tool that allows me to draw based upon external data.
The implementation of the sandbox is separate stencils for functions like ICMP, SNMP, WMI(wkstation/srv), ADO, IMPT/EXPT. I can then turn properties on in the base document for what I want to load. It also means that if I change a module, my users get a new stencil for that set of functions and nothing needs to go further. This also allows a (very) limited control of what a user can run against. So by not sending along the stuff that directly interacts with the network, I don't have network security breathing down my neck.
The primary stuff kept in the base drawing is eventhandlers to call the external stuff and some basic drawings classes (although right now mine is kinda bloated with a lot of experimental objects as I move it to a more production type of solution).
al
Putting code in the stencils is a strategy aimed at keeping copies of VBA code to a minimum. The classic problem is that people put VBA code in documents or templates, which get copied for every Visio drawing.
Putting VBA into the stencils stops this proliferation to some extent. Updates to code or shapes occur in the same place.
It seems to me that I used to have trouble with referencing the stencil projects. It caused some instabilities and problems with not being able to close documents, though. I must admit, I haven't tried it for a while, though. Is this any better? Is there a recommended technique?
I'm just an old guy out here in the cornfields to be asking about best practices. I usually go looking to you and the other mvp's to keep me on track. Frankly I am constantly surprised at how stable it is given the mixed models that I have been throwing at it. The challenge I am working on right now is that as I move it off of Access (adodb) to Sql trying to maintain the interoperability with data recordsets. I'll probably use XML as the translate between the data mechanisms. Remembering that this is sidestream to the real world of moving data centers.
al
Noticed your post...thanks...sorry for not catching last month
I am definitely not fluent in VBE (resisted since the security issues and really did not want get into "raising a barn just to build a dog house") but the post at the top seems to me to be the exact opposite of what I was looking to do. Namely, the top seems to be "how to pull a stencil and such into a VBE project" however I was more looking for "how to package/reference a module in some new drawing".
for example, it would be ideal the module got bundled with the stencil so that it could be shared at the stencil level (security mess aside). It might even be ok to say the module is a separate file that must reside in some directory (with stencil, with drawing, or etc etc etc).
I was hoping that I did not have to build an entire visio instance via VBE just to access a module.
Hopefully, an alternative is to make a template out of "clean" drawing where stencils, modules, etc are included in the template. Assuming I am correct, then somebody could send the template as a drawing to others without others becoming a rocket scientist on VBE
For more on this topic, see: Referencing VBA Modules in Stencils (http://visguy.com/vgforum/index.php?topic=359.0)