title: TextBoxInputMask avoid spaces
body: <p>I am trying to build IP address inputboxmask that looks as follow:
  <img src="https://i.stack.imgur.com/gO6Hl.png" alt="enter image description here">  </p>
  
  <p>What I do not like here is spaces between numbers. My question is, how can I prevent spaces between numbers and force user to write a number instead left spaces.</p>
  
  <p>The behavior class  </p>
  
  <pre><code>public class TextBoxInputMaskBehavior : Behavior&lt;TextBox&gt;
      {
          public MaskedTextProvider Provider { get; private set; }
  
          protected override void OnAttached()
          {
              base.OnAttached();
              AssociatedObject.Loaded += AssociatedObjectLoaded;
              AssociatedObject.PreviewTextInput += AssociatedObjectPreviewTextInput;
              AssociatedObject.PreviewKeyDown += AssociatedObjectPreviewKeyDown;
  
              DataObject.AddPastingHandler(AssociatedObject, Pasting);
          }
  
          protected override void OnDetaching()
          {
              base.OnDetaching();
              AssociatedObject.Loaded -= AssociatedObjectLoaded;
              AssociatedObject.PreviewTextInput -= AssociatedObjectPreviewTextInput;
              AssociatedObject.PreviewKeyDown -= AssociatedObjectPreviewKeyDown;
  
              DataObject.RemovePastingHandler(AssociatedObject, Pasting);
          }
  
          /*
          Mask Character  Accepts  Required?  
          0  Digit (0-9)  Required  
          9  Digit (0-9) or space  Optional  
          #  Digit (0-9) or space  Required  
          L  Letter (a-z, A-Z)  Required  
          ?  Letter (a-z, A-Z)  Optional  
          &amp;  Any character  Required  
          C  Any character  Optional  
          A  Alphanumeric (0-9, a-z, A-Z)  Required  
          a  Alphanumeric (0-9, a-z, A-Z)  Optional  
             Space separator  Required 
          .  Decimal separator  Required  
          ,  Group (thousands) separator  Required  
          :  Time separator  Required  
          /  Date separator  Required  
          $  Currency symbol  Required  
  
          In addition, the following characters have special meaning:
  
          Mask Character  Meaning  
          &lt;  All subsequent characters are converted to lower case  
          &gt;  All subsequent characters are converted to upper case  
          |  Terminates a previous &lt; or &gt;  
          \  Escape: treat the next character in the mask as literal text rather than a mask symbol  
  
          */
  
          private void AssociatedObjectLoaded(object sender, RoutedEventArgs e)
          {
              Provider = new MaskedTextProvider(InputMask, CultureInfo.CurrentCulture);
              Provider.Set(AssociatedObject.Text);
              Provider.PromptChar = PromptChar;
              AssociatedObject.Text = Provider.ToDisplayString();
  
              //seems the only way that the text is formatted correct, when source is updated
              var textProp = DependencyPropertyDescriptor.FromProperty(TextBox.TextProperty, typeof (TextBox));
              if (textProp != null)
              {
                  textProp.AddValueChanged(AssociatedObject, (s, args) =&gt; UpdateText());
              }
          }
  
          private void AssociatedObjectPreviewTextInput(object sender, TextCompositionEventArgs e)
          {
              TreatSelectedText();
  
              var position = GetNextCharacterPosition(AssociatedObject.SelectionStart);
  
              if (Keyboard.IsKeyToggled(Key.Insert))
              {
                  if (Provider.Replace(e.Text, position))
                      position++;
              }
              else
              {
                  if (Provider.InsertAt(e.Text, position))
                      position++;
              }
  
              position = GetNextCharacterPosition(position);
  
              RefreshText(position);
  
              e.Handled = true;
          }
  
          private void AssociatedObjectPreviewKeyDown(object sender, KeyEventArgs e)
          {
              if (e.Key == Key.Space) //handle the space
              {
                  TreatSelectedText();
  
                  var position = GetNextCharacterPosition(AssociatedObject.SelectionStart);
  
                  if (Provider.InsertAt(" ", position))
                      RefreshText(position);
  
                  e.Handled = true;
              }
  
              if (e.Key == Key.Back) //handle the back space
              {
                  if (TreatSelectedText())
                  {
                      RefreshText(AssociatedObject.SelectionStart);
                  }
                  else
                  {
                      if (AssociatedObject.SelectionStart != 0)
                      {
                          if (Provider.RemoveAt(AssociatedObject.SelectionStart - 1))
                              RefreshText(AssociatedObject.SelectionStart - 1);
                      }
                  }
  
                  e.Handled = true;
              }
  
              if (e.Key == Key.Delete) //handle the delete key
              {
                  //treat selected text
                  if (TreatSelectedText())
                  {
                      RefreshText(AssociatedObject.SelectionStart);
                  }
                  else
                  {
                      if (Provider.RemoveAt(AssociatedObject.SelectionStart))
                          RefreshText(AssociatedObject.SelectionStart);
                  }
  
                  e.Handled = true;
              }
          }
  
          /// &lt;summary&gt;
          ///     Pasting prüft ob korrekte Daten reingepastet werden
          /// &lt;/summary&gt;
          /// &lt;param name="sender"&gt;&lt;/param&gt;
          /// &lt;param name="e"&gt;&lt;/param&gt;
          private void Pasting(object sender, DataObjectPastingEventArgs e)
          {
              if (e.DataObject.GetDataPresent(typeof (string)))
              {
                  var pastedText = (string) e.DataObject.GetData(typeof (string));
  
                  TreatSelectedText();
  
                  var position = GetNextCharacterPosition(AssociatedObject.SelectionStart);
  
                  if (Provider.InsertAt(pastedText, position))
                  {
                      RefreshText(position);
                  }
              }
  
              e.CancelCommand();
          }
  
          private void UpdateText()
          {
              //check Provider.Text + TextBox.Text
              if (Provider.ToDisplayString().Equals(AssociatedObject.Text))
                  return;
  
              //use provider to format
              var success = Provider.Set(AssociatedObject.Text);
  
              //ui and mvvm/codebehind should be in sync
              SetText(success ? Provider.ToDisplayString() : AssociatedObject.Text);
          }
  
          /// &lt;summary&gt;
          ///     Falls eine Textauswahl vorliegt wird diese entsprechend behandelt.
          /// &lt;/summary&gt;
          /// &lt;returns&gt;true Textauswahl behandelt wurde, ansonsten falls &lt;/returns&gt;
          private bool TreatSelectedText()
          {
              if (AssociatedObject.SelectionLength &gt; 0)
              {
                  return Provider.RemoveAt(AssociatedObject.SelectionStart,
                      AssociatedObject.SelectionStart + AssociatedObject.SelectionLength - 1);
              }
              return false;
          }
  
          private void RefreshText(int position)
          {
              SetText(Provider.ToDisplayString());
              AssociatedObject.SelectionStart = position;
          }
  
          private void SetText(string text)
          {
              AssociatedObject.Text = string.IsNullOrWhiteSpace(text) ? string.Empty : text;
          }
  
          private int GetNextCharacterPosition(int startPosition)
          {
              var position = Provider.FindEditPositionFrom(startPosition, true);
  
              if (position == -1)
                  return startPosition;
              return position;
          }
  
          #region DependencyProperties
  
          public static readonly DependencyProperty InputMaskProperty =
              DependencyProperty.Register("InputMask", typeof (string), typeof (TextBoxInputMaskBehavior), null);
  
          public string InputMask
          {
              get { return (string) GetValue(InputMaskProperty); }
              set { SetValue(InputMaskProperty, value); }
          }
  
          public static readonly DependencyProperty PromptCharProperty =
              DependencyProperty.Register("PromptChar", typeof (char), typeof (TextBoxInputMaskBehavior),
                  new PropertyMetadata('_'));
  
          public char PromptChar
          {
              get { return (char) GetValue(PromptCharProperty); }
              set { SetValue(PromptCharProperty, value); }
          }
  
          #endregion
      }
  </code></pre>
  
  <p>and xaml</p>
  
  <pre><code>&lt;TextBox VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Grid.Row="6" Grid.Column="1" FontSize="22" &gt;
              &lt;i:Interaction.Behaviors&gt;
                  &lt;behaviors:TextBoxInputMaskBehavior InputMask="{StaticResource InputMaskIp}" PromptChar="_" /&gt;
              &lt;/i:Interaction.Behaviors&gt;
          &lt;/TextBox&gt;
  </code></pre>