RevitAPI: Revit 2016 Events register/unregister behavior change

中文链接

By Aaron Lu

In previous Revit version, we can register/unregister an event in a modeless dialog, but now Revit 2016 changed the behavior of Events, it is no longer allowed to do so, otherwise exception will be thrown, if that exception is not caught by your application, Revit may crash.

Revit 2016 API changes document says:

API events – behavioral change

Although the Revit API has never officially supported such a work-flow it is now enforced that registering to and unregistering from events must happen while executing on the main thread. An exception will be thrown if an external application attempts to register to (or unregister from) events from outside of valid API context.

The solutions can be:

  • Use model dialog, or make sure you register/unregister events in the Execute function of IExternalCommand or OnStartup/OnShutdown function of IExternalApplication.
  • If you must use modeless dialog, you can use ExternalEvent.Raise() method, force the context switch to Revit main thread, and in the corresponding Execute method of IExternalEventHandler, register/unregister your events.
 

Code examples

First, have a class implementing IExternalEventHandler, and in its Execute method, register a event, e.g. Application.DocumentChanged.
public class EventRegisterHandler : IExternalEventHandler
{
public void Execute(UIApplication app)
{
app.Application.DocumentChanged += Application_DocumentChanged;
}
void Application_DocumentChanged(object sender, Autodesk.Revit.DB.Events.DocumentChangedEventArgs e)
{
// do your stuff
}
public string GetName()
{
return "EventRegisterHandler";
}
}

When you want to make it happen, just create an instance of ExternalEvent, and call its Raise() method. e.g. in a modeless dialog, clicking a button to cause the registration of the event DocumentChanged. Code:

private void button1_Click(object sender, EventArgs e)
{
ExternalEvent _exEvent = null;
EventRegisterHandler _exEventHandler = null;
_exEventHandler = new EventRegisterHandler();
_exEvent = ExternalEvent.Create(_exEventHandler);
_exEvent.Raise();
}

Edited on 2016/3/8

Someone found the above code “ExternalEvent.Create” method throws Autodesk.Revit.Exceptions.InvalidOperationException: Attempting to create an ExternalEvent outside of a standard API execution, that is a true bug in my code, sorry for that :-(. And the solution is easy: do not call “ExternalEvent.Create” in modeless dialog but in “IExternalCommand.Execute” or “IExternalApplication.OnStartup”. Following is a full code example showing that: Run ExternalCommand “EventRegistrationInModelessDialogViaExternalEvent” and then click the button in the modeless dialog to register or unregister DocumentChanged event. 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System.Windows.Forms;
namespace TestScript
{
[TransactionAttribute(TransactionMode.Manual)]
public class EventRegistrationInModelessDialogViaExternalEvent
: IExternalCommand
{
public Document doc;
public Autodesk.Revit.ApplicationServices.Application RevitApp;
ExternalEvent _exEvent;
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
EventRegisterHandler _exeventHander = new EventRegisterHandler();
_exEvent = ExternalEvent.Create(_exeventHander);
MyForm form = new MyForm();
form.ExEvent = _exEvent;
form.Show();
return Result.Succeeded;
}
}
public class MyForm : System.Windows.Forms.Form
{
public MyForm()
: base()
{
Button btn = new Button();
btn.Text = "Toggle DocumentChanged Event Registration";
btn.Click += btn_Click;
btn.Width = 250;
this.Controls.Add(btn);
}
public ExternalEvent ExEvent { get; set; }
void btn_Click(object sender, EventArgs e)
{
if (ExEvent != null)
ExEvent.Raise();
else
MessageBox.Show("external event handler is null");
}
}
public class EventRegisterHandler : IExternalEventHandler
{
public bool EventRegistered { get; set; }
public void Execute(UIApplication app)
{
if (EventRegistered)
{
EventRegistered = false;
app.Application.DocumentChanged -= Application_DocumentChanged;
}
else
{
EventRegistered = true;
app.Application.DocumentChanged += Application_DocumentChanged;
}
}
void Application_DocumentChanged(object sender,
Autodesk.Revit.DB.Events.DocumentChangedEventArgs e)
{
var sb = new StringBuilder();
var added = "added:" + e.GetAddedElementIds()
.Aggregate("", (ss, el) => ss + "," + el).TrimStart(',');
var modified = "modified:" + e.GetModifiedElementIds()
.Aggregate("", (ss, el) => ss + "," + el).TrimStart(',');
var deleted = "deleted:" + e.GetDeletedElementIds()
.Aggregate("", (ss, el) => ss + "," + el).TrimStart(',');
sb.AppendLine(added);
sb.AppendLine(modified);
sb.AppendLine(deleted);
TaskDialog.Show("Changes", sb.ToString());
}
public string GetName()
{
return "EventRegisterHandler";
}
}
}

Comments

3 responses to “RevitAPI: Revit 2016 Events register/unregister behavior change”

  1. Ca ne marche pas

  2. Il faut le mettre dans un evenement bouton pour que cela fonctionne

  3. I’d like to thank the author for writing such an insightful and informative blog post about revit that is not just useful to the readers but also revealing.

Leave a Reply

Discover more from Autodesk Developer Blog

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

Continue reading