How to intercept Navigation Bar Back Button Clicked in Xamarin Forms?
Asked Answered
T

7

18

I have a xamarin form page where a user can update some data in a form. I need to intercept the Navigation Bar Back Button Clicked to warn the user if some data have not been saved.How to do it?

I'm able to intercept the hardware Bar Back Button Clicked in Android using the Android.MainActivity.OnBackPressed(), but that event is raised only on hardware Bar Back Button Clicked, not on Navigation Bar Back Button Clicked.

I tried also to override Xamarin.Forms.NavigationPageOnBackButtonPressed() but it doesn't work. Why? Any one have already solved that issue?

I also tried by overriding OnDisappear(), there are two problems:

  1. The page has already visually disappeared so the "Are you sure?" dialog appears over the previous page.
  2. Cannot cancel the back action.

So, is it possible to intercept the navigation bar back button press?

Thisbee answered 29/7, 2015 at 9:35 Comment(0)
B
10

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];
Brosine answered 31/7, 2015 at 20:17 Comment(1)
Instead of using a hard coded number, I would recommend to use the resource constant. Just replace 16908332 with Android.Resource.Id.HomeMoravia
X
5

The easiest, as @JordanMazurke also somewhat mentions, since the event for the back button cannot be handled currently (other than the physical back button for Android), is to either:

  1. NavigationPage.ShowHasBackButton(this, false)
  2. Pushing a Modal instead of a Page

Then afterwards, you can add an ActionbarItem from where you can handle the Event.

I personally spoke to the iOS team from Xamarin concerning this matter, and they basically told me we shouldn't expect support for handling the Event for the BackButtonPressed in the NavigationBar. The reason being, that on iOS, it's bad practice for the users to receive a message when Back is pressed.

Xhosa answered 30/7, 2015 at 19:26 Comment(2)
:( this makes me a sad.Oast
In case anyone wants to disable the navigationbar's back button in xaml, do: <ContentPage NavigationPage.HasBackButton="False">Jeanelle
N
2

Best way I have found is adding my own NavigationRenderer to intercept the navigation methods and a simple Interface

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

Then my INavigationHandler interface would look like this

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

Finally in any ContentView, in this example when trying to navigate back I'm just collapsing a menu and preventing a navigation back.

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;
        }
    }
}
Necktie answered 31/7, 2020 at 20:36 Comment(1)
Thanks for the hint to use NavigationPageRenderer! I use a custom ContentPage with which I control the navigation with an Action. Every child page implements this for itself.Librarianship
C
0

This is an inherently difficult task and the only way I got around it was to remove the back button entirely and then handle the backwards navigation from a 'save' button.

I have done a brief search of the Xamarin.Forms forum and the following has been suggested:

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

The link for the post is as follows:

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

Clupeoid answered 29/7, 2015 at 11:41 Comment(0)
C
0

As has already been said - you cannot do this cross-platform. However, you can handle it natively with arguably not so much effort: https://theconfuzedsourcecode.wordpress.com/2017/03/12/lets-override-navigation-bar-back-button-click-in-xamarin-forms/

The article covers iOS and Android. If you have a UWP project you'll have to hammer your own solution for it.

Edit: Here is the UWP solution! It actually turned out to be pretty easy – there is just one back button and it’s supported by Forms so you just have to override ContentPage’s 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);
        }           
    }
Cheek answered 5/9, 2017 at 18:8 Comment(0)
A
0

Back button appearance and behavior can be redefined by setting the BackButtonBehavior attached property to a BackButtonBehavior object.

 <Shell.BackButtonBehavior>
    <BackButtonBehavior Command="{Binding GoBackCommand}"/></Shell.BackButtonBehavior>

GobackCommand defined in your View Model.

You can follow below link for more details: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/shell/navigation

Arius answered 9/4, 2022 at 13:35 Comment(0)
A
-1

In Xamarin.Forms, the Page class has an OnBackButtonPressed() method that you can tap into for all platforms

Analphabetic answered 29/7, 2015 at 20:42 Comment(1)
This doesn't tell you if the navigation back button / icon is pressed - only if the system back button is pressedSouthport

© 2022 - 2024 — McMap. All rights reserved.