Xamarin Forms AppShell 如何返回上一页

Posted

技术标签:

【中文标题】Xamarin Forms AppShell 如何返回上一页【英文标题】:Xamarin Forms AppShell How return to previous page 【发布时间】:2022-01-16 01:29:31 【问题描述】:

我看到很多关于这类主题的主题,但似乎没有一个适用于我的问题。我没有导航页面,我有一个汉堡菜单——所以 Push/PopAsync() 似乎不是答案。而且我不想转到特定页面,我想返回上一页(无论它是什么),因此 GoToAsync() 似乎不是答案。

未来某处适用于 android 和 UWP 的 Xamarin 应用与 ios。下面的问题描述是针对Android的;它在 UWP 上的工作方式略有不同。

我想在汉堡菜单中添加一个帮助条目,它将用户带到默认浏览器中的帮助页面。汉堡菜单似乎只进入应用程序页面,所以我定义了一个“虚拟”视图页面,显示“正在加载...”并在其 OnAppearing() 方法中发出 Browser.OpenAsync() ,这非常有效。问题是用户会期望返回按钮会将他或她带到他们在单击帮助之前所在的页面。我尝试了几件事。我已经接近以下内容,但它不能正常工作:

在我的每个其他视图的 OnAppearing() 中,我调用了一个将 Current.MainPage.CurrentItem 的值保存在静态中的方法。然后在 OpenAsync() 之后的帮助页面中,我将 Current.MainPage.CurrentItem 设置为其从帮助页面之前的最后一页开始的设置。

Console.WriteLine("#### HelpPage loading Web Help");
_ = State.DisplayHelpPage(this, "MainHelp");        // _ = await Browser.OpenAsync(uri, blo);
Console.WriteLine("#### HelpPage returning to previous page");
State.ReloadPreviousPage();     // Current.MainPage).CurrentItem = lastFlyoutItem;

它几乎可以工作。第一次点击汉堡菜单中的帮助时,我得到了

#### HelpPage loading Web Help
#### HelpPage returning to previous page
#### HelpPage loading Web Help
#### HelpPage returning to previous page

网页加载完美。但是当我单击返回按钮时,它会再次显示。显然我的 OnAppearing() 方法被驱动了两次,我不明白。

如果我再次单击“返回”按钮,我会按照我的意愿返回到应用程序的上一页。下次我单击汉堡菜单中的帮助时,它会将我带到没有网页的虚拟视图页面。显然,我的 OnAppearing() 根本没有被驱动。但在那之后它完美地工作。我可以转到任何应用程序页面,然后单击菜单中的帮助。我得到了网页,“后退”按钮将我带回到应用程序和页面。在 UWP 中,浏览器当然不会加载到应用程序视图之上,而且我似乎每次都看到它被加载两次。

那么......我应该做些什么不同的事情?为什么我的 OnAppearing() 被驱动了两次,然后就完全没有了……然后就像我所期望的那样?

【问题讨论】:

见answer to a related question。这个想法是你可以用另一个路由“替换”一个路由,这样Shell会自动按预期执行“返回”。然后不需要在 OnAppearing 中做任何特别的事情。可能需要使用 WebView 来显示浏览器页面,而不是 Browser.OpenAsync。 WebView 将是一个解决方案。那么帮助视图将只是一个普通的视图页面。用户可以使用菜单导航,这很好——我不喜欢“后退”按钮。但我更愿意继续使用浏览器。更多空间用于帮助显示,我在应用程序的其他地方成功使用它(通过 ToolbarItem 点击访问)。 OnAppearing() 被驱动两次甚至在浏览器打开之前。 这两条消息在浏览器打开之前被记录,并且在我点击任何返回按钮之前很久。如果我从浏览器返回时发生这种情况,我会理解的。 我的理论是帮助应用页面不会“出现”两次。当用户点击返回按钮时,虚拟帮助应用页面将被前一个应用页面替换。 在测试中,我发现当用户从浏览器返回应用程序时,唯一可靠地被调用的方法是 App.OnResume。当前页面不会被告知离开应用程序,也不会返回到它。请参阅答案以获取解决方案。 【参考方案1】:

这个答案有几个部分:

    将上一页移至导航堆栈。这是通过拦截路由“//HelpPage”并将其替换为不是Shell 子级的路由来完成的。 记住“FakePageVisible”,所以当应用从浏览器返回时,我们知道在 OnResume 中执行“PopAsync”。 (可选)“Entering”标志可防止进入浏览器两次。

App.xaml.cs:

public partial class App : Application


    public App()
    
        InitializeComponent();

        MainPage = new AppShell();
    

    protected override void OnResume()
    
        if (HelpPage.FakePageVisible) 
            HelpPage.FakePageVisible = false;
            var shell = MainPage as AppShell;
            if (shell != null) 
                shell.Navigation.PopAsync();
            
        
    

AppShell.xaml.cs:

public partial class AppShell : Xamarin.Forms.Shell

    public AppShell()
    
        InitializeComponent();
        // Define a route that isn't a child of Shell.
        Routing.RegisterRoute("Help2", typeof(HelpPage));
    

    protected override void OnNavigating(ShellNavigatingEventArgs args)
    
        base.OnNavigating(args);

        if (args.Current != null) 
            if (args.Source == ShellNavigationSource.ShellItemChanged) 
                if (args.Target.Location.OriginalString == "//HelpPage") 
                    // Cancel the original route.
                    args.Cancel();
                    Device.BeginInvokeOnMainThread(() => 
                        // Used by the next OnAppearing.
                        HelpPage.Entering = true;
                        // Go there by a route that isn't a child of Shell.
                        // Doing so, pushes our previous location on to Navigation stack.
                        Shell.Current.GoToAsync("Help2");
                    );
                
            
        
    

HelpPage.xaml.cs:

public partial class HelpPage : ContentPage

    public static bool Entering;
    public static bool FakePageVisible;
    
    protected override void OnAppearing
    
        // Make sure this only happens once (just in case).
        if (Entering) 
            Entering = false;
            FakePageVisible = true;
            Xamarin.Essentials.Browser.OpenAsync("https://aka.ms/xamarin-quickstart");
        
    

对于一个简单的演示,此代码通过 HelpPage 中的静态变量进行通信。根据您的情况进行重构。

【讨论】:

尚未彻底测试,但它似乎工作。我实际上吸收了您答案的精髓并将其简化了。我所做的只是 OnNavigating 覆盖,并在其中取消了 Route 并加载了网页。该应用程序保留在现有页面上,但该应用程序被 Web 浏览器覆盖——直到用户按下“返回”按钮。瞧!您觉得这种方法有什么问题吗? @Charles - 这是一个很好的简化!我没有发现任何问题 - 就 shell 而言,你什么也没去 - 应用程序本身暂停然后恢复,不需要知道哪个 shell。 唯一缺少的是虚拟应用页面用来提供的“正在加载...”消息。用户盯着应用程序看了一两秒钟,想知道点击帮助是否有任何效果。所以我增强了 OnNavigation 中的代码以保存 CurrentPage.Content,将其替换为“Loading ...”标签,等待三秒钟,然后将原始内容放回原处。

以上是关于Xamarin Forms AppShell 如何返回上一页的主要内容,如果未能解决你的问题,请参考以下文章

导航栏下方的 AppShell Flyout 下方

Xamarin Forms App Shell OnPropertyChanged 未更新 FlyoutHeader 视图

Xamarin Shell 引发模糊路由匹配异常

Xamarin.Forms:如何在 Xamarin.Forms 跨平台项目中开发具有蓝牙连接的应用程序?

如何使用 Xamarin.Forms.Maps(无 Xamarin.Forms.GoogleMaps)在地图中应用样式或更改颜色

如何使用 Xamarin.Forms 创建抽屉/滑块菜单?