Modeless Forms in Revit

Today the moon is full.
I celebrated it last night already, sitting with a friend and having a fire on a hill with a 270 degree view to the east, west and south.
Very beautiful!

This full moon is special, because it is the occasion of the Chinese

Mid-Autumn Festival
.
It “parallels the autumnal equinox of the solar calendar, when the moon is at its fullest and roundest”.

Mid-autumn full moon festival 2011

This Chinese festival will even be celebrated in Switzerland tonight, as the

Mondefest Basel 2011
,
to honour the city partnership of Basel and Shanghai.

Accessing Revit from Outside an API Call-back Context

Back to the Revit API, here is a question that keeps cropping up again and again, prompting a summary of some basic aspects of interacting with Revit from a modeless form or external application, with an overview and pointers back to some of the previous posts and samples concerning this.

Question: How can I interact with Revit from a modeless form or external application?
I need to be able to switch back and forth between Revit and my form without closing it.

Answer: The Revit API is entirely designed to work only within pre-defined call-backs issued by Revit.

Therefore, you can only keep your form open while continuing to work in Revit in either one of two ways:

  • Display your form from a Revit external command, but make it modeless. Then the command can complete and return control to Revit, while the form remains visible. As soon as the command has returned, though, you can no longer make use of the Revit API.
  • Display your form from an external application, not from a Revit external command. In that case you obviously also have no access to the Revit API.

The reasons and more background information on the current situation are given in the discussions of

asynchronous API calls and idling
,

modeless door lister flaws
, and
the further posts that they point to.

As explained there, you cannot make use of the Revit API from an external application or a modeless context.

However, the API also provides the possibility to implement a workaround for this limitation, the Idling event.
It enables you to drive Revit from outside indirectly by posting an event to your Idling event handler, which has full access to the API and complete read and write access to the entire application object and all its documents.

To use this, subscribe to the Revit Idling event and raise a signal from your external application or modeless dialogue. In the Idling event handler, check for the raised signal and execute whatever functionality you need. How this can be done is demonstrated and described in full detail in my sample to

display a live webcam image on a building element face
.

Another example, where Revit is driven from a modeless dialogue box, e.g. from a context outside of any Revit API call-back, also using the Idling event to “get back in” to the Revit API context, is given by my

modeless loose connector navigator
sample.

The original version was written for Revit 2011. I recently

updated and improved it for Revit 2012
as
well.

Additionally, a generic pattern for this process is described in

pattern for semi asynchronous idling API access
.

Unfortunately, like any other call-back, Idling costs time and should therefore obviously be used with great care.
Use it sparingly and cautiously.


Comments

15 responses to “Modeless Forms in Revit”

  1. thank you very much for this wonderful blog

  2. Dear Jab,
    Thank YOU very much for your appreciation!
    Cheers, Jeremy.

  3. So amiable that i could see the chinese here in memory of mid-autumn festival, which is the second great festival for chinese.It is also called as reunion festival for family.

  4. Hello again, Jeremy.
    As I understand from your posts it is very dangerous to call any Revit API methods or functions in modeless form and I never must do it..
    Does it mean that I cannot ever read some properties of Revit classes?
    For example, I want to show list of Revit elements in TreeView in modeless form and made some action with these elements. How can I do it properly?
    Variant 1) http://wikihelp.autodesk.com/User:chekalin-v/sample1
    Variant 2) Or I must use ElementData? http://wikihelp.autodesk.com/User:chekalin-v/sample2
    Other question? If I want to get Element Parameters when I select element in TreeView (form is still modeless) what I must to do? I cannot calls element.Parameters directly. Am I right? So I must call it in Idling event and get back list of parameters to the form from Idling. Right?
    It would be great if you wrote example how to post back some data from idling to the form.
    Best Regards, Victor.

  5. I’ve tried to write simple example to get element parameters via idling event in modeless form and show them on it.
    The code is here http://wikihelp.autodesk.com/User:chekalin-v/ModeLessFormSample/Form1.cs
    I used “A Pattern For Asynchronously Updating Revit Documents” http://darenatwork.blogspot.com/2010/11/pattern-for-asynchronously-updating.html
    What do you think? Is this code safe and reliable or not?
    By the way, in “Asynchronous API Calls and Idling” posthttp://thebuildingcoder.typepad.com/blog/2010/04/asynchronous-api-calls-and-idling.html you wrote “…I will expect some of you may ask if there is another way at all for using multiple threads (and/or modeless dialogs) in external applications and still be able to interact with Revit…”. As I understood this sentence, modeless form executed in other thread than Revit main thread. But in my sample code I added Debug.Print(“Form thread number: {0}”, Thread.CurrentThread.ManagedThreadId); And I can see that OnIdling event and Form executed in the same thread. Is this correct or I didn’t understand something?
    Thanks, Victor.

  6. Dear Victor,
    To answer your first comment, which I addressed before noticing the second one:
    Every single call to any Revit API method is completely unsafe except in an officially supported Revit API context, which is always a callback from Revit such as the external command Execute method, the external application OnStartup and OnShutdown methods, various event handlers etc.
    If you are trying to interact asynchronously, you can use the Idling event to get such a callback.
    If you are able to use the API for read-only access, go ahead at your own risk. Lots of other people have done this as well. The worst that can happen is that you crash the computer and lose the Revit model and that your customers sue or kill you :-)
    Yes, your assumptions seem correct to me.
    If you give me a couple of hours of time, I’ll write an example.
    But so far, nobody was ever able to give me more time than what I already have and am frenetically using up :-)
    To answer your second comment:
    Thank you very much for the sample. I hope we get some time to turn it into a stand-alone blog post some day.
    I guess you are right about the threads.
    Cheers, Jeremy.

  7. Hello, Jeremy.
    Thanks a lot for explain answer.
    So. Now I understand the main rule – no Revit API calls except ICommand.Execute method, OnStartUp OnShutdown Methods and Revit events.
    I thought… if it is unsafe to call Revit API from other thread it would be great to throw an exeption when you try to call a Revit API from non-revit thread. For example, like if you try to change Windows Form control from other thread.
    Our customer often complained about corrupted file. Now I made a simple way – set main form of my add-on modal. In future, I’ll try to rewrite my code using Idling event.
    Waiting a new posts from you. May be you show my sample code to Arnost and comment it together.
    Best Regards, Victor.

  8. Dear Victor,
    The Revit API is absolutely not thread safe, so you can only use it in the one and only thread containing the callback providing the proper context.
    I can point Arnost to your sample code, but no guarantees for anything, of course.
    Probably it would be better if you study his AU class materials mentioned here first, so that he does not have to repeat to you what he already said there:
    http://thebuildingcoder.typepad.com/blog/2012/01/avoid-idling.html
    Cheers, Jeremy.

  9. Thanks for pointed out Arnost’s AU class materials.
    I’ve already downloaded it. Will learn it carefully.
    Best regards, Victor.

  10. Hi, Jeremy.
    Is there way to know when Revit application on idling or not? That I mean… When Revit is not in idle it is not allowed to perform any external command: Revit just disable External tools button (for instance when user select project browser)

    I want to disable my modeless form when revit is not in idle. That do you think, is it possible?
    Have a nice day,
    Victor.

  11. Dear Victor,
    Congratulations on submitting the first comment in The Building Coder history with an embedded image!
    You should definitely have a look at the ModelessDialog SDK samples ModelessForm_IdlingEvent and ModelessForm_ExternalEvent.
    They show one sensible way to disable the buttons in the modeless form when Revit is not ready for processing new requests.
    Please also check the Application.IsQuiescent property.
    Unfortunately, afaik, this property is pretty useless, since it requires a valid Revit API context to be called, which is only available when Revit is ready to process requests anyway…
    Still, try it out, maybe you can discover something useful.
    I sense the next blog post looming…
    Cheers, Jeremy.

  12. Hi Jeremy.
    Thanks for giving me some advice.
    I forgot to say that I still work with Revit 2012. But of course I look at the modeless form samples.
    Unfortunately, the samples doesn’t disabled the modeless form when Revit is not ready for processing requests.
    Look at the second embedded image :)

    As you can see I also selet the item in the project browser. Revit disable Extenal tools buttons, that means Revit is not ready. But the modeless form and buttons on it still enable.
    In the samples buttons are disabled when I click to one of them, i.e. when I perform my own command. It is easy to implement this behavior. But it is not that I need.
    I need something like event which will occurs when Idling state is changed.
    I tried to subscribe on PropertyChanged event for External tools button and determine when IsEnabled property is changed. It doesn’t work :(
    I’ll try to find other ways to achieve that I want. If I find, I’ll tell you.
    Regards,
    Victor

  13. Dear Victor,
    Yes, I see the difference, of course.
    Please note that these samples work perfectly well and the management strategy demonstrated is perfectly valid for Revit 2012 as well.
    Looking forward to hearing what you find out.
    Maybe some Windows API support, checking the activity?
    An extreme solution might be to send Revit some kind of dummy message now and then, something that you know will be ignored, and see whether it receives the message, i.e. the queue is processed, or whether it hangs there, i.e. Revit is too busy to process its message queue.
    I’m no Windows expert, I am sure there are better ways.
    Maybe the process monitor? You can ask Windows how busy a process is, even via .NET, probably.
    Good luck!
    Cheers, Jeremy.

  14. Hi, Jeremy.
    Thanks for you thoughts.
    In Revit 2012 works ModelessForm_IdlingEvent sample only. Obviously, ModelessForm_ExternalEvent could not work.
    Now also I have no any ideas how to determine is revit idling or not. I’ll try to implement it later. After my vacation.
    Have a nice day, Victor.

  15. Dear Victor,
    Right you are, of course.
    Good luck, happy holidays, and best wishes for you and your family!
    Cheers, Jeremy.

Leave a Reply to Jeremy TammikCancel reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading