Recently I have worked a little on a Revit functionality that required me to either come up, or dig up some of the existing methods for dismissing Revit pop-ups. The idea usually is rather simple. Any time you try to automate a task within Revit, and such task can potentially result in a warning, error or any other kind of notification window popping up, we are kind of screwed. The reason why, is that some of these pop-ups are not so straight forward to dismiss automatically, or in case of some of the warnings and failures, it’s not easy to resolve them. When they are not resolved they can roll back any of the changes to the file that we are making, and effectively render the whole process useless.
OK, enough of hypotheticals. Let’s get into it and talk about some specifics. So the first task was to open the file. One thing that usually happens when you are opening a file, that if the file is missing Links, it will show a window that looks like this:
The above DialogBox is not that big of a deal. We can dismiss it pretty easily, the only trick is to dismiss it the right way: Ignore and continue opening the project. So here’s a little background info on the above dialog from the great Jeremy Tammik: Article Read the whole thing, as it’s important to understand the context, but the most important part of it is this:
It basically classifies the three different types of dialog boxes that Revit will show you. The way these are manifested is via the DialogBoxShowingEventArgs which is a base class for two other classes: MessageBoxShowingEventArgs and TaskDialogShowingEventArgs. That’s what Jeremy is talking about in the above article. So the image above with the links error, is really a TaskDialog. As Jeremy mentions, you can set it’s result – simulate a user pressing one of the buttons – by overriding the result for that window with a value of 1001 (Open Manage Links to correct the problem) or 1002 (Ignore and continue opening the project). Here’s a sample command code to handle that dialog:
Please note that I put that code into the actual ExternalCommand class. Why? Because I want this override to only kick in and last when I am performing this specific task. I don’t want my plugin to constantly dismiss this dialog, rather only when I am executing a command that would open a file from the API.
Just to break down a few of the important lines of code here:
- 10 – This is how you can subscribe to the DialogBoxShowing event.
- 14 – This is how to unsubscribe from that event, so it only works when we are inside of our command.
- 32 – We are only interested in one, very specific Task Dialog, so that’s why I am checking for a DialogId here.
- 33 – This is how to override the result, and again 1002 is the second button from the top, so we are simulating a user pressing that button.
Now, another potential dialog box that we can dismiss is a little bit more complicated. Here’s the Dialog Box:
It normally shows up when you try to copy things between files, but there is a trick to this. In a normal instance, when you open two files and CTR+C then CTR+V, this might show up as a little warning dialog in the bottom right corner of the screen. These just disappear on their own, as they are what we call Toast Notifications. Not if you use the API to copy an element. Revit is truly weird. On top of all that, this particular Dialog is basically the first category on Jeremy’s list, which means that we don’t really have any recourse to dismiss it programmatically via the API. You can dismiss it via Cancel, but that’s not what we want to do. When that happens, I usually turn to Win32 API.
Oh yes, that nasty little library. Thanks to Matteo Cominetti for this blog post He’s outlining a method for using the Win32 API to capture a Warnings DialogBox, and simulate users pressing buttons on it, to automate exporting of the Revit Warnings. We no longer have to do that, since Revit 2018+ has that built in as part of the API, but back in the days it was a valid approach. As it turns out, it still is, just not for this particular task.
Here’s how I butchered Matteo’s code, added some of my own, in order to dismiss the above DialogBox:
First we expand our handler method to include catching the above DialogBox. We can still identify it here, but we were not able to dismiss it from here, since the override method wasn’t working for this particular type of the message. In order to dismiss it, we created a special Win32Api class, and handled it there. The Win32 interaction must happen on a background thread, so we changed this method to be async and now we can call await Win32Api.ClickOK(). Here’s the code for the method:
I am sorry, but I am not going to go into details of explaining what each line of code does here. The idea is simple. We let the DialogBox appear, and then this code simulates a left mouse button being pressed down and then released on top of the OK button. I wanted to dismiss the dialog with OK – that’s the most critical part here – instead of Cancel which actually rolls back our whole operation of copy-paste.
As you can see, Revit’s API is pretty good, but not that great. There are still some glaring holes in its functionality, and sometimes going to Win32 API is the only way to achieve certain things.
I hope you have enjoyed that. As usual please comment below.
Ps. One additional way that comes to mind, that I haven’t tried for this, was the Failure Handler. I agree, that would have been much easier, but since I haven’t tried it, I guess I don’t know if that works. If someone out there wants to give that a whirl, and let me know I will appreciate that.
I remember using scar or pascal (I think it was called that, long time ago) when I was young to cheat in online games looking for colours or small pictures on the screen to click on. Thought something like that running with dynamo to click on dialogs might be good.
great it helps also to understand, on a plus general difference what is the difference between Revit API and WIN32 API that is for me an achievement!
Thank you, that is very useful!
Here’s maybe an interesting discussion of what you were thinking of at the end… Using the failure transaction class and failuresAccessor.DeleteWarning(). Though you still have to deal with Errors!
Yes! Thanks for sharing. I knew these methods existed, but somehow decided to go after the ability to dismiss them via Win32 APIs. I was probably thinking of all the future use cases for myself when errors would crop up, and the failure handlers often times don’t offer a solution other than asking users for input. That was not acceptable for me. Again, thanks for sharing.
Thanks for sharing
I tried the Failure Handler but it didn’t work the way you did
You will need to provide more information here for me to help.
Thank you very much Konrad!
I was looking for a way to trigger the events only when my app was being called cause I was putting them in the OnStartup() method.
I didn’t think of a solution as simple as having them inside the actual Execute method! o_O
You are welcome!