Xamarin.Forms - 主/细节页面和导航历史问题

10

我有一个应用程序,它使用主从页面在所有页面显示菜单。在我的应用程序中,导航有两种方式,一种是从菜单中,另一种是从仪表板中。因此,如果我导航到另一页,然后按“返回”按钮,应用程序会关闭。它不会记住导航历史记录。 主从页面如下:

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

有什么办法可以克服这个问题吗?

3个回答

18

就像@Sten-Petrov所说的那样:您正在替换详细页面,而不触发历史机制。要触发历史机制,您需要在详细页面的导航属性上执行PushAsync(Page)。

在您的示例中,更改NavigateTo:

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

这不会替换内容,而是会打开一个带有您想要的后退按钮功能的新页面。

如果您想在主细节页面中具有后退按钮功能,则需要自定义返回堆栈过程,但我认为这并不值得。在那种情况下,只需移动到不同的页面/导航结构即可。


这正是我需要解决问题的东西 - 谢谢!! - Tom Miller

4
这里的问题在于您没有使用导航堆栈来执行页面的转换,而是替换了自己页面上的一个项目,所以除了导航到您的MasterDetailPage的页面外,没有导航历史记录可供“返回”。
您可以通过创建一个新的MenuMasterDetail.cs类来解决问题,该类继承自MasterDetailPage并初始化菜单,然后创建MenuItem_A_Page.xaml(或.cs),它从您的公共基础类继承,在您的公共基础类中,您将使用Navigation.PushAsync(...)来在页面之间进行转换。
基础类:
public class MenuDetailPage: MasterDetailPage{
  public MenuDetailPage(): base(){
    this.Master = BuildMyMenuListHere(); // the menu items will also define navigation targets
  }
}

在计算机科学中的子类:

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"},
     }
  }
}

XAML中的子类(与上面的CS一起,减去设置“Detail”部分):

<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>
...

请更新您的App.cs,使其返回一个带有第一个主/细节页面(而不是基础页面)的导航页面:

App.cs:

public static Page GetMainPage ()
{
  return new NavigationPage(new FirstDetailWithMenuPage());
}

感谢@Sten Petrov。如果我使用导航栈使用push aync,那么问题就是菜单消失了。而我想在所有页面中保留菜单。因此,我在详细页面中加载每个页面。您能否提供一个示例或类似于您提到的方法? - SoftSan
这就是为什么我建议在基类中定义菜单,然后从那个基类继承并更改细节,以便您获得MasterDetailPage的菜单,但仍然可以使用导航栈。 - Sten Petrov
感谢@Sten Petrov提供的示例。我尝试实现,但是出现了错误,提示在Android中不支持全局使用PushAsync。我尝试使用Navigation.PushAsync()来推送页面。同时,我必须先设置详细页面,因为它不允许我仅使用主页面。详细页面需要添加到Menudetailpage()中。否则会抛出错误。 - SoftSan
嗨@Sten Petrov,非常感谢你的建议。有了您提供的代码和方向,我几乎已经接近我要实现的目标了。现在我只有一个问题,就是当您转到任何详细页面,然后按返回按钮或单击菜单时,它将首先显示第一页(在我的情况下是主页),然后再次需要单击菜单才能实际显示图标。您可以在https://github.com/softsan/MasterDetailMenu找到示例。您能否请看一下并指导我错在哪里?非常感谢您的支持。此外,在iOS上我无法看到菜单。 - SoftSan
嗨@Sten Petrov - 你在那个样本中发现了什么问题吗? - SoftSan

0

我曾经遇到同样的问题,Detail.Navigation.PushAsync(itemSelected) 会使得汉堡菜单消失,并且创建另一个子类来保留它似乎对代码和性能都有很大的影响。因此,我决定为主从页面使用自己的堆栈数据类型。虽然在跟踪和编码方面有些棘手,但是现在已经可以正常工作了。

在应用程序加载时初始化它,并将每个选定的项目推送到堆栈顶部的新页面中。

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

        }

在同一页面的代码后台覆盖 OnBackButtonPressed()

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接