繁体   English   中英

如何检测用户是否在Xamarin.Forms的TabbedPage上请求了选项卡更改

[英]How to detect if user requested a tab change on TabbedPage in Xamarin.Forms

我有一个使用TabbedPage的Xamarin.Forms应用程序,我们将其称为T,T由3个ContentPage子项A,B和C组成。由于用户可以编辑选项卡B上的某些数据,因此我想在离开前通知用户标签以允许他取消导航更改并先保存更改,或者放弃更改并离开。 到目前为止,我已经成功重写了OnBackButtonPressed()方法和导航栏的后退按钮(它将退出TabbedPage)。 但是我很快注意到,在选项卡之间切换时,我仍在丢失更改。 我想覆盖对新选项卡的单击,因此我可以先向用户显示离开对话框,然后跳过更改或继续进行更改。 最好的方法是什么? 我目前仅在Android平台上工作,因此平台级别的解决方案也是可以接受的。

感谢您的建议和反馈:)

我认为没有简单的方法可以执行此操作,您可以对页面使用OnDissappearing和OnAppearing,这很简单。 但是我认为您使用的设计错误。 使用标签页可以使在页面之间导航变得更加容易,如果要在更改标签页时通知用户,那会很烦人。 如果您是我,我将在本地保存每个页面的数据。 因此,当您返回页面时,无论如何您将获得数据。

因此,最后,我遵循了艾哈迈德(Ahmad)的建议,并实现了将数据保留在各个选项卡上,以便在切换选项卡时不会丢失数据。 (调用OnAppearing时,我不再从模型数据中刷新输入字段)。

但是,为了知道ChildB页面上是否有未保存的更改,我必须执行以下过程:

  1. 我在ChildB页面上创建了HandleExit方法,该方法检查字段中未保存的更改(输入字段中至少有一个值与存储的模型中的值不同),并且其中一个提示用户未保存的更改(如果有的话) ),如果没有更改,则弹出导航堆栈。

      private async Task HandleExit() { if(HasUnsavedChanges()) { var action = await DisplayAlert("Alert", "There are unsaved changes, do you want to discard them?", "Discard changes", "Cancel"); if(!action) { return; } } await Navigation.PopAsync(); } 
  2. 由于用户可以通过两种方式从“选项卡式”页面返回(按设备上的“后退”按钮或按导航栏中的“后退”按钮),因此我必须:

    答:覆盖我的ChildB页面上的后退按钮方法,因此它调用HandleExit方法。 但是由于需要在UI线程上调用Navigation.PopAsync(),因此我必须在UI线程上显式执行该方法,如下所示:

     protected override bool OnBackButtonPressed() { Device.BeginInvokeOnMainThread(new Action(async () => { await HandleExit(); })); return true; } 

    B:由于无法拦截ContentPage上的导航栏后退按钮,因此我不得不在平台级别(Android)上拦截事件,然后在必要时通过MessagingCenter将事件传递给ContentPage。 因此,首先需要拦截事件,方法是在子页面之一中按下导航栏按钮并通过MessagingCenter发送事件。 我们可以这样做,但是在MainActivity.cs类中添加以下方法:

     public override bool OnOptionsItemSelected(IMenuItem item) { // check if the current item id // is equals to the back button id if (item.ItemId == 16908332) { // retrieve the current xamarin forms page instance var currentpage = Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack.LastOrDefault(); var name = currentpage.GetType().Name; if(name == "ChildA" || name == "ChildB" || name == "ChildC") { MessagingCenter.Send("1", "NavigationBack"); return false; } } return base.OnOptionsItemSelected(item); } 

    现在,只要我们在其中一个子页面(ChildA,ChildB,ChildC)中按导航栏的后退按钮,都不会发生任何事情。 但是该按钮将在其余页面上像以前一样工作。 对于解决方案的第二部分,我们需要处理来自MessagingCenter的消息,因此我们需要在ChildB页面中对其进行订阅。 我们可以在OnAppearing方法中订阅消息主题,如下所示:

     MessagingCenter.Subscribe<string>(this, "NavigationBack", async (arg) => { await HandleExit(); }); 

    请小心取消订阅OnDisappearing()中的主题,否则可能会发生奇怪的事情,因为即使从导航堆栈中弹出ContentPage,也将保留对ContentPage的引用。

  3. 现在我们已经在ChildB页面中处理了两个向后导航的请求,我们还需要在所有剩余的子页面(ChildA,ChildC)中处理它们,因此他们将知道ChildB页面中是否有未保存的更改,即使是当前未选择。 因此,该解决方案再次经过比较,分别处理了设备的后退按钮和导航栏的后退按钮,但是当我们在其余页面之一上时,我们首先想到了一种方法来检查ChildB是否存在未保存的更改,因此我们再次编写HandleExit方法,但是时间如下:

     private async Task HandleExit() { var root = (TabbedPage)this.Parent; var editPage = root.Children.Where(x => x.GetType() == typeof(ChildB)).FirstOrDefault(); if(editPage != null) { var casted = editPage as ChildB; if (casted.HasUnsavedChanges()) { var action = await DisplayAlert("Alert", "There are unsaved changes, do you want to discard them?", "Discard changes", "Cancel"); if (!action) { return; } } } await Navigation.PopAsync(); } 

    现在剩下的唯一事情就是处理其余子页面中的两个导航回事件。 它们的代码与实际的ChildB页面中的代码相同。

    答:处理设备后退按钮。

     protected override bool OnBackButtonPressed() { Device.BeginInvokeOnMainThread(new Action(async () => { await HandleExit(); })); return true; } 

    B:从MessagingCenter订阅主题

     MessagingCenter.Subscribe<string>(this, "NavigationBack", async (arg) => { await HandleExit(); }); 

如果一切都正确完成,那么如果ChildB页面上有未保存的更改,现在应该在任何子页面上提示对话框。 我希望这会在将来对某人有所帮助:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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