Xamarin.Forms - Master/detail page and navigation history issue
Asked Answered
P

3

10

I have an app which uses masterdetail page to show menu in all page. The navigation is happened in two way in my app. one from the menu and second way from Dashboard. so if i navigate to another page, and then press "BACK" button, it closes the application. It does not remember the navigation history. The master detail page is as below:

 public class RootPage : MasterDetailPage
    {
        public RootPage ()
        {
            var menuPage = new MenuPage ();

            menuPage.Menu.ItemSelected += (sender, e) => NavigateTo (e.SelectedItem as MenuItem);

            Master = menuPage;
            Detail = new NavigationPage (new ContractsPage ());
        }

        void NavigateTo (MenuItem menu)
        {
            Page displayPage = (Page)Activator.CreateInstance (menu.TargetType);
            Detail =    new NavigationPage (displayPage);
            IsPresented = false;
        }
    }

so any ideas how to overcome this problem?

Peking answered 8/1, 2015 at 9:31 Comment(0)
I
18

Like what @Sten-Petrov said: you are replacing the detail page and not triggering the history mechanism. To trigger the history mechanism you will need to do a PushAsync(Page) on the Navigation property of the Detail page.

In your example, change NavigateTo:

 void NavigateTo (MenuItem menu)
 {
     Page displayPage = (Page)Activator.CreateInstance (menu.TargetType);
     Detail.Navigation.PushAsync(displayPage);
 }

This will not replace the content but will bring up a new page with the back button functionality you want.

If you want back button functionality with the Master-Detail page then you'll need to customize the back stack process which, in my opinion, is not worth it. Just move to a different page/navigation structure in that case.

Intercalary answered 13/1, 2016 at 14:18 Comment(1)
This is exactly what I needed to solve my problem--thanks!!Salado
B
4

The issue here is you're not using the Navigation stack to perform transitions of your pages but instead replace an item on your own page, so there's no navigation history to go "back" other than the page that navigated to your MasterDetailPage.

You can resolve the problem by creating a new MenuMasterDetail.cs class that inherits MasterDetailPage and initializes a menu, then create MenuItem_A_Page.xaml (or .cs) that inherit from your common base and in your common base class you'll use Navigation.PushAsync(...) to transition between pages.

base class:

public class MenuDetailPage: MasterDetailPage{
  public MenuDetailPage(): base(){
    this.Master = BuildMyMenuListHere(); // the menu items will also define navigation targets
  }
}

subclass in CS:

public class FirstDetailWithMenuPage: MenuDetailPage{
  public FirstDetailWithMenuPage()
    : base() // this creates the menu
  {
    this.Detail = new StackLayout{  // change this however you need
      Children = {
        new Label { Text = "This is the first page" },
        new Button { Text= "Ok"},
     }
  }
}

Subclass in XAML (along with the CS from above, minus the part where the Detail is set):

<local:FirstDetailWithMenuPage namespace:YourNamespace;assembly=YourAssemblyName" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:local="clr-n xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="FirstDetailWithMenuPage">
    <local:FirstDetailWithMenuPage.Detail>
...

Also update your App.cs to return a navigation page with the first master/detail page you have (not the base one):

App.cs:

public static Page GetMainPage ()
{
  return new NavigationPage(new FirstDetailWithMenuPage());
}
Belief answered 8/1, 2015 at 14:58 Comment(5)
thanks @Sten Petrov. If i used navigation stack using push aync then the problem is the menu is gone. while i want to keep menu in all pages. so i load each page in detail page. Can you provide a sample or something like that on the approach you mentioned?Peking
That's why my suggestion was to define the menu in the base class and then inherit from that to change the details so that you get the menu of a MasterDetailPage but you can still use the navigation stackBelief
Thanks @Sten Petrov for your sample. I tried to implement but I get an error like PushAsync is not globally supported in Android. I try to push page using Navigation.PushAsync(). Also I have to set detail page first, as it won't allow me to use only master. Detail page needs to be added in Menudetailpage(). Otherwise it throws an error.Peking
Hi @Sten Petrov Many thanks for your inputs. I am almost near to what I am trying to achieve with the code and direction provided by you. I have only one problem now is that when you go to any detail page, and then press back button or click on Menu, it will first show the FirstPage(Home in my case) and then again I need to click on menu to actual show the icon. You can find sample at github.com/softsan/MasterDetailMenu. can you please look at it and guide me what I did wrong? Thanks a lot for your support. Also, on iOS I am not able to see menu.Peking
Hi @Sten Petrov - did you find anything wrong in that sample?Peking
T
0

I was having same issue, Detail.Navigation.PushAsync(itemSelected) makes hamburger menu vanish and also creating another sub-class to retain seemed big work on code and performance. So, I decided to use my own stack datatype for Master detail page. It was bit tricky to keep track and code but working fine.

Initialize it at the app load with current Detail page and for each item selected Push new page on top of stack.

public partial class MyMasterDetailPage: MasterDetailPage
    {
        private Stack navigationStack = new Stack();
        public MyMasterDetailPage()
        {
            InitializeComponent();
            navigationStack.Push(Detail);
            try
            {
                masterPage.listView.ItemSelected += OnItemSelected;

            }
            catch (Exception exc)
            {

                System.Diagnostics.Debug.WriteLine(exc.Message);
            }

        }

Override OnBackButtonPressed() in same Page code behind

        protected override bool OnBackButtonPressed()
        {
            try
            {
                var lastPage = navigationStack.Pop();
                if (lastPage.Equals(Detail))
                    lastPage = navigationStack.Pop();

                Detail = (Page)lastPage;
                IsPresented = false;

               // to avoid app close when complete pop and new page is push on top of it 
                if (navigationStack.Count == 0) 
                    navigationStack.Push(Detail);
                return true;
            }
            catch (Exception)
            {

                return base.OnBackButtonPressed();
            }
        }
Tie answered 30/8, 2018 at 16:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.