[BUG] Prism and MauiCompatibility are incompatibile on Windows
Description
This is a revival of https://github.com/PrismLibrary/Prism.Maui/issues/76 but with more info... it is a Prism bug, or at least a DryIoC one.
I've spent a huge amount of time digging through Prism and Maui code to locate this bug, but I don't know how you want to fix it on your side.
When using MauiCompatibility, during the lifecycle event OnLaunching, Microsoft.Maui.Controls.Compatibility.Forms.Init is called.
In this function, MauiContext.GetOptionalPlatformWindow (which is a wrapper around MauiContext.Services.GetService<Window>()) is used to try to generate a window to connect to. Since this is before the PlatformWindow is generated (that happens between OnLaunching and OnLaunched), the standard Maui IoC returns null. The Forms.Init code handles the null gracefully, does its setup and returns.
When using Prism, DryIoC does not return null in this case. It returns a Window, but that Window does not have a Native object, and thus throws a null reference error when trying to do just about anything, including check to see if the window is null.
Steps to Reproduce
- Create the Standard Prism Maui App (from Prism.Templates)
- Add MauiCompatibility()
- Run on Windows
Platform with bug
.NET MAUI
Affected platforms
Windows
Did you find any workaround?
The recommended solution in the previous issue was this:
#if WINDOWS10_0_17763_0_OR_GREATER
containerRegistry.RegisterInstance<Microsoft.UI.Xaml.Window>(Microsoft.UI.Xaml.Window.Current);
#endif
This didn't work for us, as Window.Current was null, and RegisterInstance didn't like that. Instead, we used the following, which let us run Prism and MauiCompatibility together:
#if WINDOWS10_0_17763_0_OR_GREATER
containerRegistry.RegisterSingleton<Microsoft.UI.Xaml.Window>();
#endif
However, when we used that, our app wouldn't close when we hit the X button on the window. There were still objects hanging out in memory, and I finally figured out it was because of the Forms.Init code hanging on to the singleton. Our current workaround is:
#if WINDOWS10_0_17763_0_OR_GREATER
containerRegistry.Register<Microsoft.UI.Xaml.Window>((provider)=>null);
#endif
This workaround mimics the standard Maui IoC of returning null for the window reference, and since Prism doesn't use that to generate it's PrismWindow everyone is happy. I'm not sure if this solution would still work if we were a multi-window app.
Relevant log output
No response
I'm not really seeing this as a Prism issue and you haven't provided a sample to show me any reason to the contrary. You might try using the IWindowStateManager to get the current Window though.
What example do you need? Your own template fails to load when you add MauiCompatibility. It fails because of a null reference error. It has a null reference error because DryIoC returns an invalid Window when it is asked for one before CreatePlatformWindow is called, when it should be returning null (which does not generate a null reference error from the MauiCompatibility code). Isn't DryIoC part of the Prism project?
@clnoel I have no idea what you're referring to. To be clear if you're using any templates on GitHub they aren't supported, only the templates on the private Commercial Plus feed are supported.
It has long been our policy, and it is specifically noted when you go to open a bug report that your issue will be closed without further consideration if you do not have a reproduction.
My apologies. I did assume the templates coming from this VS Extension were official. https://marketplace.visualstudio.com/items?itemName=BrianLagunas.PrismTemplatePack&ssr=false#overview
I have now made a demo app, at https://github.com/clnoel/DemoPrismMauiCompat
My example has three states, controlled by #define statements at the top of MauiProgram. Please use only one statement at a time when using my example code.
Under "BROKEN", the exception stack looks like this:
When I go into the decompiled Maui Code, I see this:
Note that it is failing in the
OnLaunching event, which happens before CreatePlatformWindow
Tracing further up the call stack, I see this:
Here, we have a call to
state.Context.GetOptionalPlatformWindow() (which is a wrapper around MauiContext.Services.GetService<Window>(), which is a dependency injection call.)
It took a great deal of tracing, but I determined that the default Maui dependency injection returned null from that call. (You can trace that yourself using the "NOPRISM" option.)
However, under DryIoC, what we get back is:
Note that, even though the Window exists, the NativeObject does not, and that is causing a whole slew of null reference errors when looking into this object, including not being able to check it for null.
My workaround substitutes null for the window object returned from MauiContext.Services.GetService<Window>() which lets this function pass.
--Christina
P.S. The link connected to the word "reproduction" in the "new bug" template doesn't actually go anywhere. I'm assuming it's supposed to connect to repo.md instead of repro.md ? or that repo.md is named wrong?
Currently we are only providing Commercial Plus subscribers with templates.
I don't really see any harm in adding this work around for WinUI apps so the next public release should have it but in the mean time you can just add this to your app to work around the issue.
#if WINDOWS
#nullable disable
container.Register<Microsoft.UI.Xaml.Window>(r => r.Resolve<IWindowManager>()
.Windows
.FirstOrDefault()
?.Handler
?.PlatformView as Microsoft.UI.Xaml.Window);
#nullable restore
#endif
If you have a Commercial Plus license and are using a more recent build than what is publicly available on NuGet.org then you should replace the Windows.FirstOrDefault() with Current
I think this is the same issue as https://github.com/PrismLibrary/Prism/issues/3213. There is a link to a sample repo in there that reproduces the problem.
We have removed the Maui.Controls.Compatibility dependency.