I want the user to be able to move products from one location to another. When done, I want to go back to the products page because he's likely to keep moving products from the same location. I've got two slightly different scenarios.
Scenario 1:
//SourceAisle/SourceLocation/Products/TargetAisle/TargetLocation
Scenario 2:
//SourceAisle/SourceLocation/Products/FavoriteLocation
In both scenarios when the user is done I want to go back to
//SourceAisle/SourceLocation/Products
I could do Shell.Current.GoToAsync("../..")
in scenario 1 and Shell.Current.GoToAsync("..")
in scenario 2 but I'd like to have some shared logic ie go back to products page.
According to the documentation it's possible to search backwards using either /route or ///route but I'm not sure I understand how it works. I don't want to "push" any new page, I basically want to "pop" all pages up to the page I want to be displayed.
Is it possible using Shell navigation?
NOTES
I had created an extension methods in an older Xamarin project that does exactly the above but I'd prefer not to use it if the Shell provides a native way to do so.
// await Shell.Current.Navigation.PopToAsync<ProductsPage>();
internal static class NavigationExtensions
{
private static readonly SemaphoreSlim _semaphore = new (1, 1);
public static async Task PopToAsync<T>(this INavigation nav, Page nextPage = null, bool animated = true) where T : Page
{
await _semaphore.WaitAsync();
try
{
bool exists = nav.NavigationStack
.Where((p, i) => i <= nav.NavigationStack.Count - 2)
.Any(p => p is T);
if (!exists)
throw new ArgumentException($"The specified page {typeof(T).Name} does not exist in the stack");
for (var index = nav.NavigationStack.Count - 2; index > 0; index--)
{
if (nav.NavigationStack[index] is T)
break;
nav.RemovePage(nav.NavigationStack[index]);
}
if (nextPage is not null)
nav.InsertPageBefore(nextPage, nav.NavigationStack[nav.NavigationStack.Count - 1]);
await nav.PopAsync(animated);
}
finally
{
_semaphore.Release();
}
}
}
EDIT
AppShell.xaml
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="MyApp.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:MyApp.Views">
<FlyoutItem Route="SourceAisle" Title="Stock Movement">
<ShellContent ContentTemplate="{DataTemplate views:AislesPage}" />
</FlyoutItem>
</Shell>
AppShell.xaml.cs
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute("SourceAisle", typeof(AislesPage));
Routing.RegisterRoute("SourceLocation", typeof(LocationsPage));
Routing.RegisterRoute("Products", typeof(ProductsPage));
Routing.RegisterRoute("TargetAisle", typeof(AislesPage));
Routing.RegisterRoute("TargetLocation", typeof(LocationsPage));
Routing.RegisterRoute("FavoriteLocation", typeof(FavoritesPage));
}
}
The aisle and location pages are used for both source and target and have some state to deal with the two different cases but that's out of the scope of this question.
From LocationsViewModel I call await Shell.Current.GoToAsync("Products")
, from ProductsViewModel I call await Shell.Current.GoToAsync("TargetAisle")
and so on. I think this is what's called relative routing and not absolute, not sure if I should do things differently.