Yevhenii Yerokhin
Yevhenii Yerokhin

Reputation: 149

Maui android. Either set MainPage or override CreateWindows

In maui android app, when app was moved to back it occassionally invokes Destroy and bringing app back to front, throws an exeption:

Either set MainPage or override CreateWindows.

 protected override void OnCreate(Bundle savedInstanceState)
 {
    base.OnCreate(savedInstanceState); <- throws here
 }

i tried to override code in App class:

 protected override Window CreateWindow(IActivationState activationState)
 {
    return base.CreateWindow(activationState); <- throws here
 }

System.NotImplementedException: 'Either set MainPage or override CreateWindow.'

also tried to restore previously successfully loaded window:

 private Window _window = null;

protected override Window CreateWindow(IActivationState activationState)
{
    if (MainPage == null && _window != null)
    {
        return _window;
    }

    return _window = base.CreateWindow(activationState);
}

but app stays unresponsive after it. Tried to use in AndroidManifest.xml options:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:networkSecurityConfig="@xml/network_security_config" android:alwaysRetainTaskState="true" android:killAfterRestore="false" android:noHistory="false" android:excludeFromRecents="false" android:persistent="true" android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
none of that had a way to prevent from destroying app. Part of my StartupClass:
    public static MauiApp CreateMauiApp()
{
    MauiAppBuilder builder = MauiApp.CreateBuilder();

    builder.UsePrismApp<App>(prism =>
    {
        prism.RegisterTypes(containerRegistry =>
        {
            //list of pages
            containerRegistry.RegisterForNavigation<NavigationPage>();
            ....
        })
        .OnAppStart(async (containerProvider, navigationService) =>
        {
            var navService = containerProvider.Resolve<INavigationService>();
            await navService.NavigateAsync(nameof(MyPage));
        });

here i use prism over maui. So is there any way to prevent app from destroying or to successfully restore app state on recreate?

Upvotes: 7

Views: 7226

Answers (6)

bebenlebricolo
bebenlebricolo

Reputation: 73

Well, as seen everywhere on the internet this issue is particularly unclear. I found a solution to this problem I had, migrating from MAUI 8 packages to MAUI 9.0.1. Have a look at the code samples from github :

namespace DeveloperBalance;

public partial class App : Application
{
    public App()
    {
        InitializeComponent();
    }

    protected override Window CreateWindow(IActivationState? activationState)
    {
        return new Window(new AppShell());
    }
}

This snippet of code was sourced from here (github maui-samples/9.0/9.0/Apps/DeveloperBalance/App.xaml.cs).

Heck, the code itself is trivial, but the doc is still quite terrible, coming from a deprecation warning we could have hopped for a more guided approach.

Hope this helps, it did the trick on my project at least.

Upvotes: 1

Ghostbird
Ghostbird

Reputation: 41

In my opinion this message is misleading in MAUI 9. If you check the underlying MAUI code, it seems there's a nicer decoupled mechanism relying on a DI-injected IWindowCreator. There an open question about the exact intended use on the MAUI Github.

The current mechanism in MAUI is somewhat ambiguous, it leaves three different approaches.

Do not decouple the main page from the app

Create the page and the window in an override of Application.CreateWindow. Call base.CreateWindow first and use the window returned by that, if any. Most likely this'll never return any window, but otherwise you'll skip some guards and window deduplication code that might be pertinent in some situations.

I do not recommend this, but it can be achieved by adding this to your Application class:

  protected override Window CreateWindow(IActivationState? activationState)
  {
    ArgumentNullException.ThrowIfNull(activationState);
    var window = base.CreateWindow(activationState) ?? new();
    // Set your main page.
    window.Page = activationState?.Context.Services.GetRequiredService<AppShell>();
    return window;
  }

Use IWindowCreator only to create the window

As the name IWindowCreator suggests, create the window in the IWindowCreator using an base.CreateWindow call in an override of Application.CreateWindow that then sets the Page of the window returned from the base call.

While this method is the most logical based on the MAUI code base, it doesn't seem to be the intended method, so I'll not give an example.

Use IWindowCreator to create both window and page

Documentation of IWindowCreator suggests that it should not only create the window, but also set its page to the main page. The documentation seems to disregard that it might be called to create subsequent non-main windows. My proposed solution below improves on the documentation in these ways:

  • I resolve the page from DI. Otherwise what's the point of decoupling?
  • I create a blank page if the IWindowCreator is used to create subsequent (and hence non-main) windows.

Register this implementation of IWindowCreator in DI with: .AddSingleton<IWindowCreator, WindowCreator>(). After doing so, you don't need to override CreateWindow or set MainPage in you Application class.

public class WindowCreator : IWindowCreator
{
  public Window CreateWindow(Application app, IActivationState? activationState)
  {
    var window = new Window();
    if (!app.Windows.Any())
    {
      // First window created shows the main page.
      // Note: Your main page class will most likely be different.
      window.Page = activationState?.Context.Services.GetRequiredService<AppShell>();
    }
    return window;
  }
}

Upvotes: 0

Gilles Prod&#39;Hon
Gilles Prod&#39;Hon

Reputation: 1

The solution is to ensure that MainPage is set on the main thread using MainThread.BeginInvokeOnMainThread, as recommended by .NET MAUI for any UI-related operations.

public App()
    {
        try
        {
            InitializeComponent();
            
            // Use MainThread to ensure MainPage is set on the main thread
            MainThread.BeginInvokeOnMainThread(() =>
            {
                this.MainPage = new AppShell();
            });
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

Upvotes: 0

FreakyAli
FreakyAli

Reputation: 16562

If this crash occurs on startup then it probably has to do with your Styles.xaml or Colors.xaml

One of these files is trying to either refer to something that does not exist or you have tried to use a StaticResource that was not created yet or does not exist.

A good way to make sure this is what is causing the issue is to put a try-catch block on your App class's InitializeComponent method and see if one of these files is throwing the exception or not.

In my case I faced this issue because i deleted all the colors and yet these colors were being referred in the styles file

Upvotes: 1

LegoCoder
LegoCoder

Reputation: 19

I encountered the same problem myself lately. I'm using Shiny.Framework. But even the sample provided by Shiny.Framework throws the exception.

I googled some more, this comment from maui repo shows me maybe this is an emulator problem. So I created a new emulator using the latest Android 33 image, and the problem went away.

I currently find that the default iamge comes with VS 2022, which is Android 29, will always throw the exception. I tried, it seems that images above android v31 works fine.

And PS:

The exception I have also had this message:

(Sample) ChildIndex <= -1, Parent: App Child: PrismWindow

So I think it has something to do with Prism. But I'm no expert on Prism or Shiny, so I can't dig any deeper. Hoper some one can solve this mystery.

Upvotes: 1

Jianwei Sun - MSFT
Jianwei Sun - MSFT

Reputation: 4332

Maui android. Either set MainPage or override CreateWindows

About override CreateWindows, you can try to use following code:

protected override Window CreateWindow(IActivationState activationState)
{
    if (this.MainPage == null)
    {
        this.MainPage = new MainPage();
    }

    return base.CreateWindow(activationState);
}

For more info you can refer to this issue: [Android] App crashes when start from background. Wish it can help you.

Upvotes: 3

Related Questions