locked
Is there nay way of cancelling the "back" button event from the NavigationPage? RRS feed

  • Question

  • User59400 posted

    Topic.

    Is there any way of cancelling the "back" button event from the NavigationPage, preventing the user from popping the navigation stack ?

    I already can intercept the hardware back button from the main activity, but I wasn't able to capture the navigation back button.

    Monday, August 4, 2014 1:27 PM

All replies

  • User59400 posted

    No solution?

    Monday, August 11, 2014 2:29 PM
  • User55225 posted

    I too would like to have this ability.

    Thanks

    Monday, August 11, 2014 2:48 PM
  • User181 posted

    In iOS there are multiple ways of going back in a navigation controller other than pressing the back button. You can do an edge swipe on the left side of the screen either on or below the navigation bar (the behavior is slightly different in those two cases). It wouldn't make sense to try to "cancel" that.

    Usually when someone wants to do this my reaction is that you're doing something wrong. If you don't want a user to be able to go back then you shouldn't be using a navigation pattern that implies that a user can go back. Maybe you should present this view modally instead of pushing it onto a navigation stack.

    What is the reason for not wanting the user to be able to go back?

    Monday, August 11, 2014 3:02 PM
  • User2773 posted

    Something similar could be done with page OnDisappearing method override.

        protected override void OnDisappearing()
        {
            bool preventPagePop = true;
    
            if (!preventPagePop) base.OnDisappearing();
        }
    
    Monday, August 11, 2014 3:06 PM
  • User181 posted

    I don't think that will prevent the view from disappearing. It will just stop the parent class from knowing that it left the screen. That's not a good idea.

    Monday, August 11, 2014 3:08 PM
  • User2773 posted

    @adamkemp? Yes my bad, I gave you a wrong tip, sorry. What a lot of people here use for that kind of situations is overriding parent page OnAppearing method and displaying a modal page if some criteria isn't matched.

    Check: https://github.com/conceptdev/xamarin-forms-samples/blob/master/EmployeeDirectoryXaml/EmployeeDirectoryXaml/Views/EmployeeListXaml.xaml.cs

    and

    http://forums.xamarin.com/discussion/comment/68724

    Monday, August 11, 2014 3:37 PM
  • User55225 posted

    Hi @adamkemp?!

    Thanks for the reply. It's appreciated.

    There are 2 scenarios where I would like to prevent the user from going back.

    1. I would like to implement a page that has a ListView and a ToolBarItem which says "Edit". Upon tapping "Edit" the view would change to Edit mode. The "Edit" ToolBarItem would switch to a plus sign, each row will have an edit and remove button. Lastly, I'd like to swap out the Back button for a "Cancel" button. Upon pressing cancel, it would revert back to Normal Selection mode.

    2. Typically for my data entry screens, I'd like to prompt the user to confirm "Cancel Changes?" before navigating away via the "Back" button. I'm used to doing this for WP and maybe it doesn't apply to iOS/Android.

    Thanks!

    Tuesday, August 12, 2014 9:56 AM
  • User181 posted

    That actually does sound reasonable. In fact, we have a UI like that in our (non-Forms) Xamarin.iOS application. In Xamarin.iOS you would do this by replacing the NavigationItem.LeftBarButtonItem with a cancel button and then restoring the original when you are no longer editing.

    I don't think Xamarin.Forms is that flexible, and unfortunately making your own renderer for this probably involves rewriting a bunch of code they already have because so many things are internal or private. In the meantime you could maybe just present the editing screen as a separate modal view. It can probably even be the same Page class bound to the same view model, but with an editing mode.

    Tuesday, August 12, 2014 2:52 PM
  • User59890 posted

    This really needs to be solve from XF point of view.
    In the WP world the back button is a hardware button that should move the user back in behavioural stack not just the page stack. e.g. Page1 --> Page2:Display mode--> Page2:Edit mode. It this case the back button should take the user back to Page2:Display mode and not to Page1. Another scenario is a process requires an on screen dialogue embedded in the page, the user would expect the back button clear the dialogue and not navigate away. For this to happen the a it must be possible to intercept the back button action and prevent the page navigation so that the user can manage an appropriate back action. As a footnote, devices have hardware back buttons, when developing cross platform app they need to build to the lowest point of change, in this case the back button should not be hidden as hardware buttons cannot be hidden. On submission to the Microsoft app store, app will be rejected if the back button doesn't behave as the user expects. This is a major restriction.

    Wednesday, September 10, 2014 9:28 AM
  • User59400 posted

    Ok, found the solution for Android, for those seeking to prevent the pop from occurring on the current page, all you need to do is override the method OnOptionsItemSelected at the MainActivity, returning false should do the job.

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

    Just a note, the event gets fired only for the back button of the navigation page, the menu items don't fire the same event.

    Best regards.

    Friday, October 3, 2014 5:13 PM
  • User74212 posted

    Confirmed OnOptionsItemSelected works for Android.

    Does anyone have a solution for iOS? What about create a button and assign to NavigationController.TopViewController.NavigationItem.LeftBarButtonItem in a page renderer?

    Saturday, October 4, 2014 10:20 AM
  • User181 posted

    Again, if you don't want back navigation then don't push the view in a NavigationPage. The android back button is a special case that you have to handle because you can't possibly hide it, but the back button on iOS only appears if you're in a navigation controller, and that is entirely within your control.

    Saturday, October 4, 2014 2:53 PM
  • User74212 posted

    I do need a back button. But I need to ask the user to confirm it with a Yes/No alert firstly. This is critical for this enterprise app, since the back button will cancel the whole session joined with a group of users. I cannot think of another way to convince PM.

    Sunday, October 5, 2014 7:59 AM
  • User181 posted

    Then present it as a modal and use a toolbar button instead. It would have to be on the right (because of Xamarin.Forms limitations), but it's possible at least.

    The only way even in the raw UIKit API to stop a user from going back in a navigation controller is to not have a back button at all (by replacing it in the toolbar). That would require a special renderer, and from your UX it sounds like you really should use a modal anyway.

    Sunday, October 5, 2014 3:13 PM
  • User59400 posted

    Modals remove the menu items on Android, that's why we didn't use it at first. I would research custom renders for iOS, looks promising.

    Monday, October 6, 2014 12:22 PM
  • User181 posted

    You can present a new NavigationPage modally to get the action bar back. But it wouldn't have a back button because you would be at the top of that NavigationPage's stack (at least I'm pretty sure that's how it would work).

    var navigationPage = new NavigationPage(loginPage);
    await Navigation.PusModalAsync(navigationPage);
    

    That might give you what you want.

    Monday, October 6, 2014 2:17 PM
  • User72778 posted

    For iOS, this can be done like this. Please note this requires NavigationPage as the root page.

    `` [assembly: Xamarin.Forms.ExportRenderer(typeof(MyPage), typeof(MyPageRenderer))]

    namespace MyRenderers
    {
        class MyPageRenderer : PageRenderer
        {
            public override void ViewWillAppear(bool animated)
            {
                base.ViewWillAppear(animated);
    
                var root = this.NavigationController.TopViewController;
    
                if ((Element as MyPage).OverrideBackButton)
                {
                    string title = NavigationPage.GetBackButtonTitle(Element);
    
                    root.NavigationItem.SetLeftBarButtonItem(new UIBarButtonItem(title, UIBarButtonItemStyle.Plain, (sender, args) =>
                    {
                        (Element as MyPage).OnPopping();
                    }), true);
                }
            }
        }
    }
    
    Friday, October 10, 2014 1:39 AM
  • User24558 posted

    Hi, @count did you resolve this? [do need a back button. But I need to ask the user to confirm it with a Yes/No alert firstly]

    I have a "Settings" view, I want to ask form confirmation before the user moving back if there are changes that were not saved yet.

    thanks.

    Friday, March 13, 2015 7:59 PM
  • User59400 posted

    @fernandop

    MainActivity.cs:

    public override void OnBackPressed()
    {
            CurrentPage.BackPressed(); // Global current page instance
            return false;
    }
    

    On your your page instance:

    public bool CanReturn
    {
        get
        {
            Logout();
            return false;
        }
    }
    
    public async Task Logout()
    {
        var exit = await  this.DisplayAlert("term_confirm", "message_logout", "term_ok", "term_cancel");
        if (exit)
           this.Navigation.PopToRootAsync();
    }
    

    Should work, it works for us.

    Best regards.

    Friday, March 13, 2015 8:11 PM
  • User24558 posted

    Hi @fbmiranda7
    where you use "CanReturn"? in BackPressed method of the Page?

    Saturday, March 14, 2015 11:04 AM
  • User59400 posted

    Sorry,

    it was a typpo during my conversion of the code to this post.

    The CanReturn is the BackPressed method of current page.

    public bool BackPressed()
    {
            Logout();
            return false;
    }
    
    Monday, March 16, 2015 12:32 PM
  • User24558 posted

    Good, thanks! I will try it later on, since now the user changed his mind and he wants a different logic.

    regards,.

    Wednesday, March 18, 2015 8:09 PM
  • User73253 posted

    I have the same problem as @AnthonyRamirez:

    Typically for my data entry screens, I'd like to prompt the user to confirm "Cancel Changes?" before navigating away via the "Back" button. I'm used to doing this for WP and maybe it doesn't apply to iOS/Android.

    Using the info above plus this, I came up with the following solution for Android. It feels like there should be a better way to handle this.

          // 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 OnOptionsItemSelected(item);
             }
          }
    
          // hardware back button
          public async override void OnBackPressed()
          {
             var currentViewModel = (IViewModel)navigator.CurrentPage.BindingContext;
    
             // could display an confirmation dialog (ex: "Cancel changes?")
             var canNavigate = await currentViewModel.CanNavigateFromAsync();
             if (canNavigate)
             {
                base.OnBackPressed();
             }
          }
    
    Tuesday, June 23, 2015 12:10 AM
  • User73253 posted

    Correction

    This: return OnOptionsItemSelected(item);

    Should be: return base.OnOptionsItemSelected(item);

    Tuesday, June 23, 2015 6:31 PM
  • User99715 posted

    To handle the navigationbar back click i did this on android (in my MainActivity in android project):

    public override bool OnOptionsItemSelected(Android.Views.IMenuItem item)
            {
    
                if (Android.Resource.Id.Home != item.ItemId) return false;
    
                OnBackPressed();
    
                return true;
    
            }
    

    this will call the OnBackButtonPressed code in the Page were the navigationbar back button was clicked.

    Monday, August 3, 2015 4:17 PM
  • User47710 posted

    Thanks jzeferino. Using your solution I can display a user prompt and handle navigation from inside Xamarin.Forms.Page.OnBackButtonPressed().

    Tuesday, August 4, 2015 11:52 PM
  • User130023 posted

    @AnthonyRamirez Did you ever find a solution for your two scenarios above? I'm looking for the same solution.

    Friday, August 14, 2015 1:23 PM
  • User42522 posted

    This is a very old thread. So many things said here are not applicable now. I got errors when I wrote the code given above. So by trail and error, found that worked.

    This is what worked for me for Android project using Xamarin.Forms of 1.4.4:

            public override bool OnOptionsItemSelected(IMenuItem item)
            {
                if (item.ItemId != 16908332)
                    return false;
    
                OnBackPressed ();
    
                return true;
            }
    

    Hope something like this can be done for iOS also?

    Saturday, August 29, 2015 2:11 AM
  • User181 posted

    No, this can't be done on iOS, and again it should not be done. If there is a back button on iOS then it cannot be overridden to do something else. The answer to not allowing users to go back is to not have a back button.

    Monday, August 31, 2015 3:45 PM
  • User42522 posted

    In iOS, the only event fired when you tap on the NavigationBar's 'Back' button is OnDisappearing(). You can do anything here. I can put whatever code that does sort of cleanup here. But OnDisppearing() is also fired if I push to another page. So if I want to differentiate between the NavigationBar.Back button and going forward to another page, I have to have a host of my own flags set if I am going forward. Similarly does NavigationBar.Back set any field on the Page class or on the NavigationBar class?

    Monday, August 31, 2015 4:01 PM
  • User181 posted

    If you want to distinguish between back and forward then your app should just call a method directly when pushing a new page. This is much easier if you follow my advice from this blog post by decoupling views and centralizing navigation logic.

    Monday, August 31, 2015 10:39 PM
  • User137989 posted
    • A cool trick to disable back button on android only is the following:- (I would suggest not to use it unless things get really desperate).

      //public override void OnBackPressed()
      //{
      return false;
      //    //base.OnBackPressed();
      //}
      

    This helps in situations such as in LG tablets, that do not have a hardware backbutton.

    Another trick is to hide the navbar in pages where we want to override the back button.

        protected override void OnAppearing()
        {
            base.OnAppearing();
            NavigationPage.SetHasNavigationBar(this, false);
        }
    

    Create a fake nav bar with a back button in XAML . And use that instead.

    Monday, February 15, 2016 3:45 PM
  • User250026 posted

    Xamarin.Android- Override OnBackPressed

    public override void OnBackPressed()
            {
                //ignore back on logout cancel
                AlertDialog ad = new AlertDialog.Builder(this).Create();
                ad.SetTitle("Logout!");
                ad.SetMessage("Are you sure you want to logout?");
                ad.SetCancelable(false);
                ad.SetCanceledOnTouchOutside(false);
                ad.SetButton("OK", delegate
                {
                    base.OnBackPressed();
                });
                ad.SetButton2("Cancel", delegate
                {
                    return;
                });
                ad.Show();
            }
    

    iOS -After trying lot more things i found the solution as below.

    public override void ViewDidLoad()
            {
                base.ViewDidLoad();
    
                UIBarButtonItem button = new UIBarButtonItem("Logout", UIBarButtonItemStyle.Plain, (sender, e) =>
                {
                    var Confirm = new UIAlertView("Confirmation", "Are you Sure You Want to Logout?", null, "Cancel",               "Confirm");
                    Confirm.Show();
                    Confirm.Clicked += (object senders, UIButtonEventArgs es) =>
                    {
                        if (es.ButtonIndex == 0)
                        {
                                // do something if cancel
                        }
                        else
                        {
                                // Do something if yes
                                this.NavigationController.PopViewController(true);
                        }
                    };
                });
    
                this.NavigationItem.LeftBarButtonItem = button;
    
            }
    
    Sunday, September 25, 2016 5:42 PM
  • User238292 posted

    It can be used directly in the code behind:

        protected override bool OnBackButtonPressed()
        {
            base.OnBackButtonPressed();
    
            //new thread
            Device.BeginInvokeOnMainThread(async () => {
    
                var result = await this.DisplayAlert("title", "messegge?", "yes", "no");
    
                 if (result)
                 {
                  //other methods
                      this.Navigation.PopAsync();
                 }
    
             });
    
             return true; //Do not navigate backwards by pressing the button
        }
    

    This work for me...

    Wednesday, November 30, 2016 9:13 PM
  • User142812 posted

    I'm trying to achieve to show a DisplayAlert asking the user if they are sure they want to close the current page. With the beautiful code from @LuisMatos above, I have been able to show the alert when pressing the hardware back button on Android. However, no example is working when pressing the back button in the toolbar. Overriding both the OnOptionsItemSelected and OnBackPressed in the MainActivity isn't working, the breakpoints aren't even hit. Is this a bug, or am I missing something?

    Xamarin bug tracker issue: https://bugzilla.xamarin.com/show_bug.cgi?id=38189

    Thursday, January 19, 2017 12:56 PM
  • User181 posted

    @JeroenvanWarmerdam.5306, there was a discussion about that above in this very thread (see discussion starting here]). If you want to entirely prevent someone from leaving a page by pressing a back button then the solution, as always, is to not have a back button.

    Saturday, January 21, 2017 6:28 AM
  • User285777 posted

    For iOS the Answer was given by @prashantchoubey.2201. You have to overwrite the LeftBarButtonItem this way:

    NavigationItem.LeftBarButtonItem = new UIBarButtonItem(
                    "Cancel me", UIBarButtonItemStyle.Plain, (sender, args) =>
                    {
                        var cancelConfirmationAlert = UIAlertController.Create("Cancel event creation", "Are you sure to cancel?", UIAlertControllerStyle.Alert);
    
                        cancelConfirmationAlert.AddAction(UIAlertAction.Create("YES", UIAlertActionStyle.Default, OnConfirmCancelCreation));
                        cancelConfirmationAlert.AddAction(UIAlertAction.Create("NO", UIAlertActionStyle.Cancel, alert => Console.WriteLine("NO was clicked")));
    
                        PresentViewController(cancelConfirmationAlert, true, null);
                    });
    

    and implement the method OnConfirmCancelCreation

    private void OnConfirmCancelCreation(UIAlertAction action)
            {
                NavigationController.PopViewController(true);
            }
    

    But @adamkemp is right. If you are doing this, maybe you should be showing this view modally and not pushing it in you NavigationController stack.

    Tuesday, February 21, 2017 3:36 PM
  • User339155 posted

    HI, How to Show popup when press naviagationbar Back button in xamarin forms.

    Tuesday, November 21, 2017 11:46 AM
  • User123029 posted

    Unfortunately the OnOptionsItemSelected approach stops working after the screen has been rotated (if ConfigurationChanges is set to prevent re-creation of the activity).

    Here is another way to handle navigation button click in Android. You need your own NavigationPage defined in PCL:

    public class CustomNavigationPage : NavigationPage
    {
        public CustomNavigationPage( Page root )
            : base( root )
        {
        }
    }
    

    Set it as MainPage. Because I'm using Catel in my project, it is done in the OnCustomizeMainPage method:

    protected override Page OnCustomizeMainPage( StartupPage currentMainPage )
    {
        var navigationPage = new CustomNavigationPage( currentMainPage );
        return navigationPage;
    }
    

    And finally define the renderer for the page:

    [assembly: ExportRenderer( typeof( CustomNavigationPage ), typeof( CustomNavigationPageRenderer ) )]
    
    class CustomNavigationPageRenderer : NavigationPageRenderer
    {
        public CustomNavigationPageRenderer( Context context )
            : base( context )
        {
        }
    
        protected override void OnLayout( bool changed, int l, int t, int r, int b )
        {
            base.OnLayout( changed, l, t, r, b );
    
            var toolbarField = typeof( NavigationPageRenderer ).GetTypeInfo().GetDeclaredField( "_toolbar" );
            var toolbar = toolbarField.GetValue( this ) as global::Android.Support.V7.Widget.Toolbar;
            if ( toolbar != null )
            {
                toolbar.SetNavigationOnClickListener( new OnClickListener( this ) );
            }
        }
    
        private class OnClickListener : Java.Lang.Object, IOnClickListener
        {
            private CustomNavigationPageRenderer _owner;
    
            public OnClickListener( CustomNavigationPageRenderer owner )
            {
                _owner = owner;
            }
    
            public void OnClick( global::Android.Views.View v )
            {
                // Navigation button clicked
            }
        }
    }
    

    The magic is done in the OnLayout that is called on creation or on every configuration change. NavigationPageRenderer stores toolbar in the _toolbar variable that is private so reflection is the only way to obtain it.

    Tuesday, November 28, 2017 1:30 PM