By Joe Ye
From Revit 2013, there are two ways to use the Idling event. In the default mode, a single raise of the event will be made 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 your application may not be able to take advantage of time when the user leaves the machine completely for a period of time.
In the non-default mode, your application forces Revit to keep the idling session open and to make repeated calls to your event subscriber. In this mode even if the user is totally inactive the Revit session will continue to make Idling calls to your application. However, this can result in performance degradation for the system on which Revit is running because the CPU remains fully engaged in serving Idling events during the Revit application's downtime.
You can indicate the preference for the non-default Idling frequency by calling SetRaiseWithoutDelay() each time the Idling event callback is made. Revit will revert to the default Idling frequency if this method is not called every time in your callback. The above part was excerpted from Revit API help documentation.
So the first trick is to call SetRaiseWithoutDelay() method to force trigger trigger Idling event subscriber. However if the task is a lengthy process, using the above trick, the Idling event subscriber will take the full control of the Revit. Revit users cannot stop the Idling event. Obviously this will cause Revit irresponsible, and is not what you want.
You may want Revit keep doing some tasks while Revit users don’t touch Revit, however when Revit users move the cursor, stop the trigger of the Idling event. and give the control to users. In the default Idling mode, after a Idling was triggered and if cursor position keep unchanged for a while, Idling will not be triggered again until the cursor is moved again. When the cursor moved again, Revit will trigger the Idling event for one time. Using this feature, we can move the cursor in the Idling event subscriber a little bit back and forth to triggered Idling event repeatedly. If Revit users move the cursor big distance than 1 pixel, in the code we stop moving the cursor, and Idling will not be triggered. So Revit users can do their own job.
Here is the full code for the second trick.
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Windows.Forms;
using Autodesk.Revit .DB;
using Autodesk.Revit.UI;
using Autodesk.Revit .ApplicationServices;
using Autodesk.Revit.Attributes ;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.UI.Events;
[TransactionAttribute(TransactionMode.Manual)]
public class RevitCommand : IExternalCommand
{
public int callCounter = 0;
public System.Drawing.Point lastCursor
= new System.Drawing.Point(-5,-5);
public Result Execute(ExternalCommandData commandData,
ref string messages, ElementSet elements)
{
UIApplication app = commandData.Application;
Document doc = app.ActiveUIDocument.Document;
app.Idling += new EventHandler<IdlingEventArgs>(app_Idling);
return Result.Succeeded ;
}
public void app_Idling(Object sender, IdlingEventArgs arg)
{
callCounter++;
//do the short time taking task here.
Trace.WriteLine("Idling session called " + callCounter.ToString());
//When Revit users don't move the mouse
if (Math.Abs(lastCursor.X - Cursor.Position.X) <= 1)
{
//move the cursor left and right with small distance:
//1 pixel. so it looks like it is stable
//this way can trigger the Idling event repeatedly
if (callCounter % 2 == 0)
{
Cursor.Position = new System.Drawing.Point(
Cursor.Position.X + 1, Cursor.Position.Y);
}
else if (callCounter % 2 == 1)
{
Cursor.Position = new System.Drawing.Point(
Cursor.Position.X - 1, Cursor.Position.Y);
}
}
lastCursor = Cursor.Position;
}
}