I try developing a Revit-addin in Revit 2019 using WPF for the GUI using following minimalized code:
[STAThread]
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
currentDocument = commandData.Application.ActiveUIDocument.Document;
RevitInterface.document = currentDocument;
MessageBox.Show("start application");
var application = new Application();
application.Exit += ApplicationExit;
int returnCode = application.Run(new Window());
MessageBox.Show("application returned with: " + returnCode.ToString());
return Result.Succeeded;
}
private void ApplicationExit(object sender, ExitEventArgs e)
{
MessageBox.Show("exit event");
}
When running this minimal example as an .exe it works fine, but when I try using it from Revit as add-in, it always crashes Revit when exiting. The order of events/outputs is:
- "start application"
- <I close the window by clicking "X" in the top right>
- "exit event"
- <Revit crashes with Popup "Encountered an improper argument">
- "application returned with: 0"
- Afterwards a zombie Revit.exe process remains which needs to be killed before restarting Revit
I already tried the following:
- Removing all add-ins except for my own from C:\ProgramData\Autodesk\Revit\Addins\2019
- Trying it with Revit 2020
- Trying it from another computer
- Closing the application via application.Shutdown() instead of closing the last window
Do you have some ideas how I still can stay the course using WPF for the add-in GUI? I do prefer it over Windows Forms quite a bit.
Solved! Go to Solution.
Yes.
First of all, I hope that you are aware that you cannot access the Revit API anywhere at all, ever, except within a valid Revit API context:
Such a context is provided by Revit events, so all your code should run within a Revit event handler. One typical event handler is the external command `Execute` method.
Now, I see that your sample is not using the Revit API. On the other hand, it is trying to do things in the user interface, such as display a message at an unexpected (for Revit) point in time.
Just do not, and all your problems will (hopefully) be solved.
To make things easier, you could base your first add-ins on existing working samples.
Good luck and have fun!
Thank you for the effort to again answer a question to a "topic that has probably come up and been answered a few hundred times in the past" ^^.
However even with your link and your suggestions my problem remains. I now tried the following setup:
using System;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace eqdb_revit_addin
{
[Transaction(TransactionMode.Manual)]
public class AddinEntryPoint : IExternalCommand
{
// This is the entry point in which the add-in starts:
[STAThread]
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
IExternalEventHandler eventHandler = new MyExternalEvent();
ExternalEvent externalEvent = ExternalEvent.Create(eventHandler);
externalEvent.Raise();
return Result.Succeeded;
}
} // end class
} // end namespace
With MyExternalEvent derived from your link:
using System.Windows;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
class MyExternalEvent : IExternalEventHandler
{
public void Execute(UIApplication uiapp)
{
UIDocument uidoc = uiapp.ActiveUIDocument;
if( null == uidoc )
{
return; // no document, nothing to do
}
Document doc = uidoc.Document;
using( Transaction tx = new Transaction(doc))
{
tx.Start("MyEvent");
var application = new Application();
int returnCode = application.Run(new Window());
tx.Commit();
}
}
public string GetName()
{
return "my event";
}
}
Of course the question remains what I am still missing but I'm also asking myself if the Execute() Method in my AddinEntryPoint has not already been just that Revit event handler that you mentioned.
Thank you for your suggestion.
In the meantime I checked out some additional templates as suggested by Jeremy Tammik and found out that these appearantly do not create their own WPF Application. It seems like my suspicion was true in the sense that Revit already has its own Application running (I would not be surprised if Revit was also written with WPF). Maybe creating a new Application and shutting it down kills Revit's Application aswell.
Opening an empty window without creating a new Application around it did finally work for me! Revit survives closing that one.
Now i got a new issue:
If I create new Window by instantiating a custom class of mine which inherits from Window, the add-in crashes immediately without even executing the first line of the constructor. I get an error message like:
"Revit encountered System.IO.FileNotFoundException: The file or assembly "System.Collections,...." or a dependency of it has not been found. The system cannot find the file specified."
Here are my imports:
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
The confusing part for me is that there are absolutely no compile errors and creating the exact same window outside a Revit add-in context works flawlessly. Another similar custom window of mine shows the same behaviour.
Here is my .csproj:
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
<DebugType>portable</DebugType>
<Platforms>x64</Platforms><!-- Only target x64 systems like the Revit APIs do -->
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<Reference Include="RevitAPI">
<HintPath>C:\Program Files\Autodesk\Revit 2019\RevitAPI.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="RevitAPIUI">
<HintPath>C:\Program Files\Autodesk\Revit 2019\RevitAPIUI.dll</HintPath>
<Private>False</Private>
</Reference>
<!-- allows for ifc id generation: -->
<Reference Include="RevitAPIIFC">
<HintPath>C:\Autodesk\WI\Autodesk Revit 2019\x64\RVT\PF64\Autodesk\Root\RevitAPIIFC.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<!-- packages installed via dotnet add package: -->
<ItemGroup>
<PackageReference Include="Dapper" Version="2.0.30" />
<PackageReference Include="MySqlConnector" Version="0.61.0" />
</ItemGroup>
</Project>
I managed to get a WPF usercontrol (or window, I'm really not sure which it is) to work inside one of my Revit Add-ins by following this intro:
There is also a part 2 and a part 3 which I haven't got to yet.
And I haven't managed to get any Revit API code to run from the WPF control yet. Hopefully, that's next after I read parts 2 and 3, and some more from Jeremy's blogs, and any other info I can find.
Right now the event handlers for my WPF are in the WPF control's code, which doesn't have access to my Revit API stuff.
Anyhow, you might try looking through the link.
P.S. You do have Windows.xaml added as a reference, right?
I have managed to run WPF from Revit this way:
see this very minimal example.
see GIT here
basically you need to be aware of MVVM (Model View - View Model) structure, as this is the layer that communicates between the Interface (WPF Window) and the Revit works running on the back.
so this is what the sample does:
Now, as you have a button on a WPF thread that is connected to MVVM Layer,
However, there are some limitation to this:
for example, if you are running a very long process... the WPF window will be blocked till revit Finish its running works, hence, running a Progress bar on WPF doesn't work this way. Progress bar must be registered to a window that is running on a separate Thread from Revit. Thread.Start(...) and ofcourse should be STAThread
I hope the above gives you some light on how this works.
Hey,
thank you for your tutorial suggestions. I will certainly have a look at it.
Currently I am not using xaml generated controls, but I am considering it. That's why I currently don't have a reference to Windows.xaml.
Now to give this thread an answer:
The basic problem I see through all the examples, lies in creating a new Application to Run() the window like:
var application = new Application();
var window = new MyWindow();
int returnCode = application.Run(window);
Instead Revit seems to bring its own application with it so you can just open your windows without creating a new Application like:
var window = new MyWindow();
window.Show();
I hope I will be able to troubleshoot my other problems aswell, but these don't contribute to the original question of this thread. I thank everybody who gave me hints and tipps.
Can't find what you're looking for? Ask the community or share your knowledge.