Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How To Implement a Mask for a Text Box in W.P.F.

0.00/5 (No votes)
25 Feb 2016 4  
I needed a simple way to implement a masked text box for various uses, including phone numbers and S.S.N. After looking around, I found only 3rd party and overly complex solutions.

Introduction

After trying several different approaches, I finally settled on creating a custom class derived from TextBox. Then very simply added a custom public enum property "Mask" to my custom class for selecting the masked type. Next, I subscribe to the TextChanged event in the constructor of my class and this is where I'll apply any logic needed to filter input, then apply our need mask.

Setting the Mask Type

Our custom TextBox which I've called "MaskedTextBox" will make use of a custom enum called "TextBoxMask". So if you are using VS 2010 or greater, you can begin by creating a new WPF project and calling it what you wish, in my case "WpfApplication6". Then right click on the project in project explorer and select Add>New>Class and add the enum listed below to the class file.

public enum TextBoxMask
    {
        Phone7Digit,
        Phone7DigitWithExt,
        Phone10Digit,
        Phone10DigitWithExt,
        Phone11Digit,
        Phone11DigitWithExt,        
        SSN
    }

These are the masks options I've used so far. You can remove or alter masks to suit your needs.

You can add your custom TextBox class to the same class file as below:

public class MaskedTextBox : TextBox
{
        public TextBoxMask Mask { get; set; }

        public MaskedTextBox()
        {
            this.TextChanged += new TextChangedEventHandler(MaskedTextBox_TextChanged);
        }

        void MaskedTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            this.CaretIndex = this.Text.Length;

            var tbEntry = sender as MaskedTextBox;

            if (tbEntry != null && tbEntry.Text.Length > 0)
            {
                tbEntry.Text = formatNumber(tbEntry.Text, tbEntry.Mask);
            }                
        }
        
        public static string formatNumber(string MaskedNum, TextBoxMask phoneFormat)
        {
            int x;           
            StringBuilder sb = new StringBuilder();
            StringBuilder sb2 = new StringBuilder();
            
            if (MaskedNum != null)
            {
                for (int i = 0; i < MaskedNum.Length; i++)
                {
                    if (int.TryParse(MaskedNum.Substring(i, 1), out x))
                    {
                        sb.Append(x.ToString());                        
                    }                    
                }
                switch (phoneFormat)
                {
                    case TextBoxMask.Phone7Digit:
                        return FormatFor7DigitPhone(sb.ToString());
                        
                    case TextBoxMask.Phone7DigitWithExt:
                        return FormatFor7DigitPhoneWithExt(sb.ToString());
                        
                    case TextBoxMask.Phone10Digit:
                        return FormatFor10DigitPhone(sb.ToString());
                        
                    case TextBoxMask.Phone10DigitWithExt:
                        return FormatFor10DigitPhoneWithExt(sb.ToString());
                        
                    case TextBoxMask.Phone11Digit:
                        return FormatFor11DigitPhone(sb.ToString());
                        
                    case TextBoxMask.Phone11DigitWithExt:
                        return FormatFor11DigitPhoneWithExt(sb.ToString());
                        
                    case TextBoxMask.SSN:
                        return FormatForSSN(sb.ToString());
                        
                    default:
                        break;
                }                
                
            }
            return sb.ToString();    
        }
}

public enum TextBoxMask
{
        Phone7Digit,
        Phone7DigitWithExt,
        Phone10Digit,
        Phone10DigitWithExt,
        Phone11Digit,
        Phone11DigitWithExt,
        SSN
}

In the Text Changed Event, first we move the Caret to the end of the text. Then, if the string is not null or 0 length, we pass the unformatted text and mask to the formatNumber method. Inside of formatNumber, we then remove all non numeric characters and pass both StringBuilders to the appropriate method based on which mask type is selected.

To save a little time and space, I've included the methods to format a 10 Digit phonenumber and SSN only. You can expand on those with your own formatting options.

public static StringBuilder FormatFor10DigitPhone(string sb)
        {
            StringBuilder sb2 = new StringBuilder();

            if (sb.Length > 0) sb2.Append("(");

            if (sb.Length > 0) sb2.Append(sb.Substring(0, 1));
            if (sb.Length > 1) sb2.Append(sb.Substring(1, 1));
            if (sb.Length > 2) sb2.Append(sb.Substring(2, 1));

            if (sb.Length > 3) sb2.Append(") ");

            if (sb.Length > 3) sb2.Append(sb.Substring(3, 1));
            if (sb.Length > 4) sb2.Append(sb.Substring(4, 1));
            if (sb.Length > 5) sb2.Append(sb.Substring(5, 1));

            if (sb.Length > 6) sb2.Append("-");

            if (sb.Length > 6) sb2.Append(sb.Substring(6, 1));
            if (sb.Length > 7) sb2.Append(sb.Substring(7, 1));
            if (sb.Length > 8) sb2.Append(sb.Substring(8, 1));
            if (sb.Length > 9) sb2.Append(sb.Substring(9, 1)); 

            return sb2;
        }

and for SSN:

public static StringBuilder FormatForSSN(String sb)
        {
            StringBuilder sb2 = new StringBuilder();

            if (sb.Length > 0) sb2.Append(sb.Substring(0, 1));
            if (sb.Length > 1) sb2.Append(sb.Substring(1, 1));
            if (sb.Length > 2) sb2.Append(sb.Substring(2, 1));

            if (sb.Length > 3) sb2.Append("-");

            if (sb.Length > 3) sb2.Append(sb.Substring(3, 1));
            if (sb.Length > 4) sb2.Append(sb.Substring(4, 1));

            if (sb.Length > 5) sb2.Append("-");

            if (sb.Length > 5) sb2.Append(sb.Substring(5, 1));
            if (sb.Length > 6) sb2.Append(sb.Substring(6, 1));
            if (sb.Length > 7) sb2.Append(sb.Substring(7, 1));
            if (sb.Length > 8) sb2.Append(sb.Substring(8, 1));

            return sb2;
        }

XAML

The following code is from my MainWindow XAML:

<Window 
    x:Class="WpfApplication6.MainWindow"
        xmlns:dc="clr-namespace:WpfApplication6"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication6"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    
    <Grid>
        <dc:MaskedTextBox Mask="SSN" 
        Height="23" Width="150"></dc:MaskedTextBox>
    </Grid>
</Window>

I've added only two lines:

xmlns:dc="clr-namespace:WpfApplication6"

in the Window declaration and for our masked text box:

<Grid>
        <dc:MaskedTextBox Mask="SSN" 
        Height="23" Width="150"></dc:MaskedTextBox>
    </Grid>

Conclusion

There is plenty of room to reduce, reuse, and refactor, but this should give you a jumping off point to experiment with your own mask types.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here