如何截取Xamarin表单中单击的导航栏后退按钮?

时间:2015-07-29 09:35:38

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

我有一个xamarin表单页面,用户可以在表单中更新某些数据。我需要截取导航栏后退按钮单击以警告用户是否有一些数据尚未保存。如何做?

我能够拦截使用Android.MainActivity.OnBackPressed()在Android中单击的硬件栏后退按钮,但该事件仅在硬件栏上单击按钮时单击,而不是单击导航栏后退按钮。

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

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

  1. 页面已经在视觉上消失了所以"你确定吗?"对话框显示在上一页上。
  2. 无法取消后退动作。
  3. 那么,是否可以截取导航栏后退按钮?

6 个答案:

答案 0 :(得分:8)

I was able to show a confirmation dialog that could cancel navigation by overriding the following methods in the 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();
     }
  }

The navigator.CurrentPage is a wrapper around the INavigation service. I do not have to cancel navigation from modal pages so I am only handling the NavigationStack.

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

答案 1 :(得分:3)

最简单的,因为@JordanMazurke也有所提及,因为当前无法处理后退按钮的事件(除了Android的物理后退按钮),是:

  1. NavigationPage.ShowHasBackButton(this, false)
  2. 推送Modal而不是Page
  3. 然后,您可以在ActionbarItem的处理位置添加Event

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

答案 2 :(得分:0)

这是一项固有的困难任务,我唯一能解决的问题是完全取下后退按钮,然后从“保存”按钮处理向后导航。

我已经对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

答案 3 :(得分:0)

正如已经说过的那样 - 你不能跨平台做这个。但是,您可以原生地处理它,而不是那么多努力: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);
        }           
    }

答案 4 :(得分:0)

我发现的最好方式是添加自己的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;
        }
    }
}

答案 5 :(得分:-1)

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