Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Load XAML Resources from another assembly

55 views
Skip to first unread message

Chad V

unread,
Dec 5, 2024, 9:39:13 AM12/5/24
to Excel-DNA
Hey gang!
I'm trying to build an Excel-DNA add-in, with WPF support. I have the WPF functionality working, using the WinForms host component, and it's working fine as long as everything is held into the one project. 

I have a separate assembly holding some common WPF component types (MyControls), and another NuGet package that holds more WPF component types (NuGetPackage). In the central add-in project (MyAddIn), I'm referencing the two separate assemblies using dependencies, and in the .csproj file I have `<ExcelAddInInclude>MyControls.dll;NuGetPackage.dll</ExcelAddInInclude>`

In order to reference all of the resources built, I have a resource dictionary built in MyAddIn/Content/AppResources.xaml. It's just composed of the following:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <!-- App-wide resource dictionary for themes, content, styles, etc. -->
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MyControls;component/Resources/Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

MyControls/Resources/Default.xaml is just another merged resource dictionary that compiles all of the resources I want to expose to my main application.

Then, when I build a Custom Task Pane to display WPF controls building off of the common controls library, I run the following code:
public static CustomPane CreateCustomTaskPane(UserControl component, string title) {
  var dict = new ResourceDictionary() { Source = new Uri("Content/AppResources.xaml", UriKind.Relative) };  
  component.Resources.MergedDictionaries.Add(dict);
  // etc., build the component out and add it to the WinForms host component.
}

In the line trying to build the above ResourceDictionary, I get the following error informing me that it has an error while loading the Content/AppResources.xaml file - specifically that it can't find the `MyControls` assembly:
FileNotFoundException: Could not load file or assembly 'MyControls, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.

I'm seeing that the MyControls.dll is successfully being exported and sent next to the .xll output file. I've tried a few other things - such as setting Application.ResourceAssembly = Assembly.GetExecutingAssembly(), only building the ResourceDictionary once and assigning it to any newly created component - but nothing has given me any success yet.

If I need to give any further information, please let me know! Thank you!

Govert van Drimmelen

unread,
Dec 6, 2024, 8:56:05 AM12/6/24
to Excel-DNA
--------------------------------------------------
Excel-DNA is now registered on 
GitHub Sponsors.
Every monthly contribution directly funds further development.
--------------------------------------------------

Hi Chad,

I don't know the WPF resource story well enough to guess what might be wrong here.
It would help a lot if you could make a small project that exhibits the problem you see.

-Govert

Chad V

unread,
Dec 6, 2024, 4:28:34 PM12/6/24
to Excel-DNA
Thanks a lot!

Here's a Git repo with the minimal reproducible code I could figure out. On AddIn.AutoOpen(), a pane is loaded, the ResourceDictionary within the local AddIn is loaded, and then fails when it tries to references the secondary project, MyControls, and its internal Text.xaml ResourceDictionary.

Error triggers in CustomTaskPaneManager.Dictionary.get, on the line that runs `new ResourceDictionary()`.

Govert van Drimmelen

unread,
Dec 6, 2024, 5:11:17 PM12/6/24
to Excel-DNA
Hi Chad,

Thank you for setting up an example project.

I see your project targets .NET 6.
Under .NET core the isolation mechanism for add-ins is weaker than under .NET Framework, since isolated AppDomains are not available under .NET 6+.
Instead, we use an "AsssemblyLoadContext" to support some partial isolation.
However, .NET Core does not provide great help in ensuring that this isolation mechanism is used correctly, and depending on the method used to load an assembly, things can go wrong.

In your case, WPF uses an "Assembly.Load(..)" call to load the resource assembly, and this goes wrong.

AssemblyLoadException.PNG

------------------------------

The workaround for this situation is to wrap the code which will do the load into a scope where "Contextual Reflection" is enabled.
Inside such a scope, the "Assembly.Load" call works correctly.

In practice, this means modifying your AutoOpen code like this:

        public void AutoOpen()
        {
            using var scope = AssemblyLoadContext.EnterContextualReflection(typeof(AddIn).Assembly);
            // Create CTP on open
            var pane = CustomTaskPaneManager.CreateCustomTaskPane(new TestingPane(), "DEMO.");

            pane.Show();
        }

After this change, I get the pane loaded as expected.

-Govert
Reply all
Reply to author
Forward
0 new messages