Xamarin 表单导航 PopAsync 无法正常工作
Xamarin Forms Navigation PopAsync isn't working
我正在 Xamarin.Forms 开发应用程序,一切进展顺利,直到我 运行 遇到导航错误。令我困惑的是,我已经在其他页面上成功地使用了相同的代码调用,但突然在这个页面上,它不起作用了。
我设计了一些独特的导航流程,因为我试图实现视觉效果,使用具有两层导航页面的主从组合,使用普通的推送/弹出代码。我遵循了 this article on medium.com 的建议。
该应用初始化一个名为 "Root Navigation" 的主页,该主页初始化主页面和详细信息页面。
public App()
{
InitializeComponent();
MainPage = new RootNavigation();
}
根导航页面:
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);
}
为了在应用程序中向前导航,我使用了一种名为 "set as detail page," 的方法,它弥合了主从行为和导航推送/弹出行为之间的差距。
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;
}
然后,向后导航调用以下两种方法之一:"BackButton_Clicked()" 或 "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();
}
显示只读数据的所有页面都已顺利导航。完成页面后,我让它调用 MessagingCenter,根导航接收消息并执行所需的导航。例如,"MenuPage_ItemSelected" 事件处理程序触发代码:
if (e.SelectedItem.ToString().ToLower() == "log out")
{
Device.InvokeOnMainThreadAsync(() =>
{
InnerPage.PopToRootAsync();
this.IsPresented = false;
});
}
这似乎工作正常,在我花了一段时间研究当辅助页面在后台线程上调用 'pop to root' 时,我必须在主线程上调用它。
好的,终于到了问题页面:"update job notes" 页面。该页面引发对消息中心的调用,根导航页面将其拾取并执行以下代码:
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();
}
单步执行代码,它正确执行 SaveNewJobNote()
和 returns true。然后,它等待显示警报 "Saved Changes"。然后,代码会陷入 "ReturnToJobList()," 的无限 while 循环,永远打印到调试输出 [0:] 'Application.Views.UpdateJobNotesPage' attempting Navigation.Pop()
中。大约一百万个周期后,我厌倦了等待并退出调试器。我似乎无法做任何事情来让页面消失!
我尝试了很多东西来研究 PopAsync
和 PopModalAsync
之间的差异。在检查了不同页面的导航堆栈上的内容之后,一切看起来都和我期望的一模一样——模态堆栈上没有任何东西(因为我从来没有在任何东西上调用 PushModalAsync
),0 上RootNavigation
堆栈,OuterPage
堆栈上有 1 个,InnerPage
堆栈上有 4 个。这一切对我来说都很有意义,但它仍然没有弹出更新作业注释页面。我还尝试了 Navigation.RemovePage(page)
的代码,但没有成功。唯一的区别是调试器包括打印关于我的代码的警告并建议我改用 PopAsync()
。
我还尝试了一些不同的方法,从 this.Navigation
、this.Outer
、this.Outer.Navigation
、this.Inner
、this.Inner.Navigation
发出 PopAsync()
调用,都没有成功。
我已经查看了 Stack Overflow 上的许多其他问题,包括 this question and this question,但其中 none 似乎适用于这种情况。如有任何帮助,我们将不胜感激!!
我记得这件事发生在我身上。
我忘记了到底是什么原因,但我也进行了一些时髦的导航。我的问题是弹出窗口,有一次,他们正在创建一个新堆栈。所以当我弹出时,我得到了意想不到的结果。
我怀疑您还在某处创建了另一个堆栈,尤其是当您在调试器中处于 0 时。
罪魁祸首很可能潜伏在 InvokeOnMainThread() 周围。
在作业列表页面可见之前,我还没有真正弄清楚我创建的应该调用 InnerNavigation.PopAsync()
的代码块有什么问题。似乎该代码块中的所有变量都评估为我期望的值,但不知何故它似乎无法从堆栈中弹出任何内容。
但是,我确实更改了处理保存作业注释的代码块,它现在确实会从堆栈中弹出“保存作业注释”页面。
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");
}
}
我正在 Xamarin.Forms 开发应用程序,一切进展顺利,直到我 运行 遇到导航错误。令我困惑的是,我已经在其他页面上成功地使用了相同的代码调用,但突然在这个页面上,它不起作用了。
我设计了一些独特的导航流程,因为我试图实现视觉效果,使用具有两层导航页面的主从组合,使用普通的推送/弹出代码。我遵循了 this article on medium.com 的建议。
该应用初始化一个名为 "Root Navigation" 的主页,该主页初始化主页面和详细信息页面。
public App()
{
InitializeComponent();
MainPage = new RootNavigation();
}
根导航页面:
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);
}
为了在应用程序中向前导航,我使用了一种名为 "set as detail page," 的方法,它弥合了主从行为和导航推送/弹出行为之间的差距。
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;
}
然后,向后导航调用以下两种方法之一:"BackButton_Clicked()" 或 "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();
}
显示只读数据的所有页面都已顺利导航。完成页面后,我让它调用 MessagingCenter,根导航接收消息并执行所需的导航。例如,"MenuPage_ItemSelected" 事件处理程序触发代码:
if (e.SelectedItem.ToString().ToLower() == "log out")
{
Device.InvokeOnMainThreadAsync(() =>
{
InnerPage.PopToRootAsync();
this.IsPresented = false;
});
}
这似乎工作正常,在我花了一段时间研究当辅助页面在后台线程上调用 'pop to root' 时,我必须在主线程上调用它。
好的,终于到了问题页面:"update job notes" 页面。该页面引发对消息中心的调用,根导航页面将其拾取并执行以下代码:
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();
}
单步执行代码,它正确执行 SaveNewJobNote()
和 returns true。然后,它等待显示警报 "Saved Changes"。然后,代码会陷入 "ReturnToJobList()," 的无限 while 循环,永远打印到调试输出 [0:] 'Application.Views.UpdateJobNotesPage' attempting Navigation.Pop()
中。大约一百万个周期后,我厌倦了等待并退出调试器。我似乎无法做任何事情来让页面消失!
我尝试了很多东西来研究 PopAsync
和 PopModalAsync
之间的差异。在检查了不同页面的导航堆栈上的内容之后,一切看起来都和我期望的一模一样——模态堆栈上没有任何东西(因为我从来没有在任何东西上调用 PushModalAsync
),0 上RootNavigation
堆栈,OuterPage
堆栈上有 1 个,InnerPage
堆栈上有 4 个。这一切对我来说都很有意义,但它仍然没有弹出更新作业注释页面。我还尝试了 Navigation.RemovePage(page)
的代码,但没有成功。唯一的区别是调试器包括打印关于我的代码的警告并建议我改用 PopAsync()
。
我还尝试了一些不同的方法,从 this.Navigation
、this.Outer
、this.Outer.Navigation
、this.Inner
、this.Inner.Navigation
发出 PopAsync()
调用,都没有成功。
我已经查看了 Stack Overflow 上的许多其他问题,包括 this question and this question,但其中 none 似乎适用于这种情况。如有任何帮助,我们将不胜感激!!
我记得这件事发生在我身上。 我忘记了到底是什么原因,但我也进行了一些时髦的导航。我的问题是弹出窗口,有一次,他们正在创建一个新堆栈。所以当我弹出时,我得到了意想不到的结果。
我怀疑您还在某处创建了另一个堆栈,尤其是当您在调试器中处于 0 时。
罪魁祸首很可能潜伏在 InvokeOnMainThread() 周围。
在作业列表页面可见之前,我还没有真正弄清楚我创建的应该调用 InnerNavigation.PopAsync()
的代码块有什么问题。似乎该代码块中的所有变量都评估为我期望的值,但不知何故它似乎无法从堆栈中弹出任何内容。
但是,我确实更改了处理保存作业注释的代码块,它现在确实会从堆栈中弹出“保存作业注释”页面。
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");
}
}