In the first part of this series of posts, we took an introductory look at the Xamarin Forms WebView control.  In this post we’ll use a custom renderer to implement a label control that uses the platforms native ability to display HTML strings.

Platform labels

Both iOS and Android have the ability to display HTML formatted strings in a built in control.  These particular attributes are not available directly to Xamarin Forms, but with a quick custom renderer we can get at the functionality pretty easily.

The PCL bit

We’ll start with the sample project that we started in the previous post. In the PCL, create a new class that we’ll use to inherit from the Xamarin Forms Label control:

using Xamarin.Forms;

namespace DisplayHtmlApp.UserControls
{
    public class HtmlLabel : Label
    {
    }
}

That’s it for the shared code.

Next we can implement the actual functionality on the platforms.

Android

In the Android project create a new class called HtmlLabelRenderer. I like to put custom renderers in their own namespace and folder, but that’s up to you. It’s the Assembly attribute that will let Xamarin Forms know where to find the implementation.

using System.ComponentModel;
using Android.OS;
using Android.Text;
using Android.Widget;
using DisplayHtmlApp.Droid.CustomRenderers;
using DisplayHtmlApp.UserControls;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(HtmlLabel), typeof(HtmlLabelRenderer))]
namespace DisplayHtmlApp.Droid.CustomRenderers
{

    public class HtmlLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            Control?.SetText(GetText(), TextView.BufferType.Spannable);
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (e.PropertyName == Label.TextProperty.PropertyName)
            {
                Control?.SetText(GetText(), TextView.BufferType.Spannable);
            }
        }

        private ISpanned GetText()
        {
            return (int)Build.VERSION.SdkInt >= 24
                ? Html.FromHtml(Element.Text, FromHtmlOptions.ModeLegacy)
                : Html.FromHtml(Element.Text);
        }
    }
}

You can see that we are creating an implementation of ISpanned from the Html.FromHtml method, that Android can use directly in it’s label renderer. The only slight complication is that the FromHtml method is deprecated in SDK v24, so we have to check that, and add the “ModeLegacy” enum as appropriate.

iOS

In the iOS project we’ll follow the same custom renderer pattern, but using the iOS specific NsAttributedString object.

using System.ComponentModel;
using DisplayHtmlApp.iOS.CustomRenderers;
using DisplayHtmlApp.UserControls;
using Foundation;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(HtmlLabel), typeof(HtmlLabelRenderer))]
namespace DisplayHtmlApp.iOS.CustomRenderers
{
    public class HtmlLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            UpdateElement();
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (e.PropertyName == Label.TextProperty.PropertyName)
            {
                UpdateElement();
            }
        }

        private void UpdateElement()
        {
            if (Control != null && Element != null && !string.IsNullOrWhiteSpace(Element.Text))
            {
                var attr = new NSAttributedStringDocumentAttributes();
                var nsError = new NSError();
                attr.DocumentType = NSDocumentType.HTML;

                var myHtmlData = NSData.FromString(Element.Text, NSStringEncoding.Unicode);
                Control.Lines = 0;
                Control.AttributedText = new NSAttributedString(myHtmlData, attr, ref nsError);
            }
        }
    }
}

HTML3

Wrap up

You do have a few options if you need to display HTML in your Xamarin Forms app. The WebView offers full featured, browser like capabilities with events and navigation, or you can take advantage of the platforms native ability to display HTML by implementing a simple custom renderer.

As usual, a simple example is available at the XamarinInsider repo.

Advertisement