Xamarin forms - Stop/Cancel back button event

« Back to list of posts

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.


By Luke Alderton at 8 Mar 2017, 05:10 AM

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

Comments


Post a comment

Please correct the following:
Share with