3

The problem occurs on Android.

I have implemented a custom renderer for a WebView to get the capability of resizing the height request base on its content.

I took this from a xamarin forum post.

[assembly: ExportRenderer(typeof(AutoHeightWebView), typeof(AutoHeightWebViewRenderer))]
namespace MyProject.Droid.Renderers
{
    public class AutoHeightWebViewRenderer : WebViewRenderer
    {

        public AutoHeightWebViewRenderer(Context context): base(context) {}

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
        {
            base.OnElementChanged(e);

            if (e.NewElement is AutoHeightWebView webViewControl)
            {
                if (e.OldElement == null)
                {
                    Control.SetWebViewClient(new ExtendedWebViewClient(webViewControl));
                }
            }
        }

        class ExtendedWebViewClient : Android.Webkit.WebViewClient
        {
            private readonly AutoHeightWebView _control;

            public ExtendedWebViewClient(AutoHeightWebView control)
            {
                _control = control;
            }

            public override async void OnPageFinished(Android.Webkit.WebView view, string url)
            {
                if (_control != null)
                {
                    int i = 10;
                    while (view.ContentHeight == 0 && i-- > 0) // wait here till content is rendered
                    {
                        await System.Threading.Tasks.Task.Delay(100);
                    }

                    _control.HeightRequest = view.ContentHeight;
                }

                base.OnPageFinished(view, url);
            }
        }
    }
}

Based on a certain logic, I change the source of the WebView and use the custom renderer to resize the view.

This works when the size is increased but not when the content size is smaller than the one before...

The be clearer, if I set the source of the WebView to a html file that is 200px height and change it to a html file that is 1000px, it works fine and I can see all the content. BUT, if I try to go back to my 200px html file, I get a 800px blank space underneath since the content doesn't change on the view.ContentHeight and keep the value of 1000px.

I followed this issue/thread and didn't find a solution to resolve this problem : https://github.com/xamarin/Xamarin.Forms/issues/1711

I have seen a lot of topics on Android saying that we need to recreate the webview. Is there any other solution?

Chetan Rawat
  • 578
  • 3
  • 17
  • Have you try with a regular `WebView` wrapped in a `Grid` with `RowDefinition` set to `Auto` ? – FabriBertani Feb 25 '20 at 17:56
  • 1
    Yes and I have to manually adjust the height or I can't see the content. If I'm not mistaken, the content appears after the rendering of the view, so it's likely that the height is set to 0 since the file is not loaded. – Christopher St-Pierre Feb 25 '20 at 18:55
  • Try setting WebView's IsVisible = false, then back to true. Might need to do the second set (=true) on UI thread after a delay. Hacky, but its an alternative to recreating the webview. – ToolmakerSteve Feb 25 '20 at 23:20
  • @ChristopherSt-Pierre I can reproduce your issue according to your code, I suggest you can [invoke C# from JavaScript](https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/hybridwebview#Invoking_C_from_JavaScript) to get the content height, you can take a look:https://forums.xamarin.com/discussion/160787/what-is-the-best-way-to-render-a-webview-with-source-as-htmlwebviewsource-html – Cherry Bu - MSFT Feb 26 '20 at 07:20

1 Answers1

1

I found a way to do it based on this thread.

I first tried with a JS command that returns the body height : document.body.scrollHeight. The problem was the same, it always returned the largest size, but never decreased the size.

As the JS command have no problem to increase the size, I had to set the height to 0, set a 100ms delay (arbitrary) and then get the height of the HTML with the JS command above and set the HeightRequest of the view.

With this, the HTML body height will not decrease... It will always start from 0 and increase to the HTML body size.

Final result :

public class AutoHeightWebViewRenderer : WebViewRenderer
{
    static AutoHeightWebView _xwebView = null;

    public AutoHeightWebViewRenderer(Android.Content.Context context) : base(context)
    {
    }

    class DynamicSizeWebViewClient : WebViewClient
    {
        public async override void OnPageFinished(Android.Webkit.WebView view, string url)
        {
            try
            {
                if (_xwebView != null)
                {
                    view.Settings.JavaScriptEnabled = true;
                    view.Settings.DomStorageEnabled = true;
                    _xwebView.HeightRequest = 0d;

                    await Task.Delay(100);

                    string result = await _xwebView.EvaluateJavaScriptAsync("(function(){return document.body.scrollHeight;})()");
                    _xwebView.HeightRequest = Convert.ToDouble(result);
                }
                base.OnPageFinished(view, url);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"{ex.Message}");
            }
        }
    }

    protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
    {
        base.OnElementChanged(e);
        _xwebView = e.NewElement as AutoHeightWebView;

        if (e.OldElement == null)
        {
            Control.SetWebViewClient(new DynamicSizeWebViewClient());
        }
    }
}