mysteryx93 / HanumanInstitute.MvvmDialogs

Library simplifying the concept of opening dialogs from a view model when using MVVM
MIT License
173 stars 9 forks source link

DialogService.Show in AvaloniaMauiHybrid #38

Closed metal450 closed 3 months ago

metal450 commented 3 months ago

I have an existing Avalonia app, in which MVVMDialogs works great. I'm now wrapping it in a Maui app, via AvaloniaMauiHybrid.

Why/Rationale (as an aside, just for context):

Avalonia itself doesn't support "Hot Restart" for iOS - so if you're not a Mac user, the only way to test is by connecting to a remote Mac. This is extremely slow/inefficient, plus you can only tether an iPhone to the Mac itself. AvaloniaMauiHybrid offers a great solution, as you can create a Maui iOS app and "embed" the Avalonia's MainView in it. Everything still works as before, but now you can use Hot Restart & directly tether to a Windows PC. This makes building 50 times faster, cuts out all the steps & issues with connecting to a remote Mac, lets you work while offline, etc. This Maui wrapper will only be used for iOS - the other Avalonia apps (Windows/Linux/Android) remain as-is.

The sole thing I can't figure out is how to get MVVMDialogs to work. Typically, we show the main view in Application.axaml.cs like:

public override void OnFrameworkInitializationCompleted()
{
    GC.KeepAlive(typeof(DialogService));
    DialogService.Show(null, MainWindow);
    base.OnFrameworkInitializationCompleted();
}

However, in this case, the "main" application is Maui. It starts like this:

public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder()
    .UseMauiApp<App>()
    .UseAvalonia<MyAvaloniaAppNamespace.Application>(appBuilder => {   }); // The Maui app has reference to Avalonia app's core project, & invokes its Application
    return builder.Build();
}

And then has one ContentPage, which hosts the MainView of the Avalonia app:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:am="clr-namespace:Avalonia.Maui.Controls;assembly=Avalonia.Maui"
             xmlns:ava="clr-namespace:Avalonia.Controls;assembly=Avalonia.Controls"
             xmlns:local="clr-namespace:MyAvaloniaAppNamespace;assembly=MyAvaloniaAppNamespace"
             Shell.NavBarIsVisible="False"
             x:Class="MauiWrapperIos.MainPage">

    <am:AvaloniaView HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
        <local:MainView >
            <local:MainView.DataContext>
                <local:MainViewModel />
            </local:MainView.DataContext>
        </local:MainView>
    </am:AvaloniaView>
</ContentPage>

Thus, the Avalonia MainView & ViewModel are "created" in the Maui app's xaml. The UseAvalonia() call above invokes the Avalonia core project's OnFrameworkInitializationCompleted(). To temporarily get it working (everything except MVVMDialogs), I just commented out the call to DialogService.Show(null, MainWindow).

So I'm sure you can see the dilemma. It seems like I need to embed the Avalonia main view in the Maui app's xaml...but then if I do, I can't create the view with MVVMDialogs. Can you think of any way I could get MVVMDialogs up and running in this case?

(Side note: if I do call DialogService.Show(), it will throw 'Specified method is not supported.'. Callstack: ' at Avalonia.iOS.WindowingPlatformStub.CreateWindow())

Thanks in advance for any ideas

mysteryx93 commented 3 months ago

DialogService.Show(null, MainWindow);

If I recall correctly, using this syntax for initiation is only required to enable features like triggering IViewLoaded / IViewClosing / IViewClosed. If you don't create it that way, the app should still work but those events won't be triggered in the ViewModel.

Would you just need a separate method to bind those events when the View is created differently?

metal450 commented 3 months ago

Hmm...so maybe the issue is actually something else then. Whenever I try to show a dialog (not just the initial MainView, but in the normal course of operation), I get a similar exception:

*** Terminating app due to uncaught exception 'System.NotSupportedException', reason: 'Specified method is not supported. (System.NotSupportedException)
   at Avalonia.iOS.WindowingPlatformStub.CreateWindow()
   at Avalonia.Controls.Platform.PlatformManager.CreateWindow()
   at Avalonia.Controls.Window..ctor()
   at HanumanInstitute.MvvmDialogs.Avalonia.DialogManager.GetDummyWindow()
   at HanumanInstitute.MvvmDialogs.DialogManagerBase`1.<>c__DisplayClass32_0`1.<<ShowFrameworkDialogAsync>b__0>d[[Avalonia.Controls.Conte
f27]].MoveNext()

I assumed that was because I hadn't initialized it properly from the get go, but maybe it's something else...?

mysteryx93 commented 3 months ago

It's crashing in

Avalonia.iOS.WindowingPlatformStub.CreateWindow()

Looks like something else.

Looks like an issue within Avalonia itself.

metal450 commented 3 months ago

Ah ok, will report to them then - thanks for the quick reply. I thought it might have been because I didn't initialize it properly, but I guess not :)

mysteryx93 commented 3 months ago

You can post a link to the thread here. I'd be curious to know more about the issue.

metal450 commented 3 months ago

Sure. I'm not gonna post it quite yet as I want to work through a few other things & make a standalone repro project first, but I made myself a note & will drop a link here once I do post it to them :)

mysteryx93 commented 3 months ago

Developing for iOS is in my plans; without a Mac. So I'm interested in your findings. Didn't get to even see the problem you encountered of needing a physical mac.

metal450 commented 3 months ago

Developing for iOS is in my plans; without a Mac.

Ultimately you'll still need a Mac. HotReload just makes development less of a hassle, but Apple won't let you do a build that will run on any other device, nor submit to the app store, without paying them for a physical Mac. They won't even let you run an iOS emulator without a Mac.

Apple is the worst :P