Change the selected menu item on shell flyout while navigating via code

786 views Asked by At

I have a shell flyout menu on my maui app and I use routing on code to navigate as well.

<!--On AppShell.xaml-->
<ShellContent 
    Title="My Page"
    ContentTemplate="{DataTemplate views:MyPage}" />
//on AppShell.xaml.cs
Routing.RegisterRoute(nameof(MyPage), typeof(MyPage));
//On MyPageViewModel
//Command using CommunityToolKit.MVVM
[RelayCommand]
async Task GoToMyPage()
{
    await Shell.Current.GoToAsync(nameof(MyPage));
}

If I navigate via the menu, the menu behavior is as expected the selected menu item changes to the current page but when I navigate via the code the selected menu remains the previous page.

I would like to keep the navigation stack to allow the user to return to the previous page via the return button but I would also like to change the selected menu item to the current page, both to show the current page highlighted but also to allow the user to select that initial page via the menu for navigation.

For reference on the following explanation lets imagine 3 pages, InitialPage MyPage RandomPage:

Currently if I already navigated via code from InitialPage to MyPage, and try to select the InitialPage on the menu its not responding because it is selected and if I change to RandomPage via the menu, and change again to InitialPage it brings me to MyPage (because I assume is the last on the navigation stack).

I want when I first change to MyPage that the selected item on the menu reflects that change of page, and when I change back to InitialPage via the menu I want to navigate to the InitialPage.

I don't care if the navigation stack is reseted upon returning to the InitalPage or if the navigation is added to it such as: { InitialPage, MyPage, InitialPage}.

What should I change, or add, to achieve what I explained above?

1

There are 1 answers

1
Rabidgoalie On

I'm sure that you've found an answer to this problem, but I am going to post this here to help anyone else that may be encountering the same issue. I've just spent the better part of an hour and a half hunting for the solution.

I was encountering the same issue as what is describe in the original question. For me, it turned out to be due to the fact that I was registering all of my routes in AppShell.xaml.cs like this:

Routing.RegisterRoute(nameof(MainView), typeof(MainView));
Routing.RegisterRoute(nameof(SomeView), typeof(SomeView));
Routing.RegisterRoute(nameof(SomeOtherView), typeof(SomeOtherView));
Routing.RegisterRoute(nameof(YetAnotherView), typeof(YetAnotherView));
Routing.RegisterRoute(nameof(OneMoreView), typeof(OneMoreView));
Routing.RegisterRoute(nameof(LastView), typeof(LastView));

This was fine for pages that were not part of my Flyout menu, but when using Shell.Current.GoToAsync() to go to a page that is part of my Flyout menu, I was seeing the exact same issue that is described in the original question.

When I was first laying out the Flyout Menu for my app, I had the routes registered like the C# code above, and Route="SomeView" defined for the <ShellContent> that was in my flyout definitions in XAML. For me, I had to remove these route definitions in my <ShellContent> mark-up because the compiler was complaining about it by throwing an exception (I'm sorry, but I don't remember the exact exception...but I believe it was complaining about duplicate routes, or something like that). I thought that registering these routes were the same in XAML and the C# code-behind, so I chose to keep the C# and drop the XAML. That was my mistake; they are not the same.

To fix this, remove the C# route registrations in AppShell.xaml.cs for your flyout items and add the routes for these flyout items directly in the XAML definitions that you created in your AppShell.xaml file. Like this:

<!-- We want our FlyoutItem routes defined here, and ONLY here. -->
<FlyoutItem Title="Home">
    <ShellContent
        ContentTemplate="{DataTemplate local:MainView}"
        Route="MainView" />
</FlyoutItem>

<FlyoutItem Title="Some View">
    <ShellContent
        ContentTemplate="{DataTemplate local:SomeView}"
        Route="SomeView" />
</FlyoutItem>

<FlyoutItem Title="Some Other View">
    <ShellContent
        ContentTemplate="{DataTemplate local:SomeOtherView}"
        Route="SomeOtherView" />
</FlyoutItem>

Using the mock code-behind from above, our AppShell.xaml.cs would now look like this:

// We still want to keep our other route registrations
Routing.RegisterRoute(nameof(YetAnotherView), typeof(YetAnotherView));
Routing.RegisterRoute(nameof(OneMoreView), typeof(OneMoreView));
Routing.RegisterRoute(nameof(LastView), typeof(LastView));

When I removed the code-behind route registrations and added the Route parameter in my XAML file, everything just worked fine. I hope that helps someone!