# .NET and More > WPF, WCF, WF >  [RESOLVED] No WPF masked textbox??? Really???

## trevorjeaton

this is the most basic of the basic in my opinion and should have been included loooong ago in WPF, that's crazy.....so on to the question:

aside from 3rd party controls, or hosting a winforms container - does anybody have either xaml or codebehind (or both) to do a simple north american telephone number masked textbox in VB2010?  format of (999) 999-9999

as usual, i'm all ears and if i find something sooner i'll post it here - suggestions are DEFINITELY welcome on this one....

Thx

----------


## trevorjeaton

so at this point i'm going to try and duplicate the behavior specifically for a "mask" of north american telephone numbers as you type and would love any contributions anybody has - for starters I've restricted the allowable characters entered in to the text box as 0 thru 9, an open bracket, a closed bracket, a space, and a dash in previewtextinput (the duplication attempt is from a winforms masked textbox called mtbphone but now being used in WPF that doesn't have a maskedtextbox control):



```

Private Sub mtbPhone_PreviewTextInput(ByVal sender As Object, ByVal e As System.Windows.Input.TextCompositionEventArgs) Handles mtbPhone.PreviewTextInput
   Dim allowedchars As String = "0123456789()- "
   If allowedchars.IndexOf(CChar(e.Text)) = -1 Then e.Handled = True
End Sub
```

so how do i allow only a single right bracket, a single left bracket, a single dash, and a single space yet allow unlimited numbers in WPF? (i"ve restricted the length of the textbox to 14 for the (999) 999-9999 format) - right now you can type 14 right brackets if you want and fill the textbox.

in moving forward with this, if i can figure that out, i'd like to capture keystrokes at each of the 14 positions of the textbox and insert the mask accordingly.....suggestions are welcome guys.....and absolutely requested at this point.... :-)

----------


## techgnome

How about only allowing them to enter numbers only, and then using the string.format method to format the number using "(###) ###-####" as  the format?

-tg

----------


## trevorjeaton

here's the new code but the string format isn't firing - numeric only input is working though:



```

Private Sub mtbPhone_PreviewTextInput(ByVal sender As Object, ByVal e As System.Windows.Input.TextCompositionEventArgs) Handles mtbPhone.PreviewTextInput
        Dim allowedchars As String = "0123456789"
        If allowedchars.IndexOf(CChar(e.Text)) = -1 Then e.Handled = True
        mtbPhone.Text = String.Format("{0:(###) ###-####}", mtbPhone.Text)
End Sub
```

----------


## trevorjeaton

okay, this works on the mtbphone_LostFocus event if 10 digits are typed in and the user tabs out of the control - eg. 1234567890 gets reformatted to (123) 456-7890:



```

Private Sub mtbPhone_LostFocus(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles mtbPhone.LostFocus
        mtbPhone.Text = String.Format("({0}) {1}-{2}", mtbPhone.Text.Substring(0, 3), mtbPhone.Text.Substring(3, 3), mtbPhone.Text.Substring(6))
End Sub
```

but how can i do the above "as you type"?  obviously the substring positions in the above code do not exist yet if the user is only typing in digit number 2 at the moment for example.....

----------


## trevorjeaton

could i determine the position of the caret or the mtb.text.length and do a select case scenario?.....

----------


## bflosabre91

y not just have 3 separate text boxes? you can easily make them numeric only, allow only 3 or 4 characters per, and switch focus after the user enters the 3 or 4 digits.

----------


## trevorjeaton

okay this one was fun, and is not only corrected as a north american phone number mask, but as a fully masked textbox for wpf - my hat goes off to matthew macdonald and his apress book "Pro WPF with VB 2008" - i used vb 2010 in this case and had the same results.

Here's what you do:

 - right-click your project and select add, then class
 - call the class MaskedTextBox.vb
 - add the imports statement at the very top



```
Imports System.ComponentModel
```

 - paste the following code between 'Public Class MaskedTextBox' and 'End Class'



```

Inherits System.Windows.Controls.TextBox

    Public Shared maskproperty As DependencyProperty

    Shared Sub New()
        maskproperty = DependencyProperty.Register("Mask", GetType(String), GetType(MaskedTextBox), New FrameworkPropertyMetadata(AddressOf maskchanged))
        Dim metadata As New FrameworkPropertyMetadata()
        metadata.CoerceValueCallback = AddressOf coercetext
        TextProperty.OverrideMetadata(GetType(MaskedTextBox), metadata)
    End Sub

    Private Shared Function coercetext(ByVal d As DependencyObject, ByVal value As Object)
        Dim textbox As MaskedTextBox = CType(d, MaskedTextBox)
        Dim maskprovider As New MaskedTextProvider(textbox.Mask)
        maskprovider.Set(CStr(value))
        Return maskprovider.ToDisplayString()
    End Function

    Public Property Mask() As String
        Get
            Return CStr(GetValue(maskproperty))
        End Get

        Set(ByVal value As String)
            SetValue(maskproperty, value)
        End Set
    End Property

    Private Function getmaskprovider() As MaskedTextProvider
        Dim maskprovider As New MaskedTextProvider(Mask)
        maskprovider.Set(Text)
        Return maskprovider
    End Function

    Private Sub refreshtext(ByVal maskprovider As MaskedTextProvider, ByVal pos As Integer)
        Me.Text = maskprovider.ToDisplayString()
        Me.SelectionStart = pos
    End Sub

    Public ReadOnly Property maskcompleted() As Boolean
        Get
            Dim maskprovider As MaskedTextProvider = getmaskprovider()
            Return maskprovider.MaskCompleted
        End Get
    End Property

    Private Shared Sub maskchanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
        Dim textbox As MaskedTextBox = CType(d, MaskedTextBox)
        d.CoerceValue(TextProperty)
        Dim maskprovider As MaskedTextProvider = textbox.getmaskprovider()
        textbox.refreshtext(maskprovider, 0)
    End Sub

    Private Function skiptoeditablecharacter(ByVal startpos As Integer) As Integer
        Dim maskprovider As MaskedTextProvider = getmaskprovider()
        Dim newpos As Integer = maskprovider.FindEditPositionFrom(startpos, True)
        If newpos = -1 Then
            Return startpos
        Else
            Return newpos
        End If
    End Function

    Protected Overrides Sub OnPreviewTextInput(ByVal e As System.Windows.Input.TextCompositionEventArgs)
        Dim maskprovider As MaskedTextProvider = getmaskprovider()
        Dim pos As Integer = Me.SelectionStart

        'Adding a character
        If pos < Me.Text.Length Then
            pos = skiptoeditablecharacter(pos)
            'Overwrite mode is on.
            If Keyboard.IsKeyToggled(Key.Insert) Then
                If maskprovider.Replace(e.Text, pos) Then
                    pos += 1
                End If
                ' insert mode is on
            Else
                If maskprovider.InsertAt(e.Text, pos) Then
                    pos += 1
                End If
            End If

            'find the new cursor position
            pos = skiptoeditablecharacter(pos)
        End If
        refreshtext(maskprovider, pos)
        e.Handled = True
        MyBase.OnPreviewTextInput(e)
    End Sub

    Protected Overrides Sub OnPreviewKeyDown(ByVal e As System.Windows.Input.KeyEventArgs)
        MyBase.OnKeyDown(e)

        Dim maskprovider As MaskedTextProvider = getmaskprovider()
        Dim pos As Integer = Me.SelectionStart

        'deleteing a character (delete key)
        ' this does nothing if you try to delete a format character
        If e.Key = Key.Delete AndAlso pos < (Me.Text.Length) Then
            If maskprovider.RemoveAt(pos) Then
                refreshtext(maskprovider, pos)
            End If
            e.Handled = True

            'deleting a character (backspace)
            ' this steps over a format character, but doesn't delete the next character
        ElseIf e.Key = Key.Back Then
            If pos > 0 Then
                pos -= 1
                If maskprovider.RemoveAt(pos) Then
                    refreshtext(maskprovider, pos)
                End If
            End If
            e.Handled = True
        End If
    End Sub

    Public Sub New()
        MyBase.New()
        Dim commandbinding1 As New CommandBinding(ApplicationCommands.Paste, Nothing, AddressOf suppresscommand)
        Me.CommandBindings.Add(commandbinding1)

        Dim commandbinding2 As New CommandBinding(ApplicationCommands.Cut, Nothing, AddressOf suppresscommand)
        Me.CommandBindings.Add(commandbinding2)
    End Sub

    Private Sub suppresscommand(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
        e.CanExecute = False
        e.Handled = True
    End Sub
```

 - Build your project

you should now have MaskedTextBox in your toolbox - drag and drop it on to your window.

 - under the properties of the control, open up the 'Other' subcategory (if it isn't already) and find the 'Mask' property

 - enter your mask - in my case it was (999) 999-9999

 - for other masks, use the following characters: 

0 = required digit
9 = optional digit or space, if left blank, a space is inserted automatically
# = optional digit, space, or plus/minus symbol.  if left blank, a space is inserted automatically
L = required ascii letter (a-z or A-Z)
? = optional ascii letter
& = required unicode character.  allows anything that isn't a control key, including punctuation and symbols
C = optional unicode character
A = required alphanumeric character (allows letter or number but not punctuation or symbols)
a = optional alphanumeric character
. = decimal placeholder
, = thousands placeholder
: = time separator
/ = date separator
$ = currency symbol
< = all the characters that follow will be converted automatically to lowercase as the user types them. (there is no way to switch back to mixed-case entry mode once you use this character)
> = all the characters that follow will be converted automatically to uppercase as the user types them
\ = escapes a masked character, turning it in to a literal.  thus, if you use \&, it is interpreted as the literal character &, which will be inserted in to the text box
All other characters = All other characters are treated as literals and are shown in the text box.

 - as an example, an ip address mask would be 990.990.990.990
 - a north american phone number would be (999) 999-9999
 - etc etc etc



Hopefully this will save others the vast amount of time i had to spend on a trivial item like this, however, i'm glad its resolved because i'll be using this class a million times over i'm sure, or at least until wpf 5 (or silverlight 5) contains a native masked textbox..... :-)

thanks to TG and bflosabre91 for their input, it was invaluable in moving forward on this one.

Enjoy!!!

----------


## trevorjeaton

correction to my mask example above - for a phone number the digits are required digits so it would actually be (000) 000-0000

----------


## kevp75

I know this is a 2yr old thread, but I was wondering how I can set the content of a masked textbox, in the code-behind?

----------

