Masked Entry in Xamarin.Forms

A mask on an entry field is way to format the input into something more human readable. For example, a phone number may look like +61 400 555 555, or (555) 555-555. There are many ways to implement a mask. For maximum configurability, you would use Regex, however for most simple cases, we can implement something much easier.

Behavior

The easiest way to implement a mask, is through a Behavior. This code will set the char positions that each mask character is meant to be located at. For example, in this code, we will choose X as our special character. Hence a mask of (XXX) XXX-XXX, will mean that spaces, brackets and dashes will all be included as part of the non-user typed entry.

public class MaskedBehavior : Behavior<Entry>
{
    private string _mask = "";
    public string Mask
    {
        get => _mask;
        set
        {
            _mask = value;
            SetPositions();
        }
    }

    protected override void OnAttachedTo(Entry entry)
    {
        entry.TextChanged += OnEntryTextChanged;
        base.OnAttachedTo(entry);
    }

    protected override void OnDetachingFrom(Entry entry)
    {
         entry.TextChanged -= OnEntryTextChanged;
         base.OnDetachingFrom(entry);
    }

    IDictionary<int, char> _positions;

    void SetPositions()
    {
        if (string.IsNullOrEmpty(Mask))
        {
            _positions = null;
            return;
        }

        var list = new Dictionary<int, char>();
        for (var i = 0; i < Mask.Length; i++)
            if (Mask[i] != 'X')
                list.Add(i, Mask[i]);

        _positions = list;
    }

    private void OnEntryTextChanged(object sender, TextChangedEventArgs args)
    {
        var entry = sender as Entry;

        var text = entry.Text;

        if (string.IsNullOrWhiteSpace(text) || _positions == null)
            return;

        if (text.Length > _mask.Length)
        {
            entry.Text = text.Remove(text.Length - 1);
            return;
        }

        foreach (var position in _positions)
            if (text.Length >= position.Key + 1)
            {
                var value = position.Value.ToString();
                if (text.Substring(position.Key, 1) != value)
                    text = text.Insert(position.Key, value);
            }

        if (entry.Text != text)
            entry.Text = text;
    }
}

This code, will also limit the amount of text you can enter, up to the mask.

Apply Mask

Now you want to apply your mask to an Entry element. Here I have added the MaskedBehavior, and added a mask of (XXX) XXX-XXX.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:behavior="clr-namespace:Enterprise.Behaviors"
             x:Class="Enterprise.View.Features.Main.MainView">
    <ContentPage.Content>
        <StackLayout VerticalOptions="Center">
            <Entry Keyboard="Numeric">
                <Entry.Behaviors>
                    <behavior:MaskedBehavior Mask="(XXX) XXX-XXX" />
                </Entry.Behaviors>
            </Entry>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

The mask can be anything you want, with the X as the character that the user types in.

Demonstration

Here you can see the entry of a phone number, and it automatically applies the mask, as they type.

Summary

To be a truly generic masked behavior, we would need to implement regex. Regex, while powerful can be difficult to understand, for many programmers perspectives, hence I like this masked behavior, to clearly indicate what you want. You can easily expand this behavior to include additional checks such as if it is a digit.


Posted

in

by

Tags: