WinUI 3 Cant instantiate a UserControl from a location outside the application package or directory (XamlParseException)
See original GitHub issueDescribe the bug
When instantiating a UserControl from outside the application package an XamlParseException is thrown from the UserControl constructor at this.InitializeComponent()
.
Exception thrown at 0x773B35D2 (KernelBase.dll) in HostApp.exe: WinRT originate error - 0x80004005 : 'Cannot locate resource from 'ms-appx:///Plugin/PluginUserControl.xaml'.'.
Exception thrown: 'Microsoft.UI.Xaml.Markup.XamlParseException' in WinRT.Runtime.dll
WinRT information: Cannot locate resource from 'ms-appx:///Plugin/PluginUserControl.xaml'.
XAML parsing failed.
Steps to reproduce the bug
See minimal reproducing repository https://github.com/williamfigtree/WinUIPluginIssue. Key steps to recreate:
-
Create a class library “Plugin” using template “Class Library (WinUI 3 in Desktop)”
-
Add a UserControl “PluginUserControl” using template “User Control (WinUI 3)” to Plugin.csproj
-
Create an application “HostApp” using template “Black App, Packaged (WinUI 3 in Desktop)”
-
Add the “PluginLoadContext” class from this tutorial https://docs.microsoft.com/en-us/dotnet/core/tutorials/creating-app-with-plugin-support to HostApp.csproj
using System; using System.Reflection; using System.Runtime.Loader; namespace HostApp { class PluginLoadContext : AssemblyLoadContext { private AssemblyDependencyResolver _resolver; public PluginLoadContext(string pluginPath) { _resolver = new AssemblyDependencyResolver(pluginPath); } protected override Assembly Load(AssemblyName assemblyName) { string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); if (assemblyPath != null) { return LoadFromAssemblyPath(assemblyPath); } return null; } protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) { string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName); if (libraryPath != null) { return LoadUnmanagedDllFromPath(libraryPath); } return IntPtr.Zero; } } }
-
Add the following code to the App constructor. You may need to modify the path to Plugin.dll.
public App() { this.InitializeComponent(); // Attempt to load a UserControl from a plugin dll // PluginUserControl throws an XamlParsingExcpetion during instantiation // Locate plugin dll in the Plugin project bin directory var rootPath = Path.GetFullPath(@"..\..\..\..\..\..\..\..\", typeof(Program).Assembly.Location); var pluginDllPath = Path.Combine(rootPath, @"Plugin\bin\x86\Debug\net5.0-windows10.0.19041.0\Plugin.dll"); // Instantiate PluginUserControl var pluginLoadContext = new PluginLoadContext(pluginDllPath); using (pluginLoadContext.EnterContextualReflection()) { var pluginUserControl = Activator.CreateInstance("Plugin", "Plugin.PluginUserControl"); } }
-
Build Plugin.csproj
-
Deploy and debug HostApp.csproj
Expected behavior
The UserControl is instantiated and the UserControl XAML resources supplied by the external project, package, or directory are located.
Screenshots
No response
NuGet package version
No response
Windows app type
- UWP
- Win32
Device form factor
Desktop
Windows version
November 2019 Update (18363)
Additional context
This is a blocking issue for applications which require plugins which supply their own UI.
Observed the issue with WinUI 3 1.0.0-preview3.
https://github.com/microsoft/microsoft-ui-xaml/issues/3888 describes a similar requirement but does not come to an appropriate solution as it requires files from the plugin to be copied into the main package. This prevents separate build and distribution of applications and plugins.
https://github.com/microsoft/microsoft-ui-xaml/issues/1365#issuecomment-534803147 and https://docs.microsoft.com/en-us/dotnet/core/tutorials/creating-app-with-plugin-support suggest that AssemblyLoadContext is the intended mechanism for plugin support and do not indicate any separate system for XAML resources.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:9
- Comments:47 (4 by maintainers)
Top GitHub Comments
I have tried https://github.com/microsoft/microsoft-ui-xaml/issues/6299#issuecomment-1023856405 by @williamfigtree and it works for a single UserControl. However, if that UserControl contains another UserControl, then it fails.
So if you have a MainApp that tries to load a UserControlA from a AssemblyX, you would be able to load UserControlA as mentioned. However if there is another UserControlB in AssemblyX and UserControlA uses it in the XAML file, then we get XAML Parsing exception when loading the UserControlA. @RealTommyKlein do you have any suggestion or work around that you can think off.
Also, @marb2000 has mentioned that the WinUI team is concentrating on the core infrastructure this year, instead of adding more controls. Would you consider this issue/feature among core infrastructure tickets that needs to be addressed in 1.1 or 1.2? This issue is preventing us from developing a class of application (plugins with UIs). I am sure many others in the community too would like to see it addressed soon. @marb2000, @MikeHillberg, A guidance would help us immensely to determine the direction for our project.
We seem to have found a workaround which allows this to work, it requires two basic things:
Using the above two you will be able to load custom controls including xaml files which reference other custom controls, converters etc. This allows you to create lately discovered plug-ins, without related sets. So you can have a main app deployed, and you can make an optional package later which has UI and load it successfully in the app.
So basically this is already possible however it needs documentation, and the InitializeComponent code generation should be fixed so there is no need for a workaround.
Note we are using UWP + WinUI 2 with C++/WinRT, but maybe somebody can test this on WinUI 3 as well.