【问题标题】:How to intercept Navigation Bar Back Button Clicked in Xamarin Forms?如何拦截在 Xamarin 表单中单击的导航栏后退按钮?
【发布时间】:2015-10-20 04:57:22
【问题描述】:

我有一个 xamarin 表单页面,用户可以在其中更新表单中的一些数据。如果某些数据没有保存,我需要拦截单击的导航栏返回按钮,以警告用户。如何做?

我可以使用Android.MainActivity.OnBackPressed() 拦截在 Android 中单击的硬件 Bar Back Button,但该事件仅在硬件 Bar Back Button Clicked 时引发,而不是在 Navigation Bar Back Button Clicked 时引发。

我也尝试过覆盖Xamarin.Forms.NavigationPageOnBackButtonPressed(),但它不起作用。为什么? 有人已经解决了这个问题吗?

我也试过覆盖OnDisappear(),有两个问题:

  1. 页面已经在视觉上消失了,所以“你确定吗?”对话框出现在前一页上。
  2. 无法取消后退操作。

那么,是否可以拦截导航栏后退按钮的按下?

【问题讨论】:

    标签: xamarin xamarin.forms xamarin.android xamarin-studio


    【解决方案1】:

    通过覆盖 FormsApplicationActivity 中的以下方法,我能够显示一个可以取消导航的确认对话框。

      // navigation back button
      public override bool OnOptionsItemSelected(IMenuItem item)
      {
         if (item.ItemId == 16908332)
         {
            var currentViewModel = (IViewModel)navigator.CurrentPage.BindingContext;
            currentViewModel.CanNavigateFromAsync().ContinueWith(t =>
            {
               if (t.Result)
               {
                  navigator.PopAsync();
               }
            }, TaskScheduler.FromCurrentSynchronizationContext());
            return false;
         }
         else
         {
            return base.OnOptionsItemSelected(item);
         }
      }
    
      // hardware back button
      public async override void OnBackPressed()
      {
         var currentViewModel = (IViewModel)navigator.CurrentPage.BindingContext;
    
         // could display a confirmation dialog (ex: "Cancel changes?")
         var canNavigate = await currentViewModel.CanNavigateFromAsync();
         if (canNavigate)
         {
            base.OnBackPressed();
         }
      }
    

    navigator.CurrentPage 是 INavigation 服务的包装器。我不必从模式页面取消导航,所以我只处理 NavigationStack。

    this.navigation.NavigationStack[this.navigation.NavigationStack.Count - 1];
    

    【讨论】:

    • 我建议不要使用硬编码数字,而是使用资源常量。只需将 16908332 替换为 Android.Resource.Id.Home
    【解决方案2】:

    正如@JordanMazurke 也提到的那样,最简单的方法是:

    1. NavigationPage.ShowHasBackButton(this, false)
    2. 推送Modal 而不是Page

    然后,您可以添加一个ActionbarItem,您可以在其中处理Event

    我亲自与 Xamarin 的 iOS 团队就此事进行了交谈,他们基本上告诉我,我们不应该期望在 NavigationBar 中支持处理 BackButtonPressedEvent。原因是,在 iOS 上,用户在按下Back 时接收消息是不好的做法。

    【讨论】:

    • :(这让我很伤心。
    • 如果有人想在 xaml 中禁用导航栏的后退按钮,请执行以下操作:
    【解决方案3】:

    我发现最好的方法是添加我自己的 NavigationRenderer 来拦截导航方法和一个简单的界面

    [assembly: ExportRenderer(typeof(NavigationPage), typeof(CustomerMobile.Droid.NavigationPageRenderer))]
    
    public class NavigationPageRenderer : Xamarin.Forms.Platform.Android.AppCompat.NavigationPageRenderer
    {
        public Activity context;
    
        public NavigationPageRenderer(Context context)
            : base(context)
        {}
    
        protected override Task<bool> OnPushAsync(Page page, bool animated) {...}
    
        protected override Task<bool> OnPopToRootAsync(Page page, bool animated){...}
    
        protected override Task<bool> OnPopViewAsync(Page page, bool animated)
        {
            // if the page implements my interface then first check the page 
            //itself is not already handling a redirection ( Handling Navigation) 
            //if don't then let the handler to check whether to process 
            // Navitation or not . 
            if (page is INavigationHandler handler && !handler.HandlingNavigation
                 && handler.HandlePopAsync(page, animated))
                return Task.FromResult(false);
            
            return base.OnPopViewAsync(page, animated);
        }
    }
    

    那么我的 INavigationHandler 界面应该是这样的

    public interface INavigationHandler
    {
        public bool HandlingNavigation { get; }
        public bool HandlePopAsync(Xamarin.Forms.Page view, bool animated);
        public bool HandlePopToRootAsync(Xamarin.Forms.Page view, bool animated);
        public bool HandlePuchAsync(Xamarin.Forms.Page view, bool animated);
    }
    

    最后,在任何 ContentView 中,在此示例中,当我尝试向后导航时,我只是折叠了一个菜单并阻止了向后导航。

    public partial class MenusList : INavigationHandler
    {
        public bool HandlingNavigation { get; private set; }
    
        public bool HandlePopAsync(Page view, bool animated)
        {
            HandlingNavigation = true;
            try 
            {
                if (Menu.Expanded)
                {
                    Menu.Collapse();
                    return true;
                }
                else return false;
            }
            finally
            {
                HandlingNavigation = false;
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      这本身就是一项艰巨的任务,我解决它的唯一方法是完全删除后退按钮,然后从“保存”按钮处理向后导航。

      我对 Xamarin.Forms 论坛进行了简短搜索,并提出了以下建议:

      public override bool OnOptionsItemSelected(Android.Views.IMenuItem item)
      {
          return false;
      }
      

      帖子链接如下:

      https://forums.xamarin.com/discussion/21631/is-there-nay-way-of-cancelling-the-back-button-event-from-the-navigationpage

      【讨论】:

        【解决方案5】:

        正如已经说过的 - 您不能跨平台进行此操作。但是,您可以在本地处理它,可以说不需要太多努力:https://theconfuzedsourcecode.wordpress.com/2017/03/12/lets-override-navigation-bar-back-button-click-in-xamarin-forms/

        这篇文章涵盖了 iOS 和 Android。如果你有一个 UWP 项目,你将不得不为它制定自己的解决方案。

        编辑: 这是 UWP 解决方案!事实证明这很简单——只有一个后退按钮,而且它受 Forms 支持,因此您只需覆盖 ContentPage 的 OnBackButtonPressed:

            protected override bool OnBackButtonPressed()
            {
                if (Device.RuntimePlatform.Equals(Device.UWP))
                {
                    OnClosePageRequested();
                    return true;
                }
                else
                {
                    base.OnBackButtonPressed();
                    return false;
                }
            }
        
            async void OnClosePageRequested()
            {
                var tdvm = (TaskDetailsViewModel)BindingContext;
                if (tdvm.CanSaveTask())
                {
                    var result = await DisplayAlert("Wait", "You have unsaved changes! Are you sure you want to go back?", "Discard changes", "Cancel");
        
                    if (result)
                    {
                        tdvm.DiscardChanges();
                        await Navigation.PopAsync(true);
                    }
                }
                else
                {
                    await Navigation.PopAsync(true);
                }           
            }
        

        【讨论】:

          【解决方案6】:

          在 Xamarin.Forms 中,Page 类有一个 OnBackButtonPressed() 方法,您可以在所有平台上使用该方法

          【讨论】:

          • 这不会告诉你是否按下了导航返回按钮/图标 - 只有当系统返回按钮被按下时
          猜你喜欢
          • 2017-08-23
          • 1970-01-01
          • 1970-01-01
          • 2022-01-17
          • 2018-04-29
          • 1970-01-01
          • 2015-11-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多