Bug 45978 - NavigationPage.CurrentPage not set properly when reusing a NavigationPage instance
Summary: NavigationPage.CurrentPage not set properly when reusing a NavigationPage ins...
Status: RESOLVED FIXED
Alias: None
Product: Forms
Classification: Xamarin
Component: Forms ()
Version: 2.3.2
Hardware: PC Windows
: Highest normal
Target Milestone: ---
Assignee: Rui Marinho
URL:
Depends on:
Blocks:
 
Reported: 2016-10-26 23:10 UTC by Brian Lagunas
Modified: 2017-09-08 18:20 UTC (History)
12 users (show)

Tags: navigation currentpage ac
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description Brian Lagunas 2016-10-26 23:10:10 UTC
When reusing a NavigationPage and pushing a prebuilt navigation stack, the NavigationPage.Current is not set properly causing an incorrect order of the pages.  While the NavigationPage.NavigationStack order is correct, the actual navigation order is not correct and is in the reverse order.   The easiest way to see this issue is in the context of a MasterDetailPage and using a NavigationPage as it's Detail.

Run the following application. I'm running in AppCompat for Android.  When the application starts, expand the MasterDetail.Master by clicking on the hamburger menu.  Click on the "ViewA" button to navigate to ViewA.  As you can see we have placed MainPage in the navigation stack prior to ViewA in this process.  DO NOT CLICK THE BACK BUTTON.  Now, swipe from the left to show the MasterDetail.Master again.  Click on the "ViewB" button to navigate to VIewB, which also adds a MainPage to it's navigation stack.  Notice the Page that is currently displayed is actually the MainPage, and NOT ViewB.  Now click the back button and you will see ViewB as the  root of the NavigationPage.  This is incorrect.  MainPage should be the index 0, and ViewB should be index 1 of the navigation order.

public class App : Application
    {
        public App()
        {
            MainPage = new MainMasterDetailPage();
        }
    }

    public class MainMasterDetailPage : MasterDetailPage
    {
        NavigationPage _navPage;

        public MainMasterDetailPage()
        {
            Title = "Main";

            var btnViewA = new Button() { Text = "ViewA" };
            btnViewA.Clicked += Button_Clicked;

            var btnViewB = new Button() { Text = "ViewB" };
            btnViewB.Clicked += Button_Clicked;

            var stack = new StackLayout();
            stack.Children.Add(btnViewA);
            stack.Children.Add(btnViewB);

            var master = new ContentPage() { Title = "Master", Content = stack };

            _navPage = new NavigationPage(new OriginalRoot());

            Master = master;
            Detail = _navPage;
        }

        private async void Button_Clicked(object sender, EventArgs e)
        {
            Page newPage = null;

            var btn = (Button)sender;
            if (btn.Text == "ViewA")
                newPage = new ViewA();
            else
                newPage = new ViewB();

            //build our new navigation stack
            var newMainPage = new MainPage();
            await newMainPage.Navigation.PushAsync(newPage);

            //clear original stack if any
            await _navPage.Navigation.PopToRootAsync(false);

            //push new stack into the navigationPage
            await _navPage.Navigation.PushAsync(newMainPage);

            //remove the original navigation root instance
            var pageToRemove = _navPage.Navigation.NavigationStack[0];
            _navPage.Navigation.RemovePage(pageToRemove);

            //The CurrentPage is always MainPage
            //the currentPage should be either ViewA or ViewB, not MainPage.
            var currentPage = _navPage.CurrentPage;

            IsPresented = false;
        }
    }

    public class OriginalRoot : ContentPage
    {
        public OriginalRoot()
        {
            Title = "Root";
            Content = new Label() { Text = "Root" };
        }
    }

    public class MainPage : ContentPage
    {
        public MainPage()
        {
            Title = "MainPage";
            Content = new Label() { Text = "MainPage" };
        }
    }

    public class ViewA : ContentPage
    {
        public ViewA()
        {
            Title = "ViewA";
            Content = new Label() { Text = "View A" };
        }
    }

    public class ViewB : ContentPage
    {
        public ViewB()
        {
            Title = "ViewB";
            Content = new Label() { Text = "View B" };
        }
    }
Comment 1 adrianknight89 2016-10-31 14:39:23 UTC
I can confirm this issue. I looked at it last night and made partial progress on it. It seems the change might require more planning. When a page is pushed, first all of its navigation stack is pushed, and then page push completes. This has the side effect of setting CurrentPage to the page instead of the topmost page on its stack. The back button doesn't seem to function correctly either.
Comment 2 Paul DiPietro [MSFT] 2017-03-20 00:08:40 UTC
Still occurring as of 2.3.5.71-nightly
Comment 3 Brian Lagunas 2017-05-18 04:46:37 UTC
Is there any update on a fix?
Comment 4 Philipp Sumi 2017-05-19 09:33:35 UTC
In case this is helpful to somebody: I just skipped using CurrentPage and started using the NavigationStack directly in order to keep track of my pages.

The code below is my root page, which keeps track of the active page in the stack, and notifies my other pages and their view models on their activation status:


    public class NavigationRootPage : NavigationPage
    {
        private Page activePage;

        public NavigationRootPage()
        {
            Popped += OnPopped;
            Pushed += OnPushed;
        }

        private void OnPushed(object sender, NavigationEventArgs e)
        {
            //use the navigation stack rather than the CurrentPage property, which is buggy
            //in case of deep links (fires in wrong order)
            Page topMostPage = Navigation.NavigationStack.LastOrDefault();

            //may fire multiple times in case of deep links - just ignore then
            if (topMostPage == activePage) return;

            SetStatus(activePage, false, false);  //not active, not destroyed
            activePage = topMostPage;
            SetStatus(activePage, true, false); //active, not reentrant
        }

        private void OnPopped(object sender, NavigationEventArgs e)
        {
            SetStatus(activePage, false, true);  //not active, destroyed
            activePage = CurrentPage;
            SetStatus(activePage, true, true); //active, reentrant
        }


        private void SetStatus(Page page, bool isActive, bool destroyed)
        {
            var aas = new[] {page as IActivationAware, page?.BindingContext as IActivationAware};

            foreach (var aa in aas)
            {
                if (aa == null) continue;
                aa.IsActive = isActive;
                if (isActive)
                {
                    aa.OnActivated(destroyed);
                }
                else
                {
                    //tell the deactivated view, also whether it's just pushed on, or popped (destroyed)
                    aa.OnDeactivated(destroyed);
                }
            }
        }
    }
Comment 5 Rui Marinho 2017-06-14 12:20:05 UTC
https://github.com/PrismLibrary/Prism/pull/1082
Comment 6 Brian Lagunas 2017-06-28 00:40:12 UTC
This has been fixed with Rui's awesome PR.