Maui Shell 倒退导航

4
我希望用户能够将产品从一个位置移动到另一个位置。完成后,我希望回到产品页面,因为他很可能会继续在同一位置移动产品。这里有两种稍微不同的情况。
情景1:
//SourceAisle/SourceLocation/Products/TargetAisle/TargetLocation

场景2:

//SourceAisle/SourceLocation/Products/FavoriteLocation

在用户完成操作后,无论哪种情况,我都希望返回到原来的界面。
//SourceAisle/SourceLocation/Products

在方案1中,我可以使用 Shell.Current.GoToAsync("../.."),在方案2中使用 Shell.Current.GoToAsync(".."),但我想要一些共享的逻辑,例如返回到产品页面

根据文档,可以使用/route///route向后搜索,但我不确定它如何工作。我不想"推送"任何新页面,我基本上想要"弹出"所有页面,直到显示我想要的页面。

使用Shell导航是否可能?

我在旧的Xamarin项目中创建了一个扩展方法,完全可以实现以上操作,但如果Shell提供本地方式来实现,我更愿意不使用它。

// 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();
        }
    }
}

编辑

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));
    }
}

走道和位置页面用于源和目标,有一些状态来处理两种不同的情况,但这超出了本问题的范围。

从LocationsViewModel中调用await Shell.Current.GoToAsync("Products"),从ProductsViewModel中调用await Shell.Current.GoToAsync("TargetAisle")等等。我认为这被称为相对路由而不是绝对路由,不确定是否应该以不同的方式处理。


不确定为什么有人投票将此问题标记为“需要更多关注”。我认为这只是一个问题。如果不清楚,我想问的是如何使用Shell从任何页面返回到任何页面。给定路由//A/B/X//A/B/Y/Z,我想知道是否存在一种方法可以返回到//A/B - Arthur Rey
如果//A/B是绝对路由,它应该可以工作。问题在于,您是否有一个route设置为B的ShellContent,并且它位于一个route设置为A的内容内。说实话,您的Shell XAML将非常有帮助。(如果缺少这些,请在Shell子类中注册routes。) - H.A.H.
1
我已经看到了你的编辑。是什么阻止你调用Shell.Current.GoToAsync("//A/B")呢? - H.A.H.
Shell导航/相对路径中,阅读//route的描述:*"...匹配的页面将替换导航堆栈。"* 如果您不想继续推送更多页面,请在前面添加// - ToolmakerSteve
@H.A.H. 我的 B 视图模型有一个 QueryProperty,它是从 A 传递过来的(所以我可以从 A 中调用类似 Shell.Current.GoToAsync("B?id=10") 的东西)。从 X 或 Z 中调用 Shell.Current.GoToAsync("//A/B") 确实会跳转到页面 B,但其 QueryProperty 为空。我想这是有道理的,因为我正在替换导航堆栈。调用 Shell.Current.GoToAsync("..")Shell.Current.GoToAsync("../..") 是有效的,因为我正在返回到堆栈上的现有页面。也许我想要的并不存在,但我想将 返回 1 或 2 页 的逻辑变成 返回到 B - Arthur Rey
Arthur,如果你在MAUI代码中检查PopToRoot代码,你会看到类似这样的内容:for (int i = _currentStack.Count; i > 1; i--)循环,然后删除页面。你甚至可以检查特定平台的渲染器,看看它是如何完成的。代码有注释,你可以立即理解发生了什么。你想要//A/b返回并在堆栈中留下2页。创建一个接受参数的方法,并调用它,如果你想要这个方法可重复使用。这是我的观点。只是想指出,我正在使用Shell.Current.GoToAsync(".."),我很满意。 - H.A.H.
2个回答

0

0
你可以编写一个类似这样的命令,它可以识别页面名称,然后导航到目标页面。
 public class MyViewModel: ContentPage
    {
        public ICommand NavigateCommand { get; private set; }
        public MyViewModel()
        {
            NavigateCommand = new Command<Type>(
             async (Type pageType) =>
             {
                 Page page = (Page)Activator.CreateInstance(pageType);
                 await Navigation.PushAsync(page);
             });
        }
    }

这是命令的样式。

<TextCell Text="Entry"
          Command="{Binding NavigateCommand}"
          CommandParameter="{x:Type views:CustomizeSpecificEntryPage}" />

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接