Revit 2013 has been released, and I presented an overview of the Revit 2013 API and the new SDK samples last week.
One of the areas of enhancement is the Idling event and interaction with modeless dialogues, which also was the subject of Arnošt Löbel's Autodesk University 2011 class CP5381 on asynchronous interactions and managing modeless UI. As I mentioned, the enhancements and new functionality are demonstrated by the new ModelessForm_ExternalEvent and ModelessForm_IdlingEvent SDK samples.
Here is an overview of these enhancements from the recent ADN newsletter:
Overview
A number of features introduced in the Revit 2012 API have been enhanced in the Revit 2013 API based on the initial real-world experience gathered since the initial release of Revit 2012. For instance, this has led to the following added features related to the Idling and other events:
- Control of the Idling event repetition
- Idling event with no active document
- New external events framework
- RevitWebcam revisited
- Use of OpenAndActivateDocument in event handlers
- Further information
Idling Event Repetition
The Idling event has been changed to offer two different modes of behaviour. The default behaviour has changed, so pre-existing code that needs the original behaviour will have to explicitly select the non-default behaviour.
In the default mode, the event will be raised one single time each time Revit begins an idle session. Note that when the user is active in the Revit user interface, idle sessions begin whenever the mouse stops moving for a moment or when a command completes. However, if the user is not active in the user interface at all, Revit may not invoke additional idling sessions for quite some time; this means that an add-in may not be able to take advantage of time when the user leaves the machine completely idle for a period of time.
In the non-default mode, Revit keeps the idling session open and makes repeated calls to the event subscriber. In this mode, Revit will continue to make Idling calls even if the user is totally inactive. However, this can result in system performance degradation, because the CPU may remain fully engaged in serving Idling events.
You can specify the non-default Idling behaviour by calling the new SetRaiseWithoutDelay method on the IdlingEventArgs instance passed in to the event handler. This call has to be made each time the Idling event call-back is made. Revit will revert to the default single-call Idling behaviour as soon as this method is not called.
Idling Event with no Active Document
In contrast to the Revit 2012 behaviour, the Idling event now is invoked even when there is no document active in Revit.
External Events Framework
The Idling event enables an interaction between Revit and an external asynchronous process. The external process cannot directly invoke any Revit API methods, but it can send a request to be processed by the Idling event handler next time it is raised. One common use of this has turned out to be supporting the interaction of a modeless dialogue with Revit. In this kind of scenario, the Idling event handler never has anything to do except when such a request has been submitted, so most of the calls to it will simply return immediately.
This interaction is supported much simpler and much more efficiently by the new external events framework. It operates similarly to the Idling event with default frequency. Instead of implementing an event handler and setting up the handler to do nothing when there is no activity in your dialog, you do the following instead:
- Implement an external event, derived from the IExternalEvent interface.
- Create an instance of your external event by calling the ExternalEvent.Create method.
- When the external asynchronous process triggers an event that requires a Revit action to be taken, call the Raise method on the external event instance.
- Revit will wait for an available Idling time cycle and call the external event Execute method.
This framework is useful for modeless dialogue developers, because you can skip the default no-op implementation when nothing has happened, and just tell Revit when there is some work to do. It saves on the time required for Revit to make repeated calls to the Idling event when there is nothing to be done.
This is the complete specification of the external event interface:
// An interface to be executed when an external // event is raised. // // An instance of a class implementing this // interface will be registered with Revit first, // and every time the corresponding external event // is raised, the Execute method of this interface // will be invoked. public interface IExternalEventHandler { // Method called to handle the external event. void Execute( UIApplication app ); // String identification of the event handler. string GetName(); }
Here is an example snippet of code creating an instance of an external event and starting a new thread to drive it from the same application:
_event = ExternalEvent.Create( new WebcamEventHandler() ); Thread thread = new Thread( new ThreadStart( Run ) ); thread.Start();
Here is part of the implementation of the Run method making use of the external event and calling its Raise method to request its Execute method to be called, in which the Revit API can be used again:
/// <summary> /// External webcam event driver. /// Check regularly whether the webcam image has /// been updated. If so, update the spatial field /// primitive with the new image data. /// Currently, we only display a grey scale image. /// Colour images could be handled as well by /// defining a custom colour palette. /// </summary> static void Run() { _running = true; while( _running ) { _data = new GreyscaleBitmapData( _width, _height, _url ); byte[] hash = _data.HashValue; if( null == _lastHash || 0 != CompareBytes( hash, _lastHash ) ) { _lastHash = hash; _event.Raise(); } Thread.Sleep( _intervalMs ); } }
RevitWebcam Revisited
I implemented the RevitWebcam sample created to demonstrate use of the Idling event when it was introduced in the Revit 2011 API, and recently migrated it to Revit 2012 in preparation for making use of this new 2013 functionality.
I now performed two further steps on it:
- Enhance it with the new Idling functionality, and
- Convert it to make use of an external event instead of the Idling event.
Here is RevitWebcam_2013.zip containing the following hopefully self-descriptive subfolders, each of which in turn contains a complete Visual Studio solution:
- RevitWebcam2011
- RevitWebcam2012_1_initial_migration
- RevitWebcam2012_2_removed_warnings
- RevitWebcam2012_3_final_cleanup
- RevitWebcam2013_1_initial_migration
- RevitWebcam2013_2_removed_warnings
- RevitWebcam2013_3_idling_repetition
- RevitWebcam2013_4_external_event
- RevitWebcam2013_4_external_event_2
If you are interested in the evolution of the project, you can compare these with each other.
Please be aware that these projects are for demonstration purposes only and skip a lot of error checking. For instance, they do nothing protect themselves against the user switching or closing the active project or making other changed to the environment while they are running, so they should not be used as examples of real world application implementations.
The ModelessForm_ExternalEvent and ModelessForm_IdlingEvent SDK samples are probably a bit simpler and more safely built (by Arnošt) to handle these kind of issues :-)
Calling OpenAndActivateDocument during Events
In the Revit 2012 API, the OpenAndActivateDocument method cannot be called from inside any event handler at all. Related to the new Idling and external event functionality, it may now be invoked from within event handlers under most circumstances, specifically under the following conditions:
- From an external event if the original active document has no open transactions or transaction groups.
- From any regular event handler including Idling and the new ApplicationInitialized event if
- There is no active document open yet in Revit, and
- The event is not nested in any other event or in the execution of an external command.
In general, the new ApplicationInitialized event is more suitable than the Idling one for opening and activating the first document.
Further Information
All the information above and more is provided in the What's New section of the Revit API help file RevitAPI.chm included with the Revit SDK.
Download Revit 2013 today and see for yourself!
Hi Jeremy,
I am using Revit 2013. Default and Nondefault Idling modes are taking more time than in revit 2012 idling.Is any other way to improve performance.
Thanks,
Posted by: sangsen | April 04, 2012 at 07:34
Dear Sangsen,
That sounds interesting. Can you prove that? How?
The best thing to do would be for you to submit an ADN DevHelp Online case for this so that we can examine it in more depth.
If you are not an ADN member, please send me a minimal reproducible case by email.
To answer your question: yes, you will probably get optimal performance by converting your code to use an external event instead of the Idling one.
Cheers, Jeremy.
Posted by: Jeremy Tammik | April 04, 2012 at 07:50
Hi Jeremy..
Thanks for your response jeremy.
I found something. Project with Idling in Debugmode only revit takes time to perform any action. In release mode revit works fine. Please help me ..
Thanks
Posted by: sangsen | April 05, 2012 at 03:09
Dear Sangsen,
Glad to hear that it works fine in release mode.
I do not understand at all what problem you are having in debug mode.
I checked with the development team, and we are not aware of Idling being slower.
The older non-default mode should be virtually the same as before.
The new mode will naturally be slower, because it will respond to real idling (system Idling) event, which is not always triggered even when the end user is seemingly not doing anything. Therefore, the MultiThreading/WorkThread work-thread analysis SDK sample uses the old Idling mode.
The original internal implementation has stayed virtually untouched.
Now, when you say 'taking more time', does that mean it is not triggered as often, correct? Or is it something else?
Also, when you ask me for help, what on earth would you like me to do?
I can pray for you, but I cannot change the internal implementation of Revit :-)
Maybe you are just observing some side effects of your debugging process, which ave nothing to do with Revit at all...
Cheers, Jeremy.
Posted by: Jeremy Tammik | April 05, 2012 at 03:26
Hi Jeremy,
I'm having a problem with using the idling event, a strange think is happening and I don't understand way, maybe you can help me.
What I'm doing is, from inside the idle event I duplicate a symbol and then I use it to create a instance of it, everything works, there is no error, no exception, but when I check the result in Revit I see that the instance, witch is a MEP family, doesn't have the connector.
I did a debug on the code and I discover that the instance that is created have a null MEPModel.ConnectorManager.
This means that the Duplicate method is not working correctly during idle event or I am doing something wrong?
If I do the same thing in a command then the connector is there.
Thank you.
Posted by: Claudiu | July 23, 2012 at 10:07
Dear Claudiu,
Thank you for the report. It makes sense, potentially, although I have not heard of any similar issue before.
There could always be an error in your code, or in the Idling event behaviour, hard to say. We would need a reproducible case to test, i.e. a minimal sample model plus one-click compile-and-install Visual Studio sample app to test this.
You are of course aware that you may have to regenerate or commit the transaction before the connector manager is fully initialised?
http://thebuildingcoder.typepad.com/blog/2010/06/to-regenerate-or-not-to-regenerate.html
http://thebuildingcoder.typepad.com/blog/2010/11/setting-text-width-requires-regen.html
If that does not help and you are an ADN member, please submit an ADN DevHelp Online case for us to explore.
Cheers, Jeremy.
Posted by: Jeremy Tammik | July 27, 2012 at 07:24
Neither the ModelessForm_ExternalEvent sample project, nor the ModelessForm_IdlingEvent project work when you are viewing a 3d perspective view. Is there a solution to this pls?
Posted by: Paul Kinnane | February 12, 2013 at 05:20
Dear Paul,
What you observe is absolutely expected.
The Revit API is disabled in perspective views:
http://thebuildingcoder.typepad.com/blog/2009/06/rfa-version-grey-commands-family-context-and-rdb-link.html#2
Cheers, Jeremy.
Posted by: Jeremy Tammik | February 12, 2013 at 13:47
Hi Jeremy,
I have a problem using DetachFromCentralOption with OpenAndActivateDocument.
It returns an exception: "The file-based central model could not be reached, because e.g. the network is down or the file server is down".
Of course it cannot reach the central server, I want to detach from central. I can open the file manually and detach from central. Do you have any idea?
Code:
OpenOptions opt = new OpenOptions();
opt.DetachFromCentralOption = DetachFromCentralOption.DetachAndPreserveWorksets;
opt.AllowOpeningLocalByWrongUser = true;
app.OpenAndActivateDocument(modelpath, opt, false);
Thanks,
Christian
Posted by: Christian | September 06, 2013 at 19:29
Dear Christian,
Nope, sorry, I do not know what might be going wrong.
If it works manually, performing the exact same step programmatically should work.
Have you tried minimalising your code even further still, to ensure that you are not inadvertently toggling some option that requires access to the server?
Maybe eliminate all option settings except the DetachFromCentralOption one?
Is the model path independent of central?
All I know is this, which seems to indicate that it should work:
http://thebuildingcoder.typepad.com/blog/2012/10/detach-workset-and-taskdialog-command-link-order.html
Cheers, Jeremy.
Posted by: Jeremy Tammik | September 09, 2013 at 04:22