lisa-marie mueller

lisa-marie mueller

bullet pont

Async Operations for Revit Plug-ins link

With guest writer: Andreas Brake

October 16, 2020

falling objects with laptop and vr headset - yellow Happy Friday! This week I am excited to welcome back Andreas Brake as the guest writer for this post. Andreas was my partner in crime for our project StreamVR which won the Best Overall Project Award at the ENGworks Hackathon. He is a software engineer at Topgolf Media doing server development. In his free time, he enjoys hiking, gaming, and gardening. This week, he will discuss how to perform async operations within Revit, a feature we used when working on StreamVR.

review

When developing Revit plug-ins, one will eventually run into the issue where the structure of the standard "tutorial" plug-in has a specific shortcoming: this is the inability to run in parallel or in the background with Revit or other plug-ins. This blog post will be going over ways to structure your plug-in to allow for asynchronous and multi-threaded activity.

For a standard add-in with a ribbon, your application has one main class that implements "IExternalApplication" this class is registered in your ".addin" file and acts as your entry point. This class defines two methods named "OnStartup" and "OnShutdown" that are called when Revit is started and shut down. These hooks are frequently used to create ribbon buttons that trigger additional commands.

Commands are classes that implement "IExternalCommand". They implement a method called Execute which, as the name would imply, is the method that is called when your command is executed. In simpler plug-ins, you can also just have a command which is registered in your ".addin" file.

When a command is executed, it blocks other activity within Revit. This means that you cannot run other plug-ins at the same time or run asynchronous operations within the plug-in that still operate on the Revit DB.

summary of changes

The major change we are doing to allow for background operations is to have our main application contain a "static" reference to itself. This static reference is assigned "OnStartup" and will act as a singleton that can be used by your other commands. It will persist throughout the Revit application lifecycle. This singleton can be used to create listeners for asynchronous events, for example a form that takes user input. We ensure that events from this form trigger a class that implements "IExternalEventHandler" which will perform additional background operations that can interact with the Revit DB.

code overview

The code below shows the application code that registers itself as a singleton.

Later a command is triggered by a button on the Revit ribbon which then calls "ShowFormInBackground" on this singleton.

"ShowFormInBackground" creates instances of all "ExternalEvent"s that will be triggered by our async operation, in this case a button press in a WPF form. The method also creates and shows the form, passing through the event objects for later usage.

Because the "ExternalEvent" objects are passed to the WPF form, we can call the "Raise" method on these events when a user clicks a button in the form. When "Raise" is called on a class that implements "IExternalEventHandler", the "Execute" method is triggered with a reference to the UIApplication. This means that we can now run additional operations against the RevitAPI including updating elements all without relying on a command that blocks all other operations within Revit.

bonus

because "ExternalEvent"s are intended to be called by external sources, we can even go so far as to trigger this event within a Task which will run elsewhere within the ThreadPool!

resources

If you want to learn to code and don’t know where to start check out my posts about Steps to Learn to Code [for architects and designers] Part 1 and Part 2.

Revit API Docs

bullet pont

recommended next