What is the best way to render a WebView with Source as HtmlWebviewSource Html = <"html text">

skadookkunnanskadookkunnan Member ✭✭
edited July 3 in Xamarin.Forms

I am currently working on an app feature where I need to show text in HTML format from server side coming via REST APIs.

The size of the text/html content varies per row and I trying to show the data in a scroll view containing multiple WebViews inside a big stack layout

My single row component looks like this,

<ContentView x:Name="RowComponent">

    <Grid>

        <Grid.RowDefinitions>

                    <RowDefinition Height="Auto" />

                    <RowDefinition Height="Auto" />

                    <RowDefinition Height="*" />

        </Grid.RowDefinitions>

        <Label Grid.Row=0/>

        <Label Grid.Row=1/>

        <WebView Grid.Row=2>

            <WebView.Source>

                <HtmlWebViewSource Html="{Binding FormattedMessage}" />

            </WebView.Source>

        </WebView>

    </Grid>

<ContentView>

And also the rows are repeated inside a content page like the following,

<ContentPage>

    <ScrollView>

        <StackLayout>

            <StackLayout>

                <StackLayout />

                <StackLayout>

                    <!-- Inside this there are multiple rows of the above component '**RowComponent**' -->

                </StackLayout>

            </StackLayout>

            <StackLayout/>

        </StackLayout>

    </ScrollView>

</ContentPage>

According to the Xamarin Forms documentation about WebView: "Grid without WidthRequest & HeightRequest. Grid is one of the few layouts that does not require specifying requested heights and widths." (Xamarin.Forms WebView Layout)

However, the WebView is not even showing up or showing any content. How can I fix this?

Thank you in advance.

Best Answer

  • skadookkunnanskadookkunnan ✭✭
    edited July 4 Accepted Answer

    Got the issue resolved using Custom Renderers mentioned in the previous post/GitHub issue.

    Following is the Source for iOS and Android Custom renderers respectively,

    iOS Renderer:

    using System;
    using Foundation;
    using MyProject.Controls;
    using MyProject.iOS.CustomRenderers;
    using UIKit;
    using Xamarin.Forms;
    using Xamarin.Forms.Platform.iOS;
    
    // Based on the Reference of Xamarin.Forms GitHub issue: https://github.com/xamarin/Xamarin.Forms/issues/1711
    
    [assembly: ExportRenderer(typeof(DynamicSizeWebView), typeof(IosDynamicSizeWebViewRenderer))]
    namespace MyProject.iOS.CustomRenderers
    {
        public class IosDynamicSizeWebViewRenderer : WebViewRenderer
        {
            protected override void OnElementChanged(VisualElementChangedEventArgs e)
            {
                try
                {
                    base.OnElementChanged(e);
    
                    if (NativeView != null)
                    {
                        var webView = (UIWebView)NativeView;
    
                        webView.Opaque = false;
                        webView.BackgroundColor = UIColor.Clear;
                    }
    
                    Delegate = new DynamicSizeWebViewDelegate(this);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Error at IosDynamicSizeWebViewRenderer OnElementChanged: " + ex.Message);
                }
            }
        }
    
        class DynamicSizeWebViewDelegate : UIWebViewDelegate
        {
            IosDynamicSizeWebViewRenderer webViewRenderer;
    
            public DynamicSizeWebViewDelegate(IosDynamicSizeWebViewRenderer _webViewRenderer = null)
            {
                webViewRenderer = _webViewRenderer ?? new IosDynamicSizeWebViewRenderer();
            }
    
            public override async void LoadingFinished(UIWebView webView)
            {
                try
                {
                    var _webView = webViewRenderer.Element as DynamicSizeWebView;
                    if (_webView != null)
                    {
                        await System.Threading.Tasks.Task.Delay(100); // wait here till content is rendered
                        _webView.HeightRequest = (double)webView.ScrollView.ContentSize.Height;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Error at ExtendedWebViewRenderer LoadingFinished: " + ex.Message);
                }
            }
        }
    }
    

    Android Renderer:

    using System;
    using System.Threading.Tasks;
    using Android.Content;
    using Android.Webkit;
    using MyProject.Controls;
    using MyProject.Droid.CustomRenderers;
    using Xamarin.Forms;
    using Xamarin.Forms.Platform.Android;
    using WebView = Android.Webkit.WebView;
    
    [assembly: ExportRenderer(typeof(DynamicSizeWebView), typeof(AndroidDynamicSizeWebViewRenderer))]
    namespace MyProject.Droid.CustomRenderers
    {
        public class AndroidDynamicSizeWebViewRenderer : WebViewRenderer
        {
            public static int _webViewHeight;
            static DynamicSizeWebView _xwebView = null;
            WebView _webView;
    
            public AndroidDynamicSizeWebViewRenderer(Android.Content.Context context) : base(context)
            {
            }
    
            class DynamicSizeWebViewClient : WebViewClient
            {
                WebView _webView;
                public async override void OnPageFinished(WebView view, string url)
                {
                    try
                    {
                        _webView = view;
                        if (_xwebView != null)
                        {
                            view.Settings.JavaScriptEnabled = true;
                            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 DynamicSizeWebView;
                _webView = Control;
    
                if (e.OldElement == null)
                {
                    _webView.SetWebViewClient(new DynamicSizeWebViewClient());
                }
    
            }
        }
    }
    

    How to use it in XAML:

    <controls:DynamicSizeWebView BackgroundColor="Transparent"
                                                             Margin="0">
        <WebView.HeightRequest>
            <OnPlatform iOS="20" />
        </WebView.HeightRequest>
    
        <WebView.Source>
            <HtmlWebViewSource Html="{Binding FormattedHTML}" />
        </WebView.Source>
    </controls:DynamicSizeWebView>
    

    Feel free to use and Happy coding.

Answers

  • skadookkunnanskadookkunnan Member ✭✭

    Looks like there is a know issue in the WebView according GitHub and we probably need to wait. Only option is the latest solution given in the Github issue: https://github.com/xamarin/Xamarin.Forms/issues/1711

    Will implement and update on this thread.

  • LeonLuLeonLu Member, Xamarin Team Xamurai

    Ok, waitting for your update.

  • skadookkunnanskadookkunnan Member ✭✭
    edited July 4 Accepted Answer

    Got the issue resolved using Custom Renderers mentioned in the previous post/GitHub issue.

    Following is the Source for iOS and Android Custom renderers respectively,

    iOS Renderer:

    using System;
    using Foundation;
    using MyProject.Controls;
    using MyProject.iOS.CustomRenderers;
    using UIKit;
    using Xamarin.Forms;
    using Xamarin.Forms.Platform.iOS;
    
    // Based on the Reference of Xamarin.Forms GitHub issue: https://github.com/xamarin/Xamarin.Forms/issues/1711
    
    [assembly: ExportRenderer(typeof(DynamicSizeWebView), typeof(IosDynamicSizeWebViewRenderer))]
    namespace MyProject.iOS.CustomRenderers
    {
        public class IosDynamicSizeWebViewRenderer : WebViewRenderer
        {
            protected override void OnElementChanged(VisualElementChangedEventArgs e)
            {
                try
                {
                    base.OnElementChanged(e);
    
                    if (NativeView != null)
                    {
                        var webView = (UIWebView)NativeView;
    
                        webView.Opaque = false;
                        webView.BackgroundColor = UIColor.Clear;
                    }
    
                    Delegate = new DynamicSizeWebViewDelegate(this);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Error at IosDynamicSizeWebViewRenderer OnElementChanged: " + ex.Message);
                }
            }
        }
    
        class DynamicSizeWebViewDelegate : UIWebViewDelegate
        {
            IosDynamicSizeWebViewRenderer webViewRenderer;
    
            public DynamicSizeWebViewDelegate(IosDynamicSizeWebViewRenderer _webViewRenderer = null)
            {
                webViewRenderer = _webViewRenderer ?? new IosDynamicSizeWebViewRenderer();
            }
    
            public override async void LoadingFinished(UIWebView webView)
            {
                try
                {
                    var _webView = webViewRenderer.Element as DynamicSizeWebView;
                    if (_webView != null)
                    {
                        await System.Threading.Tasks.Task.Delay(100); // wait here till content is rendered
                        _webView.HeightRequest = (double)webView.ScrollView.ContentSize.Height;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Error at ExtendedWebViewRenderer LoadingFinished: " + ex.Message);
                }
            }
        }
    }
    

    Android Renderer:

    using System;
    using System.Threading.Tasks;
    using Android.Content;
    using Android.Webkit;
    using MyProject.Controls;
    using MyProject.Droid.CustomRenderers;
    using Xamarin.Forms;
    using Xamarin.Forms.Platform.Android;
    using WebView = Android.Webkit.WebView;
    
    [assembly: ExportRenderer(typeof(DynamicSizeWebView), typeof(AndroidDynamicSizeWebViewRenderer))]
    namespace MyProject.Droid.CustomRenderers
    {
        public class AndroidDynamicSizeWebViewRenderer : WebViewRenderer
        {
            public static int _webViewHeight;
            static DynamicSizeWebView _xwebView = null;
            WebView _webView;
    
            public AndroidDynamicSizeWebViewRenderer(Android.Content.Context context) : base(context)
            {
            }
    
            class DynamicSizeWebViewClient : WebViewClient
            {
                WebView _webView;
                public async override void OnPageFinished(WebView view, string url)
                {
                    try
                    {
                        _webView = view;
                        if (_xwebView != null)
                        {
                            view.Settings.JavaScriptEnabled = true;
                            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 DynamicSizeWebView;
                _webView = Control;
    
                if (e.OldElement == null)
                {
                    _webView.SetWebViewClient(new DynamicSizeWebViewClient());
                }
    
            }
        }
    }
    

    How to use it in XAML:

    <controls:DynamicSizeWebView BackgroundColor="Transparent"
                                                             Margin="0">
        <WebView.HeightRequest>
            <OnPlatform iOS="20" />
        </WebView.HeightRequest>
    
        <WebView.Source>
            <HtmlWebViewSource Html="{Binding FormattedHTML}" />
        </WebView.Source>
    </controls:DynamicSizeWebView>
    

    Feel free to use and Happy coding.

Sign In or Register to comment.