Q How can I code MicroStation VBA to run something automatically? Specifically …

Questions similar to these, posed by MDL and VBA developers, appear on the Bentley Discussion Groups, typically the VBA discussion group.

Q How can I load my VBA application automatically?

A You can load your VBA project automatically by adding it to the list of startup projects in configuration variable MS_VBAAUTOLOADPROJECTS or MS_VBAREQUIREDPROJECTS. See VBA help for an explanation of those rather similar variables.

For the example DGN file event handler described here, the line in the user or project configuration file looks like this …

    MS_VBAAUTOLOADPROJECTS = OpenCloseEventHandler

Q How can I do something when a user opens or closes a DGN file?

A MicroStation VBA provides a standard Visual Basic mechanism that lets you handle file events. An event, for our purposes, occurs when a user opens or closes a DGN file. From the programmer's perspective, an event is something that happens outside the usual flow of your program. The technical term is that an event is asynchronous.

Event Handler Class

We're using a few new jargon words here, so you should expect the implementation to be a little unusual as well. To handle asynchronous events, you need an event handler. MicroStation VBA lets you define an event handler in a class module. The key to this implementation is the following line of code …

Private WithEvents hooks As Application

The keyword WithEvents tells VBA that this class is an event handler. The statement assigns the variable hooks as a reference and associates it with the Application, which in this case is MicroStation.

VBA automatically creates two subroutines hooks_OnDesignFileClosed and hooks_OnDesignFileOpened. They are known as hooks or callbacks or callback functions, because MicroStation calls your code when that named event occurs.

Here's the complete code of a class clsOpenClose that provides an event handler …

Option Explicit
' ---------------------------------------------------------------------
'   Setup this class as a DGN open/close event handler
Private WithEvents hooks As Application
' ---------------------------------------------------------------------
Private Sub Class_Initialize()
    Set hooks = Application
    Debug.Print "clsOpenClose initialised"
End Sub
' ---------------------------------------------------------------------
Public Sub hooks_OnDesignFileClosed(ByVal fileName As String)
    Debug.Print "Closed design file " & fileName
End Sub
' ---------------------------------------------------------------------
Public Sub hooks_OnDesignFileOpened(ByVal fileName As String)
    Debug.Print "Opened design file " & fileName
End Sub
' ---------------------------------------------------------------------

Establishing your Event Handler

Now you need a way to create an instance of your clsOpenClose class. One place to do this is in your program's main entry point. Usually, the main entry point is simply a public subroutine Main. Use the Set and New keywords to create a new instance of the class …

    Set oOpenCloseHandler = New clsOpenClose

But there's a problem. Part of the problem is that the VBA help section about design file events doesn't tell the full story. Where should you declare the variable oOpenCloseHandler? If you do this …

Sub Main ()
    Dim oOpenCloseHandler As clsOpenClose
    Set oOpenCloseHandler = New clsOpenClose
End Sub

Local variable oOpenCloseHandler is created and then destroyed immediately when Main exits. So your event handler started and then terminated: it was no more than a warm summer zephyr. The solution is to declare your variable at Module scope: that way it hangs around while your project is loaded …

Private m_oOpenClose As clsOpenClose

Now instantiate the variable in Main, and your class instance will stay alive until the project is unloaded …

Private m_oOpenClose As clsOpenClose
Sub Main ()
    Set m_oOpenClose = New clsOpenClose
End Sub

Another way to start the event handler is to add a subroutine named OnProjectLoad. When MicroStation first loads a project, it looks for a subroutine named OnProjectLoad and, if it exists, executes it. Since you can load a project automatically, this provides a way to load and run your event handler code automatically.

Thanks to Duncan Macdonald at PLP Architects in London for digging that nugget from Ask Inga.

In this example, OnProjectLoad is no different to Main …

' ---------------------------------------------------------------------
'   OnProjectLoad, if it exists in your module, is run automatically
'   when your VBA project is loaded into MicroStation.  You can tell
'   MicroStation to load VBA projects automatically using the configuration
'   variable MS_VBAAUTOLOADPROJECTS
'
'   In this example project, OnProjectLoad simply sets a reference in the
'   member variable m_oOpenClose to a new instance of clsOpenClose
' ---------------------------------------------------------------------
Public Sub OnProjectLoad()
    Set m_oOpenClose = New clsOpenClose
End Sub

Sample Project

Clearly this example event handler doesn't do anything useful. The Debug.Print statements give you a log (in VBA's Immediate window) of the events as they are handled …

clsOpenClose initialised
Closed design file D:\temp\point-Test2d.dgn
Opened design file L:\test.dgn
Closed design file L:\test.dgn
Opened design file D:\temp\lstring.dgn
Download

Download the Event Handler VBA Project

You can download the event handler VBA project. Unpack OpenCloseEventHandler.zip, which contains OpenCloseEventHandler.mvba. Copy the .mvba file to one of the Bentley folders in the MS_VBASEARCHDIRECTORIES configuration variable. Assuming you have a default installation of MicroStation V8.5, that folder might be C:\Program Files\Bentley\Workspace\Projects\Standards\vba.

Load the project in the usual way, and OnProjectLoad is run automatically. Load the project automatically, and OnProjectLoad is run automatically.

You could also run Main to initialise your event handler, but it's unnecessary. On loading, you'll see the clsOpenClose initialised message in VBA's Immediate window. After that, nothing will happen: you can do things in MicroStation as usual. However, when you close the current file you will see the Closed design file message. When you open a new file you will see the Opened design file message.

Q How can I do something when a user opens or closes a DGN model?

A A DGN file contains one or more models. Each model contains 2D or 3D geometry and other data. A user can navigate between models using MicroStation's user interface. MicroStation VBA provides a standard Visual Basic mechanism that lets you handle model events. An event, for our purposes, occurs when a user opens or closes a DGN model. From the programmer's perspective, an event is something that happens outside the usual flow of your program. The technical term is that an event is asynchronous.

To observe model events, write a VBA class that Implements IModelActivateEvents. Your class should have subroutines BeforeActivate and AfterActivate. Each subroutine is passed a ModelReference object.

Q How can I tell when MicroStation is doing nothing?

A The EnterIdle state informs you when MicroStation's input queue is empty and it is not performing any processing. It is waiting for user input. A new MicroStation process does not enter the idle state until it has finished initializing. A program can use this event to be notified when initialization is complete. This event can also be useful for an OnDesignFileOpened event handler that needs to be notified the next time MicroStation becomes idle.

For example, suppose user has just opened a new DGN file. At the point of opening, it is not safe to assume that all MicroStation's internal data structures are initialised and ready for use. Writing an EnterIdle event handler lets you write code that does nothing until MicroStation is fully initialised. Once MicroStation is initialised, it enters the idle state; at that time you can expect its internal data structures to be fully populated and stable.