Make URL Text Label clickable on Xamarin.Forms (CodeSnipp)

Hi everyone,

So we are still on the XamarinUIJuly, the guys are delivering such a good set of post regarding of what Xamarin.Forms is capable of and I belive it was a good time to send this snipped too.

I was requested to provide the capability of make partial text clickable say for example we have

New link xamarin on  microsoft.com remember to change your bookmarks

By default the Labels controls do not support this behavior, we can implement Span for the FormattedText and add a TapEvent to solve the issue, but… you can control how this happen when your content is dynamically created.

So… digging to find some light on the topic I found the very useful post of Adam Pedley, which talk about converting html link tags to clickable spans on xamarin.forms, I had to change a few things to make it work for my needs.

  • We use a converter
  • We analyze every word in the string to see if is a valid link (with the help of a Regular Expression)
  • Some tweaks too: fix a few bugs calculating between words and  display different colors between light and dark mode (had to put this into a chat with bubbles of different colors T_T )

Here’s is the Converter Snipped (It only cover links, because that was the only thing needed)

public class HtmlLabelConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var formatted = new FormattedString();

            foreach (var item in ProcessString((string)value))
                formatted.Spans.Add(CreateSpan(item, parameter));

            return formatted;
        }

        private Span CreateSpan(StringSection section, object parameter)
        {
            var span = new Span()
            {
                Text = section.Text
            };

            if (!string.IsNullOrEmpty(section.Link))
            {
                span.GestureRecognizers.Add(new TapGestureRecognizer()
                {
                    Command = _navigationCommand,
                    CommandParameter = section.Link
                });
                if (parameter != null && parameter.ToString() == "light")
                {
                    span.TextColor = Color.Blue;
                }
                else if (parameter != null && parameter.ToString() == "dark")
                {
                    span.TextColor = Color.White;
                }
                else
                {
                    span.TextColor = Color.DarkBlue;
                }
                // Underline coming soon from https://github.com/xamarin/Xamarin.Forms/pull/2221
                // Currently available in Nightly builds if you wanted to try, it does work :)
                // As of 2018-07-22. But not avail in 3.2.0-pre1.
                //This was already implemented! Yey!!  > 2019-07-15
                span.TextDecorations = TextDecorations.Underline;
            }

            return span;
        }

        public IList<StringSection> ProcessString(string rawText)
        {
            const string spanPattern = @"(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:\/?#[\]@!\

amp;'\(\)\*\+,;=.]"; rawText = rawText ?? ""; MatchCollection collection = Regex.Matches(rawText, spanPattern, RegexOptions.IgnoreCase); var sections = new List<StringSection>(); var lastIndex = 0; foreach (Match item in collection) { var foundText = item.Value; //Add any string before the coincidence sections.Add(new StringSection() { Text = rawText.Substring(lastIndex, item.Index - lastIndex) }); lastIndex += item.Index + item.Length; // Get HTML href var html = new StringSection() { Link = foundText, Text = item.Value }; sections.Add(html); } //Add additional text if there any left if (rawText.Length > lastIndex) { sections.Add(new StringSection() { Text = rawText.Substring(lastIndex) }); } return sections; } public class StringSection { public string Text { get; set; } public string Link { get; set; } } private ICommand _navigationCommand = new Command<string>((url) => { Device.OpenUri(new Uri(url)); }); public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }

After that the only thing left was to include the converter on my XAML Page and do the proper binding

On the ContentPage Tag I put

xmlns:Converters="clr-namespace:Proyect01.Converters;"

And then in the control I set the Binding to…

<Label  FormattedText="{Binding data.Notes, Converter={Converters:HtmlLabelConverter},ConverterParameter=dark}" />

If you see the converter class I use the ConverterParameter just to change the color, depending of the needs

And voila! problem solved.

note: tested with http, https and long url too. i.e https://www.youtube.com/watch?v=YJ8TiRCsLl4