I have been trying to create a Revit plugin that allows the user to view issues that are stored on a remote server on the local version of the file. It should create a new 3D perspective view from the stored data on the server and open it in Revit. However, whenever I attempt to run it, I get an exception warning thusly:
Exception thrown: 'Autodesk.Revit.Exceptions.InvalidOperationException' in RevitAPIUI.dll
An unhandled exception of type 'Autodesk.Revit.Exceptions.InvalidOperationException' occurred in RevitAPIUI.dll
Attempting to create an ExternalEvent outside of a standard API execution
I think I understand vaguely what this means, but I am unsure what exactly needs to be changed to fix it. I am defining a custom ExternalEventHandler and implementing its Execute method:
class CameraEventHandler : IExternalEventHandler
{
Issue issue;
int i;
public CameraEventHandler(Issue issue, int index)
{
this.issue = issue;
this.i = index;
}
public void Execute(UIApplication app)
{
Document doc = app.ActiveUIDocument.Document;
using (Transaction t = new Transaction(doc, "CameraTransaction"))
{
t.Start();
...
//Irrelevant code to set camera position programmatically
...
t.Commit();
}
}
public string GetName()
{
return "Camera event handler";
}
}
And then in one of my WPF forms, I create an ExternalEvent and calling the Raise method:
private void RevitViewButton_Click(object sender, RoutedEventArgs e)
{
CameraEventHandler handler = new CameraEventHandler(issue, issueIndex);
ExternalEvent cameraEvent = ExternalEvent.Create(handler);
cameraEvent.Raise();
}
However, the exception is thrown when it reaches the ExternalEvent.Create method.
Edit: I feel it is worth mentioning that the WPF app I am using is launched as a Revit plugin.
The Revit API cannot be used outside a valid Revit API context
The key is that all communication between the outside thread and Revit API must be done either inside a handler of an Idling event or an External Event.
Such a context is only available within callback methods that you provide to Revit and that Revit calls back when it is ready to accept your input.
A typical example of such a callback method is the Execute method of an external command.
Driving Revit from Outside through an External Event:
IExternalEventHandler handler_event
= new ExternalEventMy();
ExternalEvent exEvent
= ExternalEvent.Create( handler_event );
The external event handler class implements the actions I need to execute in a valid Revit API context:
class ExternalEventMy : IExternalEventHandler
{
public void Execute(UIApplication uiapp)
{
UIDocument uidoc = uiapp.ActiveDocument;
if( null == uidoc )
{
return; // no document, nothing to do
}
Document doc = uidoc.Document;
using( Transaction tx = new Transaction(doc))
{
tx.Start("MyEvent");
// Action within valid Revit API context thread
tx.Commit();
}
}
public string GetName()
{
return "my event";
}
}
I can raise the external event from another thread like this:
exEvent.Raise();
That is not a bug, Gaz, it is by design.
The solution suggested by Gaz is absolutely correct!
Reading this blog it would appear to be a bug in Revit.
The solution appears to be to create your custom handler during IExternalCommand.Execute or IExternalApplication.OnStartup, rather than at the time of raising the event.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With