简体   繁体   中英

Xamarin Forms Navigation PopAsync isn't working

I'm working on an app in Xamarin.Forms, and things have been going pretty steadily, until I ran into a navigation error. The thing that mystifies me about it is that I've already been successfully using the same code calls on other pages, but suddenly with this page, it isn't working.

I have designed a bit of a unique navigation flow because of the visual result that I'm trying to accomplish, using a combination of master-detail that has two tiers of navigation pages using the normal push / pop code. I was following suggestions from this article on medium.com .

The app initializes a main page called "Root Navigation" that initializes the master and detail pages.

public App()
{
    InitializeComponent();

    MainPage = new RootNavigation();
}

Root Navigation page:

public partial class RootNavigation : MasterDetailPage
{
    private MenuPage menuPage;
    private NavigationPage OuterPage;
    private NavigationPage InnerPage;

    public RootNavigation()
    {
        this.Master = this.menuPage = new MenuPage();
        this.menuPage.MenuItemsListView.ItemSelected += Menu_ItemSelected;

        var viewModel = new SelectEmployeeViewModel();
        var page = new SelectEmployeePage(viewModel);
        SetAsDetailPage(page);
    }

To navigate forward in the app, I'm using a method called "set as detail page," that bridges the gap between master-detail behavior and navigation push / pop behavior.

private void SetAsDetailPage(ContentPage page)
{
    NavigationPage.SetHasNavigationBar(newPage, false);
    if (newPage.GetType() == typeof(JobDetailPage))
    {
        newPage.ToolbarItems.Add(
            new ToolbarItem()
            {
                Text = "Back",
                Command = new Command(() => BackButton_Clicked())
            });
    }
    this.InnerPage = this.InnerPage ?? new NavigationPage();
    this.InnerPage.Navigation.PushAsync(page);
    this.OuterPage = this.OuterPage ?? new NavigationPage(this.InnerPage);
    this.Detail = this.Detail ?? this.OuterPage;
}

Then, navigating backward calls one of two methods: "BackButton_Clicked()" or "ReturnToJobList()".

private void ReturnToJobList()
{
    while (InnerPage.CurrentPage.GetType() != typeof(JobsPage))
    {
        var current = InnerPage.CurrentPage.ToString();
        System.Diagnostics.Debug.WriteLine($"'{current}' attempting Navigation.Pop()");
        InnerPage.PopAsync();
    }
}

private void BackButton_Clicked()
{
    this.InnerPage.PopAsync();
}

All of the pages that display read-only data have navigated without issue. When I'm done with the page, I have it raise a call to the MessagingCenter, and the root navigation receives the message and performs the desired navigation. For example, the "MenuPage_ItemSelected" event handler fires the code:

if (e.SelectedItem.ToString().ToLower() == "log out")
{
    Device.InvokeOnMainThreadAsync(() =>
    {
        InnerPage.PopToRootAsync();
        this.IsPresented = false;
    });
}

This seems to be working fine, after I spent a while researching that when a secondary page calls 'pop to root' on a background thread, I have to invoke it on the main thread.

Ok, so finally to the problem page: The "update job notes" page. The page raises a call to the Messaging Center, and the Root Navigation page picks that up and executes the following code:

private async void SaveJobNotes(ContentPage sender)
{
    if (sender is UpdateJobNotesPage notesPage)
    {
        bool result = await SaveNewJobNote(notesPage);
        var message = result ? "Saved changes" : "An error occurred; changes not saved";
        await DisplayAlert("Save", message, "OK");
    }

    ReturnToJobList();
}

Stepping through the code, it correctly executes SaveNewJobNote() and returns true. Then, it awaits displaying the alert "Saved Changes". Then, the code then gets stuck in an infinite while loop in the "ReturnToJobList()," forever printing out into the debug output [0:] 'Application.Views.UpdateJobNotesPage' attempting Navigation.Pop() . After about a million cycles I get tired of waiting and quit the debugger. I can't seem to do anything to make the page go away!

I've tried a bunch of stuff with investigating the differences between PopAsync and PopModalAsync . After checking what's on the navigation stacks for the different pages in question, everything looks exactly like what I'd expect -- there's nothing on the modal stacks for anything (because I never called PushModalAsync on anything), there's 0 on the RootNavigation stack, 1 on the OuterPage stack, and 4 on the InnerPage stack. That all makes perfect sense to me, but it still doesn't pop the Update Job Notes page. I also tried code with Navigation.RemovePage(page) with no success. The only difference there was that the debugger included printing a warning about my code and suggesting I use PopAsync() instead.

I also tried some different things with making the PopAsync() call from this.Navigation , this.Outer , this.Outer.Navigation , this.Inner , this.Inner.Navigation , all with no success.

I have already looked at a bunch of other questions on Stack Overflow including this question and this question but none of them seem to apply in this case. Any assistance would be greatly appreciated!!

I remember this was happening to me. I forget exactly what was the cause, but I had some funky navigation going on as well. My problem was around popups and at one point, they were creating a new stack. So when I popped, I was getting unexpected results.

I would suspect you are also creating another stack somewhere, especially if you are at 0 in the debugger.

The culprit is most likely lurking around that InvokeOnMainThread().

I haven't really figured out what the problem is with the code block that I created that was supposed to call InnerNavigation.PopAsync() until the Job List page was visible. It seems like all the variables in that code block evaluate to values that I'd expect, but somehow it doesn't seem to be able to pop anything off the stack.

However, I did change my code block that handles saving Job Notes, and it does now pop the Save Job Notes page off the stack.

private async void SaveJobNotes(ContentPage sender)
{
    this.InnerPage.PopAsync(); //I don't understand why this works and the 
                                //other didn't, but it correctly pops the page

    if (sender is UpdateJobNotesPage notesPage)
    {
        bool noteSaved = await SaveNewJobNote(notesPage);
        bool progressSaved = await SaveJobProgress(notesPage);
        var message = noteSaved && progressSaved ? 
            "Changes were save successfully" : 
            "An error occurred; changes not saved";
        await DisplayAlert("Save", message, "OK");
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM