Skip to content

Navigation

Axemasta edited this page Jun 22, 2021 · 2 revisions

The Navigating event allows you to cancel navigation using the following syntax.

async void OnNavigating(object sender, SuperWebNavigatingEventArgs e)
{
    if (e.CanCancel)
    {
        var token = e.GetDeferral();

        bool canBrowse = await EvaluateUrlAsync(e.Url);

        if (!canBrowse)
        {
            e.Cancel();
        }

        token.Complete();
    }
}

You can cancel navigation synchronously or asynchronously.

Defer Navigation

To start the process, request a DeferralToken:

async void OnNavigating(object sender, SuperWebNavigatingEventArgs e)
{
    if (e.CanCancel)
    {
        var token = e.GetDeferral();
    }
}

Once you have requested this token, you must return it otherwise it will block the SuperWebView from performing any more actions.

If you do not acquire this token, navigation will be automatically granted.

Cancel Navigation

If you wish to cancel the navigation, call the Cancel() method on the SuperWebNavigatingEventArgs:

async void OnNavigating(object sender, SuperWebNavigatingEventArgs e)
{
    if (e.CanCancel)
    {
        var token = e.GetDeferral();

        e.Cancel();
    }
}

Commit Navigation Action

Once you have decided whether to cancel navigation, hand the token back by calling the Complete() method:

async void OnNavigating(object sender, SuperWebNavigatingEventArgs e)
{
    if (e.CanCancel)
    {
        var token = e.GetDeferral();

        e.Cancel();

        token.Complete();
    }
}

CanCancel

It is advised but not necessary to check if cancellation can occur before acquiring a deferral token. If e.CanCancel is false, a fake deferral token will be returned and it will have no impact on the navigation event.

Context

This feature is the reason I forked the original control. There are numerous issues on Xamarin Forms relating to cancelling navigation using async await (#13834, #12720). The issue is that with the current implementation of the renderers, when the Navigating event is fired, you have a small window to change the Cancel value to true/false before the renderer evaluates whether the control can browse to a site. This means that when using a long running task to decide whether the site can be accessed, the page will be loaded before the task returns. Here are the references in the source code: see iOS, Android).

I originally added a new property to NavigatingWebSourceArgs that added a task that could be awaited within a renderer to achieve the desired outcome. I raised a PR (#12756) with the Xamarin team and they suggested that I use the DeferalToken concept from Shell to essentially block the UI thread from running until my background task completes. I have since completed that implementation and raised another PR (#14137), but unfortunately due to Xamarin Forms being close to EOL and the team busy with .NET MAUI, I doubt the change will be implemented at all. Due to having a hard requirement for this functionality in an app I am developing, I took matters into my own hands and went nuclear.

Clone this wiki locally