1

I am developing with Visual Studio 2022 a NET MAUI mobile app that contains a WebView object. The source website, mywebsite.com, is loaded correctly and all its internal links work.

My aim is making sure that when a link to a different website, say, microsoft.com, is clicked, the website opens in the default browser of the user's mobile phone.

Problem is, when I click on microsoft.com (or whatever other external link), nothing happens.

Navigating event is not triggered with these external links whether the target in the HTML is set to _blank or not; the solutions provided here (MAUI Webview target="_blank" href redirects not working on android) and here (Intercept all links user clicks on in WebView (.NET Maui)) did not work for me.

The navigating event is regularly triggered when clicking on links internal to mywebsite.com.

My MainPage.xaml.cs contains:

public MainPage()
{
    InitializeComponent();
    // Subscribe to the Navigating event
    proteoWebView.Navigating += OnNavigating;
    // Subscribe to the Navigated event
    proteoWebView.Navigated += OnNavigated;
}

private void OnNavigating(object sender, WebNavigatingEventArgs e)
{
    // Check if the URL is external
    if (!e.Url.StartsWith("https://mywebsite.com"))
    {
        // Cancel the navigation in the WebView
        e.Cancel = true;
        // Open the URL in the default browser of the user's phone
        Launcher.TryOpenAsync(e.Url);
    }
}

My MainPage.xaml contains:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ProteoApp.MainPage"
             NavigationPage.HasNavigationBar="False">
    <Grid>
        <WebView 
                 x:Name="proteoWebView"
                 Source="https://mywebsite.com"
                 Grid.Row="0"
                 Grid.Column="0"
                 VerticalOptions="FillAndExpand"
                 HorizontalOptions="FillAndExpand"/>
    </Grid>
</ContentPage>

Other information: I am developing with Visual Studio 2022, .NET 7.0, and I have experienced the issue with the Pixel 5 – API 33 (13.0 Android) emulator.

Antelion
  • 155
  • 2
  • 14

2 Answers2

0

I should correct myself.

Foreign links with a target="_blank" property do not trigger OnNavigating event. Foreing links without a target="_blank" property do trigger OnNavigating event.

Furthermore, in order to open the user's phone default browser, an async call must be performed:

private void OnNavigating(object sender, WebNavigatingEventArgs e)
    {
        if (!e.Url.StartsWith("https://mywebsite.com"))
        {
            e.Cancel = true;
            Task.Run(async () =>
            {
                await Launcher.OpenAsync(e.Url);
            });
        }

As I have mentioned, the above code works only for foreign links without a target="_blank" property. In order to make it work also for foreign links with a target="_blank" property, it is necessary to add this code to the OnNavigated event:

private void OnNavigated(object sender, WebNavigatedEventArgs e)
    {
        //The following code changes the target of all the links in _self
        proteoWebView.EvaluateJavaScriptAsync(@"(function() {
            var links = document.getElementsByTagName('a');
            for (var i = 0; i < links.length; i++)
            {
                links[i].setAttribute('target', '_self');
            }
        })()");
    }

This solved the issue for me.

Antelion
  • 155
  • 2
  • 14
0

In my case, no matter how the target of the link are defined, in Android I could never navigate to an external link (using Navigating event of the WebView).

I solved this issue like I did for Xamarin.Forms project:

Create a custom WebChromeClient:

/// <summary>
/// External links tapped in a WebView don't open in .Net MAUI.
/// In order to be able to open links in Android in the web view, a <see cref="ExtendedWebViewChrome"/>
/// can be set to intercept taps on external links and open the default browser.
/// </summary>
internal class ExtendedWebViewChrome : WebChromeClient
{
    /// <inheritdoc/>
    public override bool OnCreateWindow(WebView view, bool isDialog, bool isUserGesture, Message resultMsg)
    {
        // Get information on where the user tapped in the web view.
        var result = view.GetHitTestResult();

        if (result == null)
        {
            return base.OnCreateWindow(view, isDialog, isUserGesture, resultMsg);
        }

        // Extract the external link URL of the link from the hit result Extra property.
        var externalLinkUrl = result.Extra;

        try
        {
            var externalLinkUri = Uri.Parse(externalLinkUrl);

            Context context = view.Context;
            Intent browserIntent = new Intent(Intent.ActionView, externalLinkUri);
            context.StartActivity(browserIntent);

            return false;
        }
        catch (Exception exception)
        {
            System.Diagnostics.Debug.WriteLine(
                $"There was an error trying to open the following link: {externalLinkUrl}.\n " +
                $"Exception occurred: {exception.Message} at {exception.StackTrace}");
        }

        // Something went wrong, return what parent would return.
        return base.OnCreateWindow(view, isDialog, isUserGesture, resultMsg);
    }
}

Then you can use it in a custom renderer (still didn't migrate to MAUI handlers):

/// <summary>
/// Sets the AllowFileAccess flag to true and sets horizontal scroll to the right for RTL locales.
/// </summary>
public class ExtendedWebViewRenderer : WebViewRenderer
{
    /// <summary>
    /// Default constructor.
    /// </summary>
    public ExtendedWebViewRenderer(Context context) : base(context) { }

    /// <summary>
    /// Fires on element changed.
    /// </summary>
    protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
    {
        base.OnElementChanged(e);

        if (Control == null)
        {
            return;
        }

        // Enables or disables file system access within WebView.
        // This setting was by default true before API level 30 but now it is needed to be set explicitly.
        // It needs to be set before the attempt to load the HTML is made.
        Control.Settings.AllowFileAccess = true;

        // External links tapped in a WebView  don't open in .NET MAUI.
        // In order to solve the issue, a custom web client can be set to intercept link taps.
        Control.SetWebChromeClient(new ExtendedWebViewChrome());

        ...            
    }
}

Curiously enough, this issue only happens in Android AND if you set the WebView source with a UrlWebViewSource. If a HtmlWebViewSource is used instead, external links can be opened with the Navigating event handler.

jordi casas
  • 143
  • 1
  • 2
  • 7