20

I would like to create a TextBox that only accepts numeric values, in a specific range. What is the best way to implement such TextBox?

I thought about deriving TextBox and to override the validation and coercion of the TextProperty. However, I am not sure how to do this, and I understand that deriving WPF control is generally not recommended.


Edit:
What I needed was a very basic textbox that filters out all key presses which are not digits. The easiest way to achieve it is to handle the TextBox.PreviewTextInput event:

private void textBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
    int result;
    if (!validateStringAsNumber(e.Text,out result,false))
    {
        e.Handled = true;
    }
}

(validateStringAsNumber is my function that primarily use Int.TryParse)

Some of the suggested solutions are probably better, but for the simple functionality I needed this solution is the easiest and quickest to implement while sufficient for my needs.

2
  • 1
    Check my question on this topic here: stackoverflow.com/questions/5511/numeric-data-entry-in-wpf Commented Aug 4, 2009 at 8:03
  • Or maybe something like this : private void txtNumeric_PreviewTextInput(object sender, TextCompositionEventArgs e) { if (e.Text.Any(c=>!char.IsDigit(c))) e.Handled = true; } Commented Sep 11, 2013 at 23:10

5 Answers 5

8

Most implementations I have seen so far are using the PreviewTextInput event to implement the correct mask behavior. This one inherits from TextBox and this one uses attached properties. Both use .Net's MaskedTextProvider to provide the correct mask behaviour, but if you just want a simple 'numbers only' textbox you don't need this class.

Sign up to request clarification or add additional context in comments.

Comments

6
private void txt_TextChanged(object sender, TextChangedEventArgs e)
{
    TextBox textBox = sender as TextBox;
    int iValue = -1;

    if (Int32.TryParse(textBox.Text, out iValue) == false)
    {
         TextChange textChange = e.Changes.ElementAt<TextChange>(0);
         int iAddedLength = textChange.AddedLength;
         int iOffset = textChange.Offset;

         textBox.Text = textBox.Text.Remove(iOffset, iAddedLength);
    }
}

2 Comments

When I use the code above I retrieve error message: "Error 1 'System.Collections.Generic.ICollection<System.Windows.Controls.TextChange>' does not contain a definition for 'ElementAt' and no extension method 'ElementAt' accepting a first argument of type 'System.Collections.Generic.ICollection<System.Windows.Controls.TextChange>' could be found (are you missing a using directive or an assembly reference?) NewProduct.xaml.cs 104 51 MediaStore" What reference is missing?
@Legato probably different .net framework
4

In my humble opinion, the best way to fulfill this requirement is using only OnTextChanged event because it can handle the digit from keystroke and also be able to handle Copy+Paste from clipboard too. I hope that my VB code shown below can throw some light on this.

Private Sub NumericBox_TextChanged(sender As Object, e As TextChangedEventArgs) Handles Me.TextChanged
  Dim Buffer As New StringBuilder
  Dim Index As Integer = Me.SelectionStart
  For Each C In Me.Text
    If Char.IsDigit(C) Then
      Buffer.Append(C)
    ElseIf Me.SelectionStart > Buffer.Length Then
      Index -= 1
    End If
  Next
  Me.Text = Buffer.ToString
  Me.SelectionStart = Index
End Sub

Comments

0

The overall best why is to use the override method OnKeyPressed for the textbox like. Here is the code for the override using the static Char Method IsDigit.

if (!Char.IsDigit(e.KeyChar) && !Char.IsControl(e.KeyChar)) e.Handled = true;

That is all you really need to do.

3 Comments

The only problem with this is that it won't allow you to use the decimal point (.)
TextBox only has KeyDown and PreviewKeyDown events which send parameter as KeyEventArgs . and this doesn't contain KeyChar property only Key property which returns one from Keys enum
Also, what if someone wanted to paste a number from the clipboard?
-1

This would be my preferred approach:

private void yearTxt_PreviewKeyDown(object sender, KeyEventArgs e)
{
  switch (e.Key)
  {
    case Key.D0:
    case Key.D1:
    case Key.D2:
    case Key.D3:
    case Key.D4:
    case Key.D5:
    case Key.D6:
    case Key.D7:
    case Key.D8:
    case Key.D9:
    case Key.NumLock:
    case Key.NumPad0:
    case Key.NumPad1:
    case Key.NumPad2:
    case Key.NumPad3:
    case Key.NumPad4:
    case Key.NumPad5:
    case Key.NumPad6:
    case Key.NumPad7:
    case Key.NumPad8:
    case Key.NumPad9:
    case Key.Back:
      break;
    default:
      e.Handled = true;
      break;
  }
}

2 Comments

If you use numeric text boxes often and don't want to repeat the code a lot, you can do this: 1. Make a method by renaming Bryuns code to 'numericTextBox' or something similar. 2. Call the method in 'PreviewKeyDown' events like this: private void textBox1_PreviewKeyDown(object sender, KeyEventArgs e) { numericTextBox(sender, e); }
This does not handle pasted text or users modifying numeric keys and typing chars like $%^. Also the user cannot use the arrow keys to move the caret.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.