Search code examples
maui.net-maui.shell

Maui Shell weird navigation issue with timing of ApplyQueryAttributes and Page Lifecycle


Having a bit of a weird one with Shell navigation and the timing of ApplyQueryAttributes relative to other page lifecycle events.

My app navigates to a MainPage with a menu that then navigates through other pages in the app until it reaches a page that requires a Flyout Menu (at which point I set the Shell.Current.FlyoutContent).

So

MainPage > PageA > PageB > PageWithFlyoutMenu

I'm performing the navigation as follows

MainPage to PageA: await Shell.Current.GoToAsync($"PageA", true);

Lifecycle event order: Constructor > ApplyQueryAttributes > OnAppearing > OnNavigatedTo

PageA to PageB: await Shell.Current.GoToAsync($"PageB");

Lifecycle event order: Constructor > ApplyQueryAttributes > OnAppearing > OnNavigatedTo

PageB to PageWithFlyoutMenu: Shell.Current.FlyoutContent = IPlatformApplication.Current?.Services.GetService<MenuPage>(); await Shell.Current.GoToAsync($"///PageWithFlyoutMenu-1");

Lifecycle event order: Constructor > OnAppearing > OnNavigatedTo > ApplyQueryAttributes

The issue here is that ApplyQueryAttributes is the last to be invoked.

AppShell is as follows:

<Shell
x:Class="****.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:****.Views"
xmlns:localDetail="clr-namespace:****.Views.DetailPages"
Title="****">

<ShellContent
    Title="MainPage"
    ContentTemplate="{DataTemplate local:MainPage}"
    FlyoutItemIsVisible="False"
    Route="MainPage"
    Shell.FlyoutItemIsVisible="False" />

<ShellContent
    Title="PageWithFlyoutMenu-1"
    ContentTemplate="{DataTemplate localDetail:PageWithFlyoutMenu-1}"
    Route="PageWithFlyoutMenu-1" />

<ShellContent
    Title="PageWithFlyoutMenu-2"
    ContentTemplate="{DataTemplate localDetail:PageWithFlyoutMenu-2}"
    Route="PageWithFlyoutMenu-2" />

</Shell>

And I have the following routes registered:

Routing.RegisterRoute("PageA", typeof(PageA));
Routing.RegisterRoute("PageB", typeof(PageB));

EDIT: ApplyQueryAttributes in PageB as requested in comments:

public override void ApplyQueryAttributes(IDictionary<string, object> query)
{
    Tracker.WriteLine();

    if (query.ContainsKey("MyDto"))
    {
        var myParameter = query["MyDto"] as MyDto;

        if (myParameter != null) MyId = new Guid(myParameter.MyId);
    }
    
    base.ApplyQueryAttributes(query);
}

Can anyone shed any light on why this would be?

Link to Repo


Solution

  • After some attempts, I concluded that if you want to use the default Shell structure in your project first, and then change the Shell to the Flyout structure through Shell.Current.FlyoutContent = IPlatformApplication.Current?.Services.GetService<FlyoutMenu>();, this will not work.

    This will disrupt the internal operation logic of the Shell, and therefore cause ApplyQueryAttributes not to be triggered first when the pageB jumps.

    I suggest that Mainpage, 、PageA, and PageB do not use the Shell structure, and set AppShell.xaml to the Flyout structure, add PageWithFlyoutMenu1ViewModel and PageWithFlyoutMenu2ViewModel in it, and then change the logic of jumping from pageB to PageWithFlyoutMenu1ViewModel in your project to jump from pageB to AppShell.

    The following is the general implementation steps:

    Step1: change the code in App.xaml.cs:

    Public App(MainPageViewModel mainPageViewModel)
    {
        InitializeComponent();
        MainPage = new NavigationPage(new Mainpage(mainPageViewModel));
    }
    

    Step2: Change the MainPage->PageA and PageA->PageB jump logic to the following code:

    await Navigation.PushAsync(new PageA(pageAViewModel));
    

    Step3:Set the code in the appshell to Flyout structure, which means you don't need to use FlyoutMenu anymore.

    <Shell ...>
        <FlyoutItem Title="Page With Flyout Menu_1">
           <Tab>
               <ShellContent ContentTemplate="{localDetail:PageWithFlyoutMenu1}" />
           </Tab>
        </FlyoutItem>
        <FlyoutItem Title="Page With Flyout Menu_2"">
           <Tab>
               <ShellContent ContentTemplate="{localDetail:PageWithFlyoutMenu2}" />
           </Tab>
        </FlyoutItem>
    </Shell>
    

    Step4: change the code of Navigate method in PageBViewModel.cs:

    App.Current.MainPage = new AppShell();