RevitToolkit icon indicating copy to clipboard operation
RevitToolkit copied to clipboard

Error WPF loading Revit 2025: Revit Module

Open trgiangv opened this issue 1 year ago • 8 comments

Hi @Nice3point

  • There is bug with Dependencies Isolate mechanism in Revit 2025, the others version work fine
  • I have already test on your sample project MultiProjectApplication
    • Can not execute the command with WPF implementation
    • Work fine if using IExternalCommand instead of ExternalCommand
    • The other sample MultiProjectSolution work fine with Dependency Injection
  • Can you explain me, why I got this error and way to fix it? Thank you

2024-07-29_23h29_51

trgiangv avatar Jul 29 '24 16:07 trgiangv

@trgiangv Hi, good question

This is a WPF problem at the moment

https://github.com/dotnet/wpf/issues/1700

The thing is that WPF uses the first found assembly and its context instead of the one allocated for the plugin. And if the context is different, an exception is thrown on this line:

https://github.com/dotnet/wpf/blob/7b9ea813382f4543474dc42e5dd92b28ef753c48/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Application.cs#L463

I investigated the problem and found that Module1 is loaded 2 times, in Default context and AddinContext, which throws an exception.

However, this can be fixed, just avoid loading the dependency when binding the command to the Revit ribbon. Move ExternalCommands from modules to the project containing ExternalApplication. This will prevent preloading the dependency (similar to DI) and load it only in the AddinContext, avoiding the Default.

изображение

And now it works:

изображение

Thanks for discovering it, I'll update the examples with this workaround, and wait for Microsoft to fix WPF

Nice3point avatar Aug 09 '24 13:08 Nice3point

Thank you for clarifying the issue @Nice3point

trgiangv avatar Aug 11 '24 08:08 trgiangv

I just came across this as well when registering a dockable window. Do you happen to have any useful tips for how to handle this situation?

russgreen avatar Aug 17 '24 10:08 russgreen

@russgreen

Works fine for me. Can you share your sample? Try to use DockablePaneProvider

https://github.com/user-attachments/assets/ba276ea3-3eda-40e7-861f-05a06d275c27

Nice3point avatar Aug 17 '24 19:08 Nice3point

To be fair I was still using IDockablePaneProvider to register the panel. Completely missed that you have the DockablePanelProvider. Also now tried this and my issue still occured. My addin is fine. Loads up OK and works but then I click on a command in DiRoots.One. I was getting this error.

Error Name: XamlParseException
Full Name: System.Windows.Markup.XamlParseException
Inner Exception: [A]Microsoft.Xaml.Behaviors.InvokeCommandAction cannot be cast to [B]Microsoft.Xaml.Behaviors.InvokeCommandAction. Type A originates from 'Microsoft.Xaml.Behaviors, Version=1.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' in the context '"Debug R25" Nice3point.Revit.Toolkit.Helpers.AddinLoadContext #6' at location 'D:\Development\Source\Repos\RevitResourceLibrary\source\RRL.Addin\bin\Debug R25\Microsoft.Xaml.Behaviors.dll'. Type B originates from 'Microsoft.Xaml.Behaviors, Version=1.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' in the context 'Default' at location 'C:\Program Files\Autodesk\Revit 2025\Microsoft.Xaml.Behaviors.dll'.
Exception: Set connectionId threw an exception.
OS Version: Microsoft Windows NT 10.0.22635.0

But norrowed it down. What I've found in the past is if I need to use Xaml.Behaviors in an addin Window I would sometimes get 'Could not load file or assembly 'Microsoft.Xaml.Behaviors' (https://github.com/microsoft/XamlBehaviorsWpf/issues/86) unless I add this line to the view constructor. If I disable this I don't get the error with DiRoots.

public partial class MainView : Page
{
    public MainView()
    {
        InitializeComponent();

        //enable this line of code if XAML Behaviors is used
        //var _ = new Microsoft.Xaml.Behaviors.DefaultTriggerAttribute(typeof(Trigger), typeof(Microsoft.Xaml.Behaviors.TriggerBase), null);
    }
}

I've commented out this line and disabled all other addins but cannot reproduce the old Xaml problem now either. I can find a workaround to XAML interaction logic in this panel so will do that instead.

russgreen avatar Aug 18 '24 08:08 russgreen

The found way from @CG-KhalidFathy if moving ExternalCommands didn't help https://github.com/Nice3point/RevitTemplates/issues/57

Nice3point avatar Sep 01 '24 09:09 Nice3point

the problem gets more complicated when using external UI libraries like material design toolkit or mah app even the suggested way of LoadViewFromUri might not work for all cases! i think if we can provide a way to use the old way of assembly resolver instead of AddinContext might solve these issues till Microsoft solve the issue of wpf xml parser

AmeerMansour avatar Oct 03 '24 04:10 AmeerMansour

Same issue here with a twist, with a specific plugin(AVAIL 5.2.3) installed I was actually able to reproduce this error on the newest updated multiple project solution sample as well. So far I haven't found anything special about AVAIL beside it also referenced Microsoft.extension.DI (8.0.0), but its seems to be triggering this dependency loading context error in 2025. Without AVAIL dependency isolation appears to work as intended, no duplicating module when checked using Revit lookup.

image image

For the moment I am partially navigating around this issue with the fix by @CG-KhalidFathy Here is a modified version that dynamically find the xaml path when used in the code behind

public static class IsolatedViewLoader
{
    // Used to sidestep issue in https://github.com/Nice3point/RevitToolkit/issues/7
    public static void LoadViewFromUri(this FrameworkElement view, string baseUri)
    {
        var resourceLocater = new Uri(baseUri, UriKind.Relative);
        var exprCa = (PackagePart)typeof(Application).GetMethod("GetResourceOrContentPart", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { resourceLocater });
        var stream = exprCa.GetStream();
        var uri = new Uri((Uri)typeof(BaseUriHelper).GetProperty("PackAppBaseUri", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null, null), resourceLocater);
        var parserContext = new ParserContext
        {
            BaseUri = uri
        };
        typeof(XamlReader).GetMethod("LoadBaml", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { stream, parserContext, view, true });
    }

    // Utility method to dynamically determine the URI for the XAML file and load it
    public static void LoadView<T>(this T view) where T : FrameworkElement
    {
        var uri = GetXamlUri(view.GetType());
        view.LoadViewFromUri(uri);
    }

    // Method to dynamically determine the URI for the XAML file based on the class name and namespace
    private static string GetXamlUri(Type viewType)
    {
        var assemblyName = viewType.Assembly.GetName().Name;
        var namespaceName = viewType.Namespace;
        var className = viewType.Name;
        var xamlFileName = className + ".xaml";

        // Remove the first level of the namespace
        var namespaceParts = namespaceName.Split('.');
        var adjustedNamespace = string.Join("/", namespaceParts, 1, namespaceParts.Length - 1);

        var uri = $"/{assemblyName};component/{adjustedNamespace}/{xamlFileName}";
        return uri;
    }
}

ZMPeterZhang avatar Jan 02 '25 15:01 ZMPeterZhang