Search code examples
dependency-injectionmauimaui-windows

How to register views in a multi-window MAUI application?


How do I correctly register a view that is used multiple times to open multiple windows?

Currently, I can only open a window that uses a view once. The second time I attempt to open another window using the same view, I get this error:

System.InvalidOperationException: 'MauiContext is null.'

The relevant code looks like this:

public partial class SubPage : ContentPage
{
    public SubPage()
    {
        InitializeComponent();
    }
}

public partial class MainPage : ContentPage
{
    readonly SubPage subPage;

    public MainPage(SubPage subPage)
    {
        InitializeComponent();
        this.subPage = subPage;
    }

    private void OnNewWindowClicked(object sender, EventArgs e)
    {
        Window newWindow = new()
        {
            Page = this.subPage,
        };
        Application.Current?.OpenWindow(newWindow);
    }
}

The views are registered in MauiProgram.cs like so:

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .Services
                .AddSingleton<MainPage>()
                .AddTransient<SubPage>();
        return builder.Build();
    }
}

Using this code, I can open a new window that uses SubPage, but the moment I try to open a second SubPage window, the MauiContext is null exception occurs.

I can "solve" the issue by not registering the view as a service, and instead creating a new instance of the view every time a window is opened. However, I am not sure if this is the right way to do things.

Window newWindow = new()
{
    Page = new SubPage(),
};
Application.Current?.OpenWindow(newWindow);

Any clues as to what I should be doing?


Solution

  • You cannot use a view instance in two places simultaneously in the UI. (AFAIK, this is true in most (all?) hierarchical UI systems, not just Maui. Internal variables need to be set, specific to one location.)

    Therefore, storing subPage in a variable and reusing it is an error. Remove that variable.

    • Your workaround is one solution, though it obviously isn't using the service you registered.

    • An alternative is to manually ask the service for an instance; AddTransient registration of the view then gives a new instance each time:

    1. See https://stackoverflow.com/a/72439742/199364 to get at Maui’s IServiceProvider and store in App class. Hopefully there will some day be a standard call, to avoid need to capture it like this.

    2. OR follow Gerald's pattern in https://stackoverflow.com/a/72439035/199364; use DI of provider in page, store the provider there:

    private IServiceProvider Provider;
    
    public MainPage(IServiceProvider serviceProvider)
    {
        Provider = serviceProvider;
    }
    
    ... Provider.GetService<SubPage>();