Underlined Label Text in Xamarin.Forms

This is how you underline a Text of a Label using Effects.

Posted by Sven-Michael Stübe on August 29, 2016

I saw a question on stackoverflow and thought it would be a nice use case for Effects.

Problem

The FontAttributes contain only None, Bold and Italic. If you want to underline your text, you have to implement it on your own.

Effects

Xamarin.Forms Effects allow you to customize the underlying native controls without writing custom renderers and custom controls. Creating your own effects is really easy if you follow the linked tutorial.

Core
To be able to use the effect in you Xaml, you have to create a RoutingEffect in your Core project.

public class UnderlineEffect : RoutingEffect
{
	public const string EffectNamespace = "Example";

	public UnderlineEffect() : base($"{EffectNamespace}.{nameof(UnderlineEffect)}")
	{
	}
}

I introduced EffectNamespace to add some refactoring safety, because this namespace is used in the platform specific implementation, too.

Add the Effect to your Label:

<Label Text="Welcome to underlined Xamarin Forms!"
       VerticalOptions="Center"
       HorizontalOptions="Center">
    <Label.Effects>
        <local:UnderlineEffect />
    </Label.Effects>
</Label>

Android

On Android it’s pretty easy to underline the whole text. You just have to set the paint flag for it. You should listen to some property changes (e.g. Text and FormattedText), because the renderer might reset the paint flags. You should add the flag using the OR-operator in combination with the old flags, to keep the already set flags. When you remove the effect, you should remove via the inverse bit operation.

[assembly: ResolutionGroupName(UnderlineLabel.UnderlineEffect.EffectNamespace)]
[assembly: ExportEffect(typeof(UnderlineEffect), nameof(UnderlineEffect))]
namespace UnderlineLabel.Droid
{
    public class UnderlineEffect : PlatformEffect
    {
        protected override void OnAttached()
        {
            SetUnderline(true);
        }

        protected override void OnDetached()
        {
            SetUnderline(false);
        }

        protected override void OnElementPropertyChanged(System.ComponentModel.PropertyChangedEventArgs args)
        {
            base.OnElementPropertyChanged(args);

            if (args.PropertyName == Label.TextProperty.PropertyName || args.PropertyName == Label.FormattedTextProperty.PropertyName)
            {
                SetUnderline(true);
            }
        }

        private void SetUnderline(bool underlined)
        {
            try
            {
                var textView = (TextView)Control;
                if (underlined)
                {
                    textView.PaintFlags |= PaintFlags.UnderlineText;
                }
                else
                {
                    textView.PaintFlags &= ~PaintFlags.UnderlineText;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Cannot underline Label. Error: ", ex.Message);
            }
        }
    }
}

iOS

Underlining a Text on iOS is as easy as on Android. You have to add the UnderlineStyle attribute to the AttributedText.

[assembly: ResolutionGroupName(UnderlineLabel.UnderlineEffect.EffectNamespace)]
[assembly: ExportEffect(typeof(UnderlineEffect), nameof(UnderlineEffect))]
namespace UnderlineLabel.iOS
{
    public class UnderlineEffect : PlatformEffect
    {
        protected override void OnAttached()
        {
            SetUnderline(true);
        }

        protected override void OnDetached()
        {
            SetUnderline(false);
        }

        protected override void OnElementPropertyChanged(System.ComponentModel.PropertyChangedEventArgs args)
        {
            base.OnElementPropertyChanged(args);

            if (args.PropertyName == Label.TextProperty.PropertyName || args.PropertyName == Label.FormattedTextProperty.PropertyName)
            {
                SetUnderline(true);
            }
        }

        private void SetUnderline(bool underlined)
        {
            try
            {
                var label = (UILabel)Control;
                var text = (NSMutableAttributedString)label.AttributedText;
                var range = new NSRange(0, text.Length);

                if (underlined)
                {
                    text.AddAttribute(UIStringAttributeKey.UnderlineStyle, NSNumber.FromInt32((int)NSUnderlineStyle.Single), range);
                }
                else
                {
                    text.RemoveAttribute(UIStringAttributeKey.UnderlineStyle, range);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Cannot underline Label. Error: ", ex.Message);
            }
        }
    }
}

Conclusion

It’s very easy to customize native controls with Effects.
Keep in mind, that this is a solution for Labels with simple text. If you want to underline just some parts of the Text using FormattedText, you might have a problem :) See my post on FormattedText for more information on the problem.

Code Sample

The implementation of the Effect are pushed to a github project
( stackoverflow-answers/xamarin-forms-underline). Feel free to use these if you want to play around or have a closer look to effects.

If you like the answer, I'd appreciate an upvote ;)
http://stackoverflow.com/a/39200923/1489968
Thanks!


Background Photo by Karen / CC BY
Found a typo? Send me a pull request!