Xamarin forms - Stop/Cancel back button event

Introduction:

We've all been there, set with the task to ask the user if they're sure they really want to go go back and loose the work/information on the current screen. The trouble is that if you were to Google search for just that, you'd find almost every answer says you need some sort of custom handler for each device. We can't accept that can we? Will we? No.

In order to work correctly this post relies on one axiom; that we're not trying to prevent the user from leaving your app. There are many reasons not to prevent users from leaving, but take the time to ask yourself if the user really needs to be questioned about it, if there's no data or work for the user to loose on quitting the application or page, then we've answered the question already.

On the other hand, you may have an application whereby the user enters a lot of information that could be lost, or there may not be a way of getting back to edit that information once the user has left the page, and this may be by design. In which case, we'll need to prompt the user. Because what's more annoying than tapping back for a second time after you questioned if you'd actually pressed the button, only to find out the device was just running slow, and you're now stuck with a partial set of data.

The code:

So let's get on with it. Assuming you already have a number of Xaml pages within your Xamarin project and the associated cs file to go with them, open the cs file for the page on which you'd like to query the user about leaving and take a look at the following code:

Boolean blnShouldStay = false;
protected override bool OnBackButtonPressed()
{
    if (blnShouldStay)
    {
        // Yes, we want to stay.
        return true;
    }
    else
    {
        // It's okay, we can leave.
        base.OnBackButtonPressed();
        return false;
    }
}

We have this variable called blnShouldStay, we've named it that because returning true for the OnBackButtonPressed event tells Xamarin that we'd like to stay on the page, as if to say 'Hey Xamarin, I've handled it, don't worry about the default action.' and assuming you have some synchronous code elsewhere, that's great, if you paste the above into your page's cs file, then can now prevent the back button from working just fine.

What if we want to prevent the back action after running some asynchronous task?

Say for example we wanted to ask the user. Easy. We have to return true all the time and handle both the leave and the stay actions. See the example below which handles just that:

protected override bool OnBackButtonPressed()
{
    // Begin an asyncronous task on the UI thread because we intend to ask the users permission.
    Device.BeginInvokeOnMainThread(async () =>
    {
        if (await DisplayAlert("Exit page?", "Are you sure you want to exit this page? You will not be able to continue it.", "Yes", "No"))
        {
            base.OnBackButtonPressed();

            await App.Navigation.PopAsync();
        }
    });

    // Always return true because this method is not asynchronous.
    // We must handle the action ourselves: see above.
    return true;
}

We begin by calling Device.BeginInvokeOnMainThread, this method is used to run both synchronous and asynchronous tasks and it's useful for two reasons. The first being a need to run an asynchronous task that deals with the UI within a synchronous method, just as we do above. And the second being where you've just run a large set of CPU heavy code and now you'd like to update the UI with the result or notify the user with a pop up.

So looking at the code above, you'll see that all we're doing is preventing the back buttons default action and handling it ourselves, by running an asynchronous task that waits for the users response to the question 'Do you want to leave?'.

If the user answers No, then there's nothing to do, but if they answer Yes, then we're going to have to handle the back action ourselves since we canceled that already. We do this by just calling PopAsync() on our apps navigation object. The location of your navigation page may differ from mine.

And that's it if you don't care about the back button in the navigation bar at the top of your application, because for some reason Xamarin doesn't call OnBackButtonPressed when the user taps it. (If anyone knows why, please let me know in the comments.)​​

Getting rid on the navigation bar back button so we don't have to deal with it.

It's as simple as calling NavigationPage.SetHasNavigationBar(this, false); from within your ContentPage constructor:

public partial class MyPage : ContentPage
{
    public MyPage()
    {
        InitializeComponent();

        // Disable the navigation bar for this page.
        NavigationPage.SetHasNavigationBar(this, false);
    }

    protected override void OnAppearing()
    {
        base.OnAppearing();
    }

    protected override bool OnBackButtonPressed()
    {
        // Begin an asyncronous task on the UI thread because we intend to ask the users permission.
        Device.BeginInvokeOnMainThread(async () =>
        {
            if (await DisplayAlert("Exit page?", "Are you sure you want to exit this page? You will not be able to continue it.", "Yes", "No"))
            {
                base.OnBackButtonPressed();

                await App.Navigation.PopAsync();
            }
        });

        // Always return true because this method is not asynchronous.
        // We must handle the action ourselves: see above.
        return true;
    }
}

All you have to do then is build a title bar in the xaml for that page, which could be in the form of a StackLayout with a white background containing label:

<StackLayout
VerticalOptions="Start"
HorizontalOptions="Fill"
Orientation="Horizontal"
Spacing="5"
HeightRequest="40"
BackgroundColor="#fff">
<StackLayout.Padding>
<OnPlatform x:TypeArguments="Thickness"
iOS="20, 10, 10, 10"
Android="20, 10, 10, 10"
WinPhone="20, 10, 10, 10" />
</StackLayout.Padding>
<Label x:Name="title" Text="" TextColor="#000" FontSize="18" HorizontalOptions="Fill" VerticalOptions="CenterAndExpand" />
</StackLayout>

That's it, we're done. I hope I helped and wish you happy coding in the future.


Published at

Tags: Xamarin,Xamarin Forms,Android,iOS,C#,Xaml

Luke Alderton

Comments

Antony
Hi Luke....How can I proceed if instead of going back to previews pages, I want to close entirely the app?
18/12/2017
Luke Alderton
Hi Antony, you'd probaby find this StackOverflow post useful: https://stackoverflow.com/questions/30274004/xamarin-close-android-application-on-back-button, in short, you have to use a dependency handler and execute the order to close the app using native functions.
20/12/2017
jake
Very straight-forward, and informative post. Thank you!
26/05/2018
Rezi
thanx! I searched a lot for this. You saved my day.
22/03/2019
SK
Thank you. Just right to the point. Excellent job.
14/12/2019
Hannah
Thank you! Your post was most helpful!
14/01/2020
Darío
Thanks this is very useful.
01/03/2020
mitchell
This is great, I appreciate you're taking the time to share it.
29/04/2020
Share with
Tags
Latest Comments
By Mark Gentry on Windows Server 2019 - Change product key does nothing
20 Aug 2021, 03:30 AM
By Cathy on In-Place Upgrade for a Windows Server domain controller
31 Jul 2021, 18:28 PM
By Mr. Greymatter on Raspberry Pi - Running Java app on Raspbian
16 Feb 2021, 07:35 AM
By Mikko Seittenranta on Xamarin Forms multiple instances of same app open
16 Feb 2021, 04:34 AM
By Andrew on Auto/Custom height on Xamarin Forms WebView for Android and iOS
22 Jan 2021, 22:15 PM
By Nick on Raspberry Pi - Running Java app on Raspbian
14 Oct 2020, 19:37 PM
By Ivan on Fixed: Value cannot be null Parameter Name: source
15 Sep 2020, 19:47 PM
By Anand on Raspberry Pi - Bluetooth using Bluecove on Raspbian
7 Sep 2020, 16:53 PM
Categories
App Development
Event
Game Development
Mapping
Modelling
Programming
Review
Robotics
Tutorial
Web Development