Xamarin Forms AppShell How return to previous page

Viewed 849

I see a lot of threads on this sort of subject but none seem to apply to my question. I don't have a navigation page, I have a hamburger menu -- so Push/PopAsync() would not appear to be the answer. And I don't want to go to a specific page, I want to go back to the previous page (whatever it was) so GoToAsync() would not appear to be the answer.

Xamarin app for Android and UWP with iOS somewhere in the future. The description of the problem below is specific to Android; it works a little differently on UWP.

I want to put a Help entry in the hamburger menu that will take the user to a help page in the default browser. Hamburger menu seems to only go to an app page, so I defined a "dummy" View page that displays "Loading ..." and issues Browser.OpenAsync() in its OnAppearing() method, and that pretty much works. The problem is that the user would expect that the Back button would take him or her to the page they were on before clicking Help. I tried a couple of things. I have gotten close with the following but it does not quite work correctly:

In each of my other Views' OnAppearing() I call a method that saves the value of Current.MainPage.CurrentItem in a static. Then in the Help page after the OpenAsync() I set Current.MainPage.CurrentItem to its setting from the last page before the Help page.

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;

It almost works. The first time I click Help in the hamburger menu I get

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

The Web page loads perfectly. But when I click the Back button it displays again. Obviously my OnAppearing() method has been driven twice, which I do not understand.

If I click the Back button again I come back to the previous page in the application just as I wanted. The next time I click Help in the Hamburger menu it takes me to my dummy View page with no Web page. Obviously, my OnAppearing() is not being driven at all. But after that it works perfectly. I can go to any app page, and click Help in the menu. I get the Web page, and the Back button takes me back to the app and the page. In UWP of course the browser does not load on top of the app Views, and I seem to see it being loaded twice every time.

So ... what should I be doing differently? And why is my OnAppearing() being driven twice and then not at all ... and thereafter as I would expect?

1 Answers

There are several parts to this answer:

  1. Get the previous page on to the Navigation stack. This is done by intercepting the Route "//HelpPage", and replacing it with a route that ISN'T a child of Shell.
  2. Remember "FakePageVisible", so we know to do "PopAsync" in OnResume, when app returns from browser.
  3. (Optional) "Entering" flag prevents going to browser twice.

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

For a simple demo, this code communicates via static variables in HelpPage. Re-factor as appropriate for your situation.

Related