The MicroStation Development Library (MDL) and MicroStationAPI provide APIs for developers wanting to create custom applications for MicroStation® from Bentley Systems. We create a MicroStation application as a DLL, written using C++ and built with the Microsoft C++ compiler and linker provided with Visual Studio.
When editing your source code, you can choose whether to use Microsoft Visual Studio, Microsoft Visual Studio Code, or one of your favourite text editors.
When building your app, you can use Visual Studio or the Bentley Systems make (bmake) tools.
The Dependency API is a subset of the MicroStationAPI C++ interface. It lets a developer write code that makes one graphic element depend in some way on one or more root elements. The nature of the dependency is up to the programmer.
For example, when a user creates a tag element it is dependent on its host element. That dependency is an offset relationship. The dependent element (the tag) tries to main a constant geometric offset from its host. When a user moves the host, using MicroStation's Move tool, the tags follow the host.
You can download the source code of the BoxDependency C++ project described here.
The box dependency provides two example application for MicroStation CONNECT …
The original BoxDependency was created by Mark Anderson (alas no longer with Bentley Systems), not long after MicroStation V8 was introduced. He wrote it using almost entirely MDL (C-style) programming, because the C++ MicroStationAPI was not available to him at that time.
BoxDependency demonstrates a use of the dependency API. The user selects a number of elements and the application draws a rectangle (a shape element) around them. In dependency API terms, those elements are the root elements of the dependency. BoxDependency adds a dependency linkage to that rectangle: the linkage includes the Element ID of each element in the selection set (the root IDs). The application also starts an asyncronous dependency monitor, in the form of a callback function. If any of the root elements is modified (e.g. moved), the dependency callback is invoked by MicroStation. The callback, in the BoxDependency example, computes a new rectangle that encompasses all the root elements. It then modifies and redraws the rectangle.
You can download the source code of the BoxDependency C++ project described here.
Start by using BoxDependency to witness its functions. Here are some boxes (shape elements) ready to demonstrate BoxDependency …
Select those elements and key-in BoxDependency command
BOX DEPEND CREATE
That command performs the following actions …
The IDs of the boxes are known as the root IDs of the dependency.
The BoxDependency application sets a system callback in its MdlMain()
.
The effect of that callback is that when MicroStation observes a change in a dependency,
it invokes the registered callback.
In this example the callback performs the following …
This example works when user identifies a root element. The root this case is the anchor on which the dependency lies. User places a cell and we attach a dependency linkage to that cell. The dependency linkage includes …
This example works when user identifies a root element. The root this case is the anchor on which the dependency lies. User places a cell and we attach a dependency linkage to that cell. The dependency linkage includes …
The Dependency API provided in the MicroStation CONNECT SDK is a C++ wrapper around the
mdlDependency_api
.
The DependencyManagerLinkage
is a data struct
.
See header file DgnPlatform/DependencyManagerLinkage.h
.
Its heritage is a C-style union
of data that represent different kinds of dependency …
union { UChar data [DEPENDENCY_SOME_DATA]; ElementId elemid [DEPENDENCY_SOME_ELEMIDS]; DependencyRootFarElementID far_elemid [DEPENDENCY_SOME_FARELEMIDS]; DependencyRootElementID_V e_v [DEPENDENCY_SOME_ELEMIDVS]; DependencyRootFarElementID_V far_e_v [DEPENDENCY_SOME_FARELEMIDVS]; AssocPoint assoc [DEPENDENCY_SOME_ASSOCPOINTS]; DependencyRootPath_V path_v; } root;
Which member of that union is specified in member variable rootDataType
.
For historical reasons that variable is a four-bit integer (i.e. stores values in range 0..15),
and is buried in yet another union
u.f.rootDataType
.
The number of roots is specified in member variable UInt16 nRoots
.
nRoots
is the maximum value of the index into those root arrays.
For most purposes those arrays seem enormous:
certainly for our purposes, where our example application uses a single root.
Some applications may want to store extra data in the dependency linkage. In this example …
elemid
— it doesn't need any extra data
assoc
, and stores the offset vector as extra data
elemid
, and stores the offset vector as extra data
The mechanism for storing extra data deviates badly from C++ guidelines.
Suppose, as in the BoxDependency example, we use a single root. That is, nRoots
is one.
We poke extra data in the chosen union array just after that location.
In other words …
// OffsetIdDependency DependencyLinkage.root.elemid [0]; // Root ID goes here; nRoots == 1 DependencyLinkage.root.elemid [1]; // Extra data goes here
// OffsetAssocDependency DependencyLinkage.root.assoc [0]; // AssocPoint goes here; nRoots == 1 DependencyLinkage.root.assoc [1]; // Extra data goes here
In this example, we poke a DVec3d
into that location.
Because it's a different data type, we have to perform some casting hacks to persuade the C++
compiler to accept it.
We create a class that inherits from IRootsChangedCallback
and register it with MicroStation,
usually in MdlMain()
.
That class must implement virtual method OnRootsChanged
, which MicroStation calls when it observes
a dependency change.
Here's the signature of OnRootsChanged
…
virtual StatusInt OnRootsChanged ( ElementHandleCR dependentElement, DependencyLinkage const& dependencyData, UInt8* pRootStatus, UInt8 selfStatus ) override;
That callback gives you information about your dependent element. You will probably receive multiple callbacks in no predetermined order. Briefly …
dependentElement
is our element. Something may have happened to our element or to one of the dependency roots
dependencyData
is our dependency data, extracted from our dependent element
const
references — we can't change them
pRootStatus
is an array of rootStatus
, nRoots
in size
selfStatus
applies to our dependent element
Our callback function must observe and react to the rootStatus
array and to the selfStatus
code.
A couple of switch
statements handles those.
The BoxDependency application requires a user-defined selection set when establishing its criteria.
I started to write a SelectionSetManager
class, but the compiler told me that one already existed!
Sure enough, I looked in header file DgnView/SelectionSetManager.h
and found a perfectly-formed
class already in place.
Class Bentley::DgnPlatform::SelectionSetManager
is straightforward to use, and as you would expect eliminates the
memory-management issues that were your responsibility with the MDL mdlSelect_api.
You can see how it's used if you
download
the source code of the BoxDependency example.
You can download the BoxDependency. The project is built using Bentley Make (BMake). It uses the C++ compiler and linker installed with Visual Studio 2013.
Post questions about C++ and the MicroStationAPI to the MicroStation Programming Forum.