Announcements

The Autodesk Community Forums has a new look. Read more about what's changed on the Community Announcements board.

Revit Add-in with WPF fails on some PCs, works perfectly on others

revitdeveloper
Contributor

Revit Add-in with WPF fails on some PCs, works perfectly on others

revitdeveloper
Contributor
Contributor

I have an Add-in that uses WPF. The WPF has 3 Dropdown selections.

 

The Problem:

On some PCs, all selection values load and display correctly in the WPF, while on other PCs, the selection remains blank.

 

This is what the WPF selection looks like (without clicking on the dropdown):

revitdeveloper_0-1721381408461.png

 

 

The strange thing is if I remove the 3rd selection then it works properly on all PCs.

 

 

This is what the WPF looks like if I remove the 3rd selection (without clicking on the dropdown):

revitdeveloper_1-1721381408463.png

 

 

 

 

 

This is the relevant part of the code from the .xaml.cs (WPF Codebehind) class:

revitdeveloper_2-1721381408472.png

 

This is the relevant part of the code from the “IFCToRevitGetPipeAndDuctTypesIExternalEvent” class:

revitdeveloper_3-1721381408483.png

 

 

 

 

 

 

This is the relevant part of the code from the “RevitFilters” class:

revitdeveloper_4-1721381408486.png

 

revitdeveloper_5-1721381408490.png

 

revitdeveloper_6-1721381408492.png

 

 

I have identified where the problem begins:

In the class “IFCToRevitGetPipeAndDuctTypesIExternalEvent” (shown in the fourth screenshot), the Execute method is not being triggered. I inserted a MessageBox within this method, but it never appeared.

 

Attempts Made Without Success:

  • Created a separate IExternalEvent specifically to populate item 3.
  • Removed all other add-ins from the affected PCs.
  • Created a second “_eventCompleted.Task” exclusively for item 3.

 

The issue persists inconsistently:

It works on some PCs running Windows 10 or 11.

It does not work on other PCs running the same versions of Windows.

 

Have you encountered a similar problem before and found a solution? This add-in is in high demand within the company, so any suggestions for resolving this issue would be greatly appreciated.

 

Please let me know if you need additional code or a more detailed explanation.

0 Likes
Reply
Accepted solutions (1)
818 Views
11 Replies
Replies (11)

ricaun
Advisor
Advisor

Did you test with the same file in multiple pcs?

 

I suppose the GetAllSystemTypes is throwing an Exception, or some Exception is happening inside the Execute.

At least if something fail in there the OnEventComplete never happen.

 

You could add a try/catch inside the Execute to show the exception if happens.

 

Did you test something like that?

 

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

0 Likes

revitdeveloper
Contributor
Contributor

Thank you for your suggestion!

 

It was tested with the same model.

You’re right, a try/catch block should have been used.

 

I will test this again on Monday when I have access to my colleague's PC.

0 Likes

revitdeveloper
Contributor
Contributor

 

I retested the code, incorporating the try-catch block, but the selection remains empty. It appears that the Execute method is not being called. This behavior is quite strange and unusual, as the code works perfectly on my PC but not on my colleague's.

0 Likes

ricaun
Advisor
Advisor

How and when are you creating the IFCToRevitGetPipeAndDuctTypesIExternalEvent?

 

When you mention that is not trigger the Execute you mean that the method Execute never starts, like if you add a message box in the start of the method that never happen I suppose.

 

I kinda have some issues in the past that the Raise never start the ExternalEvent, was related with the way I was creating the ExternalEvent, maybe that's the issue.

 

You could try to use some library to make easier to run Revit code using asynchronously Task, at least to test if fix the issue.

 

That handle the creation of the ExternalEvent with a Task embedded with timeout and others stuff.

 

 

 

 

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

0 Likes

revitdeveloper
Contributor
Contributor

I am creating the IFCToRevitGetPipeAndDuctTypesIExternalEvent in the command class:

[Transaction(TransactionMode.Manual)]
public class IFCToRevitCommand : IExternalCommand
{
    public static Document Doc { get; set; }
    public static UIDocument UiDoc { get; set; }
    private static Autodesk.Revit.ApplicationServices.Application Rapp { get; set; }
    private static ExternalCommandData CommandData { get; set; }
    private static string RefMessage { get; set; }

    private static string DocumentTitle { get; set; }


    public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
    {
        // Prepare variables
        UIApplication uiApp = commandData.Application;
        UIDocument uiDoc = uiApp.ActiveUIDocument;
        Document doc = uiDoc.Document;
        Autodesk.Revit.ApplicationServices.Application app = uiApp.Application;

        // Set property values
        CommandData = commandData;
        Doc = doc;
        UiDoc = uiDoc;
        Rapp = app;
        RefMessage = message;

        // Create ExternalEvents
        IExternalEventHandler handler_iFCToRevitGetPipeTypesIExternalEvent = new IFCToRevitGetPipeAndDuctTypesIExternalEvent();
        ExternalEvent iFCToRevitGetPipeTypesExternalEvent = ExternalEvent.Create(handler_iFCToRevitGetPipeTypesIExternalEvent);

        IExternalEventHandler handler_iFCToRevitIExternalEvent = new IFCToRevitIExternalEvent();
        ExternalEvent iFCToRevitExternalEvent = ExternalEvent.Create(handler_iFCToRevitIExternalEvent);


        // Initialise WPF
        IFCToRevitWPF iFCToRevitWPF = new IFCToRevitWPF(commandData, elements, uiDoc, uiApp, iFCToRevitGetPipeTypesExternalEvent, iFCToRevitExternalEvent);
        iFCToRevitWPF.Show();

        return Result.Succeeded;
    }
}

 

In the WPF .xaml.cs (code-behind) class,  the variables passed from the command class are initialised through the constructor. Additionally, in the constructor, I also initialize an ExternalEvent property. This allows me to raise the ExternalEvent in the "RaiseExternalEventAsync()" method of the code-behind.


public partial class IFCToRevitWPF
{
    public static UIDocument UiDoc { get; set; }
    public static Document Doc { get; set; }
    public static ExternalCommandData CommandData { get; set; }
    public static string RefMessage = "";
    public static ElementSet ElementSetProperty { get; set; }
    public static UIApplication UiApp { get; set; }

    public static string SelectedPipeType { get; set; }

    public static string SelectedDuctType { get; set; }

    public static string SelectedSystemType { get; set; }

    public static ExternalEvent IFCToRevitGetPipeTypesExternalEvent { get; set; }
    public static ExternalEvent IFCToRevitExternalEvent { get; set; }

    private TaskCompletionSource<bool> _eventCompleted;


    public IFCToRevitWPF(
        ExternalCommandData commandDataMK,
        ElementSet elementsMK,
        UIDocument uiDocMK,
        UIApplication uiAppMK,
        ExternalEvent iFCToRevitGetPipeTypesExternalEvent,
        ExternalEvent iFCToRevitExternalEvent
        )
    {

        CommandData = commandDataMK;
        ElementSetProperty = elementsMK;
        UiDoc = uiDocMK;
        Doc = uiDocMK.Document;
        UiApp = uiAppMK;
        IFCToRevitGetPipeTypesExternalEvent = iFCToRevitGetPipeTypesExternalEvent;
        IFCToRevitExternalEvent = iFCToRevitExternalEvent;

        _eventCompleted = new TaskCompletionSource<bool>();
        IFCToRevitGetPipeAndDuctTypesIExternalEvent.EventCompleted += OnEventCompleted;

        InitializeComponent();
        ApplicationThemeManager.Apply(this);

    }

    private void OnEventCompleted(object sender, EventArgs e)
    {
        _eventCompleted.TrySetResult(true);
    }

    private async void RaiseExternalEventAsync()
    {

        // Raise the external event and wait for it to complete asynchronously
        IFCToRevitGetPipeTypesExternalEvent.Raise();
        await _eventCompleted.Task;

        // Load ComboBox items after the event completes
        SelectPipeType_ComboBox.ItemsSource = IFCToRevitGetPipeAndDuctTypesIExternalEvent.PipeTypes;
        SelectDuctType_ComboBox.ItemsSource = IFCToRevitGetPipeAndDuctTypesIExternalEvent.DuctTypes;
        SelectSystemType_ComboBox.ItemsSource = IFCToRevitGetPipeAndDuctTypesIExternalEvent.SystemTypes;

    }

}

 

 

If you have more of an idea now based on what you see in my code, please let me know.


Thank you for the suggestion of the library that can handle all the async operations, this looks very cool! 


If I have the time I will look into it more, maybe it is the solution.

0 Likes

ricaun
Advisor
Advisor

Why do you have so many static properties. 😆

 

One thing you could try is to move the creation of the ExternalEvent to be inside the constructor of the Window, and make the ExternalEvent not static.

 

And you probably could remove the static Doc inside the IExternalEvent.Execute and get the document using the UIApplication.

 

By default I only pass the UIApplication in the constructor of the Window, with that I have access to the document, uidocument and so on.

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

revitdeveloper
Contributor
Contributor

I like to use properties so I can pass values to other classes but also encapsulate the variables.

 

The code now looks like this:

public partial class IFCToRevitWPF
{
    public static UIDocument UiDoc { get; set; }
    public static Document Doc { get; set; }
    public static ExternalCommandData CommandData { get; set; }
    public static string RefMessage = "";
    public static ElementSet ElementSetProperty { get; set; }
    public static UIApplication UiApp { get; set; }

    public static string SelectedPipeType { get; set; }

    public static string SelectedDuctType { get; set; }

    public static string SelectedSystemType { get; set; }

    public ExternalEvent IFCToRevitGetPipeTypesExternalEvent { get; set; }
    public ExternalEvent IFCToRevitExternalEvent { get; set; }

    private TaskCompletionSource<bool> _eventCompleted;


    public IFCToRevitWPF(
        ExternalCommandData commandDataMK,
        ElementSet elementsMK,
        UIDocument uiDocMK,
        UIApplication uiAppMK
        )
    {

        IExternalEventHandler handler_iFCToRevitGetPipeTypesIExternalEvent = new IFCToRevitGetPipeAndDuctTypesIExternalEvent();
        ExternalEvent iFCToRevitGetPipeTypesExternalEvent = ExternalEvent.Create(handler_iFCToRevitGetPipeTypesIExternalEvent);
        IFCToRevitGetPipeTypesExternalEvent = iFCToRevitGetPipeTypesExternalEvent;

        IExternalEventHandler handler_iFCToRevitIExternalEvent = new IFCToRevitIExternalEvent();
        ExternalEvent iFCToRevitExternalEvent = ExternalEvent.Create(handler_iFCToRevitIExternalEvent);
        IFCToRevitExternalEvent = iFCToRevitExternalEvent;

        CommandData = commandDataMK;
        ElementSetProperty = elementsMK;
        UiDoc = uiDocMK;
        Doc = uiDocMK.Document;
        UiApp = uiAppMK;

        _eventCompleted = new TaskCompletionSource<bool>();
        IFCToRevitGetPipeAndDuctTypesIExternalEvent.EventCompleted += OnEventCompleted;

        InitializeComponent();
        ApplicationThemeManager.Apply(this);

    }

    private void OnEventCompleted(object sender, EventArgs e)
    {
        _eventCompleted.TrySetResult(true);
    }

    private async void RaiseExternalEventAsync()
    {

        // Raise the external event and wait for it to complete asynchronously
        IFCToRevitGetPipeTypesExternalEvent.Raise();
        await _eventCompleted.Task;

        SelectPipeType_ComboBox.ItemsSource = IFCToRevitGetPipeAndDuctTypesIExternalEvent.PipeTypes;
        SelectDuctType_ComboBox.ItemsSource = IFCToRevitGetPipeAndDuctTypesIExternalEvent.DuctTypes;
        SelectSystemType_ComboBox.ItemsSource = IFCToRevitGetPipeAndDuctTypesIExternalEvent.SystemTypes;

    }

}

 

and 

 

[Transaction(TransactionMode.ReadOnly)]
public class IFCToRevitGetPipeAndDuctTypesIExternalEvent : IExternalEventHandler
{
    public static List<string> PipeTypes { get; set; }
    public static List<string> DuctTypes { get; set; }

    public static List<string> SystemTypes { get; set; }

    public static event EventHandler EventCompleted;



    public void Execute(UIApplication app)
    {
        UIDocument uiDoc = app.ActiveUIDocument;
        Document doc = uiDoc.Document;

        try
        {
            PipeTypes = RevitFilters.GetAllPipeTypesNames(doc);
            DuctTypes = RevitFilters.GetAllDuctTypesNames(doc);
            SystemTypes = RevitFilters.GetAllSystemTypesNames(doc);

            OnEventCompleted();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "IFCToRevitGetPipeAndDuctTypesIExternalEvent: Error");
        }

        
    }

    private static void OnEventCompleted()
    {
        EventCompleted?.Invoke(null, EventArgs.Empty);
    }


    public string GetName()
    {
        return "This text does not matter, only needed to execute the IExternalEventHandler: `TransferParameterValuesIExternalEvent.cs`";
    }
}

 

Unfortunately the result is the same.

0 Likes

ricaun
Advisor
Advisor

How are you executing the RaiseExternalEventAsync?

 

The Raise() return ExternalEventRequest basically a status about the ExternalEvent, do something to show if is ExternalEventRequest.Accepted.

 

Now I'm thinking that the ExternalEvent was working fine all along, add MessageBox in the start and end to make sure.

public void Execute(UIApplication app)
{
   MessageBox.Show("Start", "Execute");
...
   MessageBox.Show("End", "Execute");
}

It's strange that some pc work and other don't, odd. 😅

 

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

0 Likes

revitdeveloper
Contributor
Contributor
Accepted solution

I found the solution,

 

for whatever reason when adding the method directly:

 

 

List<string> mepSystemTypes = new FilteredElementCollector(doc)
    .OfClass(typeof(MEPSystemType))
    .Cast<MEPSystemType>()
    .Select(st => st.Name)
    .ToList();

SystemTypes = mepSystemTypes;

 

 

 

instead of calling the method from another class:

 

 

SystemTypes = RevitFilters.GetAllSystemTypesNames(doc);

 

 

 

it works, this is the full Execute method:

 

 

public void Execute(UIApplication app)
{
    try
    {
        PipeTypes = RevitFilters.GetAllPipeTypesNames(doc);
        DuctTypes = RevitFilters.GetAllDuctTypesNames(doc);

        List<string> mepSystemTypes = new FilteredElementCollector(doc)
            .OfClass(typeof(MEPSystemType))
            .Cast<MEPSystemType>()
            .Select(st => st.Name)
            .ToList();

        SystemTypes = mepSystemTypes;


        OnEventCompleted();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "IFCToRevitGetPipeAndDuctTypesIExternalEvent: Error");
    }

    
}

 

 

 

I guess it is an issue concerning the references, maybe they are not being found.

 


Nonetheless, it would be intriguing to understand why this is the case.
Do you or anyone else have any ideas?

 

 

 

0 Likes

ricaun
Advisor
Advisor

That's seems a really random fix, does not make sense.

 

You can try to replace the ExternalEvent and make your code less static and make the code less entangled.

 

Try this code with AsyncExternalEventHandler:

 

 

public partial class IFCToRevitWPF
{
    private readonly UIApplication UiApp;
    private readonly AsyncExternalEventHandler IFCToRevitGetPipeTypesExternalEvent;
    public IFCToRevitWPF(UIApplication uiAppMK)
    {
        this.UiApp = uiAppMK;
        this.IFCToRevitGetPipeTypesExternalEvent = new AsyncExternalEventHandler(ExecuteIFCToRevitGetPipeTypes);

        InitializeComponent();
    }

    private void ExecuteIFCToRevitGetPipeTypes(UIApplication uiapp)
    {
        Document document = uiapp.ActiveUIDocument.Document;
        SelectPipeType_ComboBox.ItemsSource = RevitFilters.GetAllPipeTypesNames(document);
        SelectDuctType_ComboBox.ItemsSource = RevitFilters.GetAllDuctTypesNames(document);
        SelectSystemType_ComboBox.ItemsSource = RevitFilters.GetAllSystemTypes(document);
    }

    private async void RaiseExternalEventAsync()
    {
        await IFCToRevitGetPipeTypesExternalEvent.RaiseAsync();
    }
}

 

 

You can find the code AsyncExternalEventHandler.cs here.

 

I don't know why your code works and the other approach does not, probably you are doing something strange with ExternalEvent and Revit just ignore or something...

 

 

 

 

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

0 Likes

revitdeveloper
Contributor
Contributor

Thank you again!

 

Your detailed example is much appreciated. I attemted to quickly implement your solution, but it became clear that I need more time to do so.

 

Additionally, another test was conducted:

the "RevitFilters" class, which is located in another vs project, was moved to the same vs project as the command.

The code now works as expected when implementing: 

 

 

SystemTypes = RevitFilters.GetAllSystemTypesNames(doc);

 

 

 

The reason for this issue remains unclear when using multiple VS projects.

It may be related to certain references or the asynchronous operations involved.



 

 

0 Likes