Setting current url in a custom xamarin forms web view

Viewed 966

I'm starting with Xamarin.Forms and what I'm trying to do is simply setting a CurrentUrl property on a custom Webview in Xamarin.Forms

In other words: When OnPageFinished method is called, I need to set the CurrentUrl property of MyWebView to the new Url.

Anyone have an idea?

Here's my main Webview:

public class MyWebView: Xamarin.Forms.WebView
{

    public static readonly BindableProperty UrlProperty = BindableProperty.Create(
        propertyName: "CurrentUrl",
        returnType: typeof(string),
        declaringType: typeof(MyWebView),
        defaultValue: default(string));

    public string CurrentUrl
    {
        get { return (string)GetValue(UrlProperty); }
        set { SetValue(UrlProperty, value); }
    }
}

And here's my Renderer in Project.Droid:

[assembly: ExportRenderer(typeof(MyWebView), typeof(MyProject.Droid.WebViewRenderer))]
namespace Manateq.Droid
{
    public class WebViewRenderer : Xamarin.Forms.Platform.Android.WebViewRenderer
    {
        public WebViewRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Xamarin.Forms.WebView> e)
        {

            base.OnElementChanged(e);
            Control.SetWebViewClient(new Callback(Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity));
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            MyWebView myWebView = sender as MyWebView;
            if (e.PropertyName == "CurrentUrl")
            {
            }
        }
    }

    public class Callback : WebViewClient
    {
        Activity _context;
        public Callback(Activity _context)
        {
            this._context = _context;
        }
        public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, string url)
        {
            //view.LoadUrl(url);
            Intent i = new Intent(Intent.ActionView, Uri.Parse(url));
            _context.StartActivity(i);
            return true;
        }
        public override void OnPageStarted(Android.Webkit.WebView view, string url, Android.Graphics.Bitmap favicon)
        {
            base.OnPageStarted(view, url, favicon);
            DependencyService.Get<ILoadingIndicator>().Show();
        }
        public override void OnPageFinished(Android.Webkit.WebView view, string url)
        {
            base.OnPageFinished(view, url);
            //element.CurrentUrl = url;
            DependencyService.Get<ILoadingIndicator>().Dismiss();
        }
    }
}

And here's I'm using the custom web view in xaml:

<customControls:MyWebView VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" x:Name="webView"/>
2 Answers

You can got the value of CurrentUrl by MyWebView myWebView = e.NewElement as MyWebView; var currentUrl=myWebView.CurrentUrl; in OnElementChanged; Then you can transfer this value to Callback's constructor. In the end, you can set it in the OnPageFinished method by element.CurrentUrl = currenturl; like following code.

[assembly: ExportRenderer(typeof(MyWebView), typeof(WebViewRenderer))]
namespace WebviewDemo.Droid
{
    public class WebViewRenderer : Xamarin.Forms.Platform.Android.WebViewRenderer
    {
        public WebViewRenderer(Context context) : base(context)
        {
        }
        protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Xamarin.Forms.WebView> e)
        {
           
            base.OnElementChanged(e);
          
            MyWebView myWebView = e.NewElement as MyWebView;
            var currentUrl=myWebView.CurrentUrl;
            Control.SetWebViewClient(new Callback(Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity, currentUrl));
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            MyWebView myWebView = sender as MyWebView;
            if (e.PropertyName == "CurrentUrl")
            {
            }
        }
    }

    public class Callback : WebViewClient
    {
        Activity _context;
        string currenturl;
        public Callback(Activity _context,string currenturl)
        {
            this._context = _context;
            this.currenturl = currenturl;
        }
        public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, string url)
        {
            //view.LoadUrl(url);
            Intent i = new Intent(Intent.ActionView, Uri.Parse(url));
            _context.StartActivity(i);
            return true;
        }
        public override void OnPageStarted(Android.Webkit.WebView view, string url, Android.Graphics.Bitmap favicon)
        {
            base.OnPageStarted(view, url, favicon);
            DependencyService.Get<ILoadingIndicator>().Show();
        }
        public override void OnPageFinished(Android.Webkit.WebView view, string url)
        {
            base.OnPageFinished(view, url);
            element.CurrentUrl = currenturl;
            DependencyService.Get<ILoadingIndicator>().Dismiss();
        }
    }
}

Do you want to achieve the result like this GIF?

enter image description here

If so, you can load the current url in the OnPageFinished method.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Webkit;
using Android.Widget;
using WebviewDemo;
using WebviewDemo.Droid;
using Xamarin.Forms;

[assembly: ExportRenderer(typeof(MyWebView), typeof(WebViewRenderer))]
namespace WebviewDemo.Droid
{
    public class WebViewRenderer : Xamarin.Forms.Platform.Android.WebViewRenderer
    {
        public WebViewRenderer(Context context) : base(context)
        {
        }
        protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Xamarin.Forms.WebView> e)
        {
           
            base.OnElementChanged(e);
          
            MyWebView myWebView = e.NewElement as MyWebView;
            var currentUrl=myWebView.CurrentUrl;
            Control.SetWebViewClient(new Callback(Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity, currentUrl));
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            MyWebView myWebView = sender as MyWebView;
            if (e.PropertyName == "CurrentUrl")
            {

            }
        }
    }

    public class Callback : WebViewClient
    {
        Activity _context;
        string currenturl;
        
        public Callback(Activity _context,string currenturl)
        {
            this._context = _context;
            this.currenturl = currenturl;
        }


        public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, string url)
        {
            //if (!loadingFinished)
            //{
            //    redirect = true;
            //}

            //loadingFinished = false;
            //view.LoadUrl(currenturl);
            //Console.WriteLine("Loading web view...");
            return true;
        }

        public override void OnPageStarted(Android.Webkit.WebView view, string url, Android.Graphics.Bitmap favicon)
        {
            base.OnPageStarted(view, url, favicon);
           
        }

        public override void OnPageFinished(Android.Webkit.WebView view, string url)
        {
            
            if (url.Equals("https://www.google.com/"))
            {
                view.LoadUrl(currenturl);
            }
           
        }

        //public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, string url)
        //{
        //    //view.LoadUrl(url);

        //    //   Intent i = new Intent(Intent.ActionView, Uri.Parse(url));
        //    //  _context.StartActivity(i);

        //    view.LoadUrl(url);
        //    isRedirected = true;
        //    return true;
        //}

        //public override void OnPageStarted(Android.Webkit.WebView view, string url, Android.Graphics.Bitmap favicon)
        //{
        //    base.OnPageStarted(view, url, favicon);

        //    if (!isRedirected)
        //    {
        //        //Do something you want when starts loading
        //    }

        //    isRedirected = false;
        //    // DependencyService.Get<ILoadingIndicator>().Show();
        //}
        //public override void OnPageFinished(Android.Webkit.WebView view, string url)
        //{
        //    base.OnPageFinished(view, url);
        //    if (!isRedirected)
        //    {
        //        //Do something you want when finished loading 
               
        //    }
           
        //   // element.CurrentUrl = currenturl;
        //  //  DependencyService.Get<ILoadingIndicator>().Dismiss();
        //}
    }
}

In the xaml. You can bind the value for CurrentUrl, Or just set it directly.

 <customControls:MyWebView VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" x:Name="webView"/>

Xaml background code.

 public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
               webView.Source = "https://www.google.com";
             webView.CurrentUrl = "https://www.baidu.com";
        }
    }

The answer provided by Leon is good. But I found an easier way to do it. Here's what I've done.

In the Callback class that extends WebviewClient I've edited OnPageStarted and OnPageFinished to be as following:

public override void OnPageStarted(Android.Webkit.WebView view, string url, Android.Graphics.Bitmap favicon)
{
    DependencyService.Get<ILoadingIndicator>().Show();
    base.OnPageStarted(view, url, favicon);
    var args = new WebNavigatingEventArgs(WebNavigationEvent.NewPage, new UrlWebViewSource { Url = url }, url);
    _renderer.ElementController.SendNavigating(args);
}
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
    DependencyService.Get<ILoadingIndicator>().Dismiss();
    base.OnPageFinished(view, url);
    var source = new UrlWebViewSource { Url = url };
    var args = new WebNavigatedEventArgs(WebNavigationEvent.NewPage, source, url, WebNavigationResult.Success);
    _renderer.ElementController.SendNavigated(args);
}

As for _rendered its: ElementController => Element; and it exist in the WebViewRenderer class.

When adding these line of codes, the Navigated event is being hit normally. So when that happens we can simply use the url parameter that exist in Navigated event.

Related