I have a windows forms app with a textbox control that I want to only accept integer values. In the past I've done this kind of validation by overloading the KeyPress event and just removing characters which didn't fit the specification. I've looked at the MaskedTextBox control but I'd like a more general solution that could work with perhaps a regular expression, or depend on the values of other controls.
Ideally this would behave such that pressing a non numeric character would either produce no result or immediately provide the user with feedback about the invalid character.
Two options:
Use a NumericUpDown instead. NumericUpDown does the filtering for you, which is nice. Of course it also gives your users the ability to hit the up and down arrows on the keyboard to increment and decrement the current value.
Handle the appropriate keyboard events to prevent anything but numeric input. I've had success with this two event handlers on a standard TextBox:
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) &&
(e.KeyChar != '.'))
{
e.Handled = true;
}
// only allow one decimal point
if ((e.KeyChar == '.') && ((sender as TextBox).Text.IndexOf('.') > -1))
{
e.Handled = true;
}
}
You can remove the check for '.' (and the subsequent check for more than one '.') if your TextBox shouldn't allow decimal places. You could also add a check for '-' if your TextBox should allow negative values.
If you want to limit the user for number of digit, use: textBox1.MaxLength = 2; // this will allow the user to enter only 2 digits
And just because it's always more fun to do stuff in one line...
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar);
}
NOTE: This DOES NOT prevent a user from Copy / Paste into this textbox. It's not a fail safe way to sanitize your data.
I am assuming from context and the tags you used that you are writing a .NET C# app. In this case, you can subscribe to the text changed event, and validate each key stroke.
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (System.Text.RegularExpressions.Regex.IsMatch(textBox1.Text, "[^0-9]"))
{
MessageBox.Show("Please enter only numbers.");
textBox1.Text = textBox1.Text.Remove(textBox1.Text.Length - 1);
}
}
Here is a simple standalone Winforms custom control, derived from the standard TextBox, that allows only System.Int32 input (it could be easily adapted for other types such as System.Int64, etc.). It supports copy/paste operations and negative numbers:
public class Int32TextBox : TextBox
{
protected override void OnKeyPress(KeyPressEventArgs e)
{
base.OnKeyPress(e);
NumberFormatInfo fi = CultureInfo.CurrentCulture.NumberFormat;
string c = e.KeyChar.ToString();
if (char.IsDigit(c, 0))
return;
if ((SelectionStart == 0) && (c.Equals(fi.NegativeSign)))
return;
// copy/paste
if ((((int)e.KeyChar == 22) || ((int)e.KeyChar == 3))
&& ((ModifierKeys & Keys.Control) == Keys.Control))
return;
if (e.KeyChar == '\b')
return;
e.Handled = true;
}
protected override void WndProc(ref System.Windows.Forms.Message m)
{
const int WM_PASTE = 0x0302;
if (m.Msg == WM_PASTE)
{
string text = Clipboard.GetText();
if (string.IsNullOrEmpty(text))
return;
if ((text.IndexOf('+') >= 0) && (SelectionStart != 0))
return;
int i;
if (!int.TryParse(text, out i)) // change this for other integer types
return;
if ((i < 0) && (SelectionStart != 0))
return;
}
base.WndProc(ref m);
}
Update 2017: My first answer has some issues:
you can type something that's longer than an integer of a given type (for example 2147483648 is greater than Int32.MaxValue);
more generally, there's no real validation of the result of what has been typed;
it only handles int32, you'll have to write specific TextBox derivated control for each type (Int64, etc.)
So I came up with another version that's more generic, that still supports copy/paste, + and - sign, etc.
public class ValidatingTextBox : TextBox
{
private string _validText;
private int _selectionStart;
private int _selectionEnd;
private bool _dontProcessMessages;
public event EventHandler<TextValidatingEventArgs> TextValidating;
protected virtual void OnTextValidating(object sender, TextValidatingEventArgs e) => TextValidating?.Invoke(sender, e);
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (_dontProcessMessages)
return;
const int WM_KEYDOWN = 0x100;
const int WM_ENTERIDLE = 0x121;
const int VK_DELETE = 0x2e;
bool delete = m.Msg == WM_KEYDOWN && (int)m.WParam == VK_DELETE;
if ((m.Msg == WM_KEYDOWN && !delete) || m.Msg == WM_ENTERIDLE)
{
DontProcessMessage(() =>
{
_validText = Text;
_selectionStart = SelectionStart;
_selectionEnd = SelectionLength;
});
}
const int WM_CHAR = 0x102;
const int WM_PASTE = 0x302;
if (m.Msg == WM_CHAR || m.Msg == WM_PASTE || delete)
{
string newText = null;
DontProcessMessage(() =>
{
newText = Text;
});
var e = new TextValidatingEventArgs(newText);
OnTextValidating(this, e);
if (e.Cancel)
{
DontProcessMessage(() =>
{
Text = _validText;
SelectionStart = _selectionStart;
SelectionLength = _selectionEnd;
});
}
}
}
private void DontProcessMessage(Action action)
{
_dontProcessMessages = true;
try
{
action();
}
finally
{
_dontProcessMessages = false;
}
}
}
public class TextValidatingEventArgs : CancelEventArgs
{
public TextValidatingEventArgs(string newText) => NewText = newText;
public string NewText { get; }
}
For Int32, you can either derive from it, like this:
public class Int32TextBox : ValidatingTextBox
{
protected override void OnTextValidating(object sender, TextValidatingEventArgs e)
{
e.Cancel = !int.TryParse(e.NewText, out int i);
}
}
or w/o derivation, use the new TextValidating event like this:
var vtb = new ValidatingTextBox();
...
vtb.TextValidating += (sender, e) => e.Cancel = !int.TryParse(e.NewText, out int i);
but what's nice is it works with any string, and any validation routine.
This is exactly what the Validated/Validating events were designed for.
Here's the MSDN article on the topic: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.validating.aspx
The TL;DR version: check the .Text property in the Validating event and set e.Cancel=True when the data is invalid.
When you set e.Cancel=True, the user can't leave the field, but you will need to give them some kind of feedback that something's wrong. I change the box's background color to light red to indicate a problem. Make sure to set it back to SystemColors.Window when Validating is called with a good value.
Try a MaskedTextBox. It takes a simple mask format so you can limit the input to numbers or dates or whatever.
You can use the TextChanged event
private void textBox_BiggerThan_TextChanged(object sender, EventArgs e)
{
long a;
if (! long.TryParse(textBox_BiggerThan.Text, out a))
{
// If not int clear textbox text or Undo() last operation
textBox_LessThan.Clear();
}
}
This might be useful. It allows "real" numeric values, including proper decimal points and preceding plus or minus signs. Call it from within the related KeyPress event.
private bool IsOKForDecimalTextBox(char theCharacter, TextBox theTextBox)
{
// Only allow control characters, digits, plus and minus signs.
// Only allow ONE plus sign.
// Only allow ONE minus sign.
// Only allow the plus or minus sign as the FIRST character.
// Only allow ONE decimal point.
// Do NOT allow decimal point or digits BEFORE any plus or minus sign.
if (
!char.IsControl(theCharacter)
&& !char.IsDigit(theCharacter)
&& (theCharacter != '.')
&& (theCharacter != '-')
&& (theCharacter != '+')
)
{
// Then it is NOT a character we want allowed in the text box.
return false;
}
// Only allow one decimal point.
if (theCharacter == '.'
&& theTextBox.Text.IndexOf('.') > -1)
{
// Then there is already a decimal point in the text box.
return false;
}
// Only allow one minus sign.
if (theCharacter == '-'
&& theTextBox.Text.IndexOf('-') > -1)
{
// Then there is already a minus sign in the text box.
return false;
}
// Only allow one plus sign.
if (theCharacter == '+'
&& theTextBox.Text.IndexOf('+') > -1)
{
// Then there is already a plus sign in the text box.
return false;
}
// Only allow one plus sign OR minus sign, but not both.
if (
(
(theCharacter == '-')
|| (theCharacter == '+')
)
&&
(
(theTextBox.Text.IndexOf('-') > -1)
||
(theTextBox.Text.IndexOf('+') > -1)
)
)
{
// Then the user is trying to enter a plus or minus sign and
// there is ALREADY a plus or minus sign in the text box.
return false;
}
// Only allow a minus or plus sign at the first character position.
if (
(
(theCharacter == '-')
|| (theCharacter == '+')
)
&& theTextBox.SelectionStart != 0
)
{
// Then the user is trying to enter a minus or plus sign at some position
// OTHER than the first character position in the text box.
return false;
}
// Only allow digits and decimal point AFTER any existing plus or minus sign
if (
(
// Is digit or decimal point
char.IsDigit(theCharacter)
||
(theCharacter == '.')
)
&&
(
// A plus or minus sign EXISTS
(theTextBox.Text.IndexOf('-') > -1)
||
(theTextBox.Text.IndexOf('+') > -1)
)
&&
// Attempting to put the character at the beginning of the field.
theTextBox.SelectionStart == 0
)
{
// Then the user is trying to enter a digit or decimal point in front of a minus or plus sign.
return false;
}
// Otherwise the character is perfectly fine for a decimal value and the character
// may indeed be placed at the current insertion position.
return true;
}
Just use a NumericUpDown control and set those ugly up down buttons visibility to false.
numericUpDown1.Controls[0].Visible = false;
NumericUpDown is actually a collection of controls containing a 'spin box' (up down buttons), a text box and some code to validate and wange-jangle it all together.
Marking:
YourNumericUpDown.Controls[0].visible = false
will hide the buttons while keeping the underlying code active.
While not being an obvious solution it is simple and effective. .Controls[1] would hide the text box portion if you wanted to do that instead.
I've been working on a collection of components to complete missing stuff in WinForms, here it is: Advanced Forms
In particular this is the class for a Regex TextBox
/// <summary>Represents a Windows text box control that only allows input that matches a regular expression.</summary>
public class RegexTextBox : TextBox
{
[NonSerialized]
string lastText;
/// <summary>A regular expression governing the input allowed in this text field.</summary>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public virtual Regex Regex { get; set; }
/// <summary>A regular expression governing the input allowed in this text field.</summary>
[DefaultValue(null)]
[Category("Behavior")]
[Description("Sets the regular expression governing the input allowed for this control.")]
public virtual string RegexString {
get {
return Regex == null ? string.Empty : Regex.ToString();
}
set {
if (string.IsNullOrEmpty(value))
Regex = null;
else
Regex = new Regex(value);
}
}
protected override void OnTextChanged(EventArgs e) {
if (Regex != null && !Regex.IsMatch(Text)) {
int pos = SelectionStart - Text.Length + (lastText ?? string.Empty).Length;
Text = lastText;
SelectionStart = Math.Max(0, pos);
}
lastText = Text;
base.OnTextChanged(e);
}
}
Simply adding something like myNumbericTextBox.RegexString = "^(\\d+|)$"; should suffice.
simply use this code in textbox :
private void textBox1_TextChanged(object sender, EventArgs e)
{
double parsedValue;
if (!double.TryParse(textBox1.Text, out parsedValue))
{
textBox1.Text = "";
}
}
Both integers and floats need to be accepted, including the negative numbers.
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
// Text
string text = ((Control) sender).Text;
// Is Negative Number?
if (e.KeyChar == '-' && text.Length == 0)
{
e.Handled = false;
return;
}
// Is Float Number?
if (e.KeyChar == '.' && text.Length > 0 && !text.Contains("."))
{
e.Handled = false;
return;
}
// Is Digit?
e.Handled = (!char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar));
}
I have made something for this on CodePlex.
It works by intercepting the TextChanged event. If the result is a good number it will be stored. If it is something wrong, the last good value will be restored. The source is a bit too large to publish here, but here is a link to the class that handles the core of this logic.
In our webpage with the definition of textbox we can add an onkeypress event for accepting only numbers. It will not show any message but it will prevent you from wrong input. It worked for me, user could not enter anything except number.
<asp:TextBox runat="server" ID="txtFrom"
onkeypress="if(isNaN(String.fromCharCode(event.keyCode))) return false;">
This is my aproach:
using linq (easy to modify filter)
copy/paste proof code
keeps caret position when you press a forbidden character
accepts left zeroes
and any size numbers
private void numeroCuenta_TextChanged(object sender, EventArgs e)
{
string org = numeroCuenta.Text;
string formated = string.Concat(org.Where(c => (c >= '0' && c <= '9')));
if (formated != org)
{
int s = numeroCuenta.SelectionStart;
if (s > 0 && formated.Length > s && org[s - 1] != formated[s - 1]) s--;
numeroCuenta.Text = formated;
numeroCuenta.SelectionStart = s;
}
}
This is a nice and short way to do it with .NET 5/Core
private void textBox1_KeyDown(object sender, KeyEventArgs e) {
if (e.KeyData != Keys.Back)
e.SuppressKeyPress = !int.TryParse(Convert.ToString((char) e.KeyData), out int _);
}
Edit: Added support for backspace key
you could use TextChanged/ Keypress event, use a regex to filter on numbers and take some action.
I would handle it in the KeyDown event.
void TextBox_KeyDown(object sender, KeyEventArgs e)
{
char c = Convert.ToChar(e.PlatformKeyCode);
if (!char.IsDigit(c))
{
e.Handled = true;
}
}
private void txt3_KeyPress(object sender, KeyPressEventArgs e)
{
for (int h = 58; h <= 127; h++)
{
if (e.KeyChar == h) //58 to 127 is alphabets tat will be blocked
{
e.Handled = true;
}
}
for(int k=32;k<=47;k++)
{
if (e.KeyChar == k) //32 to 47 are special characters tat will
{ be blocked
e.Handled = true;
}
}
}
try this is very simple
Take a look at Input handling in WinForm
I have posted my solution which uses the ProcessCmdKey and OnKeyPress events on the textbox. The comments show you how to use a Regex to verify the keypress and block/allow appropriately.
Hi you can do something like this in the textchanged event of the textbox.
here is a demo
private void textBox1_TextChanged(object sender, EventArgs e)
{
string actualdata = string.Empty;
char[] entereddata = textBox1.Text.ToCharArray();
foreach (char aChar in entereddata.AsEnumerable())
{
if (Char.IsDigit(aChar))
{
actualdata = actualdata + aChar;
// MessageBox.Show(aChar.ToString());
}
else
{
MessageBox.Show(aChar + " is not numeric");
actualdata.Replace(aChar, ' ');
actualdata.Trim();
}
}
textBox1.Text = actualdata;
}
It seems like many of the current answers to this question are manually parsing the input text. If you're looking for a specific built-in numeric type (e.g. int or double), why not just delegate the work to that type's TryParse method? For example:
public class IntTextBox : TextBox
{
string PreviousText = "";
int BackingResult;
public IntTextBox()
{
TextChanged += IntTextBox_TextChanged;
}
public bool HasResult { get; private set; }
public int Result
{
get
{
return HasResult ? BackingResult : default(int);
}
}
void IntTextBox_TextChanged(object sender, EventArgs e)
{
HasResult = int.TryParse(Text, out BackingResult);
if (HasResult || string.IsNullOrEmpty(Text))
{
// Commit
PreviousText = Text;
}
else
{
// Revert
var changeOffset = Text.Length - PreviousText.Length;
var previousSelectionStart =
Math.Max(0, SelectionStart - changeOffset);
Text = PreviousText;
SelectionStart = previousSelectionStart;
}
}
}
If you want something more generic but still compatible with Visual Studio's Designer:
public class ParsableTextBox : TextBox
{
TryParser BackingTryParse;
string PreviousText = "";
object BackingResult;
public ParsableTextBox()
: this(null)
{
}
public ParsableTextBox(TryParser tryParse)
{
TryParse = tryParse;
TextChanged += ParsableTextBox_TextChanged;
}
public delegate bool TryParser(string text, out object result);
public TryParser TryParse
{
set
{
Enabled = !(ReadOnly = value == null);
BackingTryParse = value;
}
}
public bool HasResult { get; private set; }
public object Result
{
get
{
return GetResult<object>();
}
}
public T GetResult<T>()
{
return HasResult ? (T)BackingResult : default(T);
}
void ParsableTextBox_TextChanged(object sender, EventArgs e)
{
if (BackingTryParse != null)
{
HasResult = BackingTryParse(Text, out BackingResult);
}
if (HasResult || string.IsNullOrEmpty(Text))
{
// Commit
PreviousText = Text;
}
else
{
// Revert
var changeOffset = Text.Length - PreviousText.Length;
var previousSelectionStart =
Math.Max(0, SelectionStart - changeOffset);
Text = PreviousText;
SelectionStart = previousSelectionStart;
}
}
}
And finally, if you want something fully generic and don't care about Designer support:
public class ParsableTextBox<T> : TextBox
{
TryParser BackingTryParse;
string PreviousText;
T BackingResult;
public ParsableTextBox()
: this(null)
{
}
public ParsableTextBox(TryParser tryParse)
{
TryParse = tryParse;
TextChanged += ParsableTextBox_TextChanged;
}
public delegate bool TryParser(string text, out T result);
public TryParser TryParse
{
set
{
Enabled = !(ReadOnly = value == null);
BackingTryParse = value;
}
}
public bool HasResult { get; private set; }
public T Result
{
get
{
return HasResult ? BackingResult : default(T);
}
}
void ParsableTextBox_TextChanged(object sender, EventArgs e)
{
if (BackingTryParse != null)
{
HasResult = BackingTryParse(Text, out BackingResult);
}
if (HasResult || string.IsNullOrEmpty(Text))
{
// Commit
PreviousText = Text;
}
else
{
// Revert
var changeOffset = Text.Length - PreviousText.Length;
var previousSelectionStart =
Math.Max(0, SelectionStart - changeOffset);
Text = PreviousText;
SelectionStart = previousSelectionStart;
}
}
}
Using the approach described in Fabio Iotti's answer I have created a more generic solution:
public abstract class ValidatedTextBox : TextBox {
private string m_lastText = string.Empty;
protected abstract bool IsValid(string text);
protected sealed override void OnTextChanged(EventArgs e) {
if (!IsValid(Text)) {
var pos = SelectionStart - Text.Length + m_lastText.Length;
Text = m_lastText;
SelectionStart = Math.Max(0, pos);
}
m_lastText = Text;
base.OnTextChanged(e);
}
}
"ValidatedTextBox", which contains all nontrivial validation behavior.
All that's left to do is inherit from this class and override "IsValid" method with whatever validation logic is required.
For example, using this class, it is possible to create "RegexedTextBox" which will accept only strings which match specific regular expression:
public abstract class RegexedTextBox : ValidatedTextBox {
private readonly Regex m_regex;
protected RegexedTextBox(string regExpString) {
m_regex = new Regex(regExpString);
}
protected override bool IsValid(string text) {
return m_regex.IsMatch(Text);
}
}
After that, inheriting from the "RegexedTextBox" class, we can easily create "PositiveNumberTextBox" and "PositiveFloatingPointNumberTextBox" controls:
public sealed class PositiveNumberTextBox : RegexedTextBox {
public PositiveNumberTextBox() : base(#"^\d*$") { }
}
public sealed class PositiveFloatingPointNumberTextBox : RegexedTextBox {
public PositiveFloatingPointNumberTextBox()
: base(#"^(\d+\" + CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator + #")?\d*$") { }
}
Sorry to wake the dead, but I thought someone might find this useful for future reference.
Here is how I handle it. It handles floating point numbers, but can easily be modified for integers.
Basically you can only press 0 - 9 and .
You can only have one 0 before the .
All other characters are ignored and the cursor position maintained.
private bool _myTextBoxChanging = false;
private void myTextBox_TextChanged(object sender, EventArgs e)
{
validateText(myTextBox);
}
private void validateText(TextBox box)
{
// stop multiple changes;
if (_myTextBoxChanging)
return;
_myTextBoxChanging = true;
string text = box.Text;
if (text == "")
return;
string validText = "";
bool hasPeriod = false;
int pos = box.SelectionStart;
for (int i = 0; i < text.Length; i++ )
{
bool badChar = false;
char s = text[i];
if (s == '.')
{
if (hasPeriod)
badChar = true;
else
hasPeriod = true;
}
else if (s < '0' || s > '9')
badChar = true;
if (!badChar)
validText += s;
else
{
if (i <= pos)
pos--;
}
}
// trim starting 00s
while (validText.Length >= 2 && validText[0] == '0')
{
if (validText[1] != '.')
{
validText = validText.Substring(1);
if (pos < 2)
pos--;
}
else
break;
}
if (pos > validText.Length)
pos = validText.Length;
box.Text = validText;
box.SelectionStart = pos;
_myTextBoxChanging = false;
}
Here is a quickly modified int version:
private void validateText(TextBox box)
{
// stop multiple changes;
if (_myTextBoxChanging)
return;
_myTextBoxChanging = true;
string text = box.Text;
if (text == "")
return;
string validText = "";
int pos = box.SelectionStart;
for (int i = 0; i < text.Length; i++ )
{
char s = text[i];
if (s < '0' || s > '9')
{
if (i <= pos)
pos--;
}
else
validText += s;
}
// trim starting 00s
while (validText.Length >= 2 && validText.StartsWith("00"))
{
validText = validText.Substring(1);
if (pos < 2)
pos--;
}
if (pos > validText.Length)
pos = validText.Length;
box.Text = validText;
box.SelectionStart = pos;
_myTextBoxChanging = false;
}
This one works with copy and paste, drag and drop, key down, prevents overflow and is pretty simple
public partial class IntegerBox : TextBox
{
public IntegerBox()
{
InitializeComponent();
this.Text = 0.ToString();
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
private String originalValue = 0.ToString();
private void Integerbox_KeyPress(object sender, KeyPressEventArgs e)
{
originalValue = this.Text;
}
private void Integerbox_TextChanged(object sender, EventArgs e)
{
try
{
if(String.IsNullOrWhiteSpace(this.Text))
{
this.Text = 0.ToString();
}
this.Text = Convert.ToInt64(this.Text.Trim()).ToString();
}
catch (System.OverflowException)
{
MessageBox.Show("Value entered is to large max value: " + Int64.MaxValue.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
this.Text = originalValue;
}
catch (System.FormatException)
{
this.Text = originalValue;
}
catch (System.Exception ex)
{
this.Text = originalValue;
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK , MessageBoxIcon.Error);
}
}
}
Do not forget that a user can paste an invalid text in a TextBox.
If you want to restrict that, follow the below code:
private void ultraTextEditor1_TextChanged(object sender, EventArgs e)
{
string append="";
foreach (char c in ultraTextEditor1.Text)
{
if ((!Char.IsNumber(c)) && (c != Convert.ToChar(Keys.Back)))
{
}
else
{
append += c;
}
}
ultraTextEditor1.Text = append;
}
I was also looking for the best way to check only numbers in textbox and problem with keypress was it does not support copy paste by right click or clipboard so came up with this code which validates the when cursor leaves the text field and also it checks for empty field. (adapted version of newguy)
private void txtFirstValue_MouseLeave(object sender, EventArgs e)
{
int num;
bool isNum = int.TryParse(txtFirstValue.Text.Trim(), out num);
if (!isNum && txtFirstValue.Text != String.Empty)
{
MessageBox.Show("The First Value You Entered Is Not a Number, Please Try Again", "Invalid Value Detected", MessageBoxButtons.OK, MessageBoxIcon.Error);
txtFirstValue.Clear();
}
}
Here are more than 30 answers and a lot of answers are helpful. But I want to share a generalized form for the System.Windows.Forms.TextBox and System.Windows.Controls.TextBox.
There is not available KeyPress event in System.Windows.Controls.TextBox.
This answer is for those people who want to implement with the same logic for System.Windows.Forms.TextBox and System.Windows.Controls.TextBox.
This is NumberTextBox code. Use commented line instead of the previous line for System.Windows.Controls.TextBox.
public class NumberTextBox : System.Windows.Forms.TextBox
//public class NumberTextBox : System.Windows.Controls.TextBox
{
private double _maxValue;
private double _minValue;
private bool _flag;
private string _previousValue;
public NumberTextBox()
{
this.TextAlign = HorizontalAlignment.Right;
//TextAlignment = TextAlignment.Right;
KeyDown += TextBox_KeyDown;
TextChanged += TextBox_TextChanged;
_minValue = double.MinValue;
_maxValue = double.MaxValue;
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
_previousValue = this.Text;
_flag = this.SelectedText.Length > 0;
}
private void TextBox_TextChanged(object sender, EventArgs e)
//private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var text = this.Text;
if (text.Length < 1) return;
var cursorPosition = SelectionStart == 0 ? SelectionStart : SelectionStart - 1;
var insertedChar = text[cursorPosition];
if (IsInvalidInput(insertedChar, cursorPosition, text))
{
HandleText(text, cursorPosition);
}
ValidateRange(text, cursorPosition);
}
private bool IsInvalidInput(char insertedChar, int cursorPosition, string text)
{
return !char.IsDigit(insertedChar) && insertedChar != '.' && insertedChar != '-' ||
insertedChar == '-' && cursorPosition != 0 ||
text.Count(x => x == '.') > 1 ||
text.Count(x => x == '-') > 1;
}
private void HandleText(string text, int cursorPosition)
{
this.Text = _flag ? _previousValue : text.Remove(cursorPosition, 1);
this.SelectionStart = cursorPosition;
this.SelectionLength = 0;
}
private void ValidateRange(string text, int cursorPosition)
{
try
{
if (text == "." || _minValue < 0 && text == "-") return;
var doubleValue = Convert.ToDouble(text);
if (doubleValue > _maxValue || doubleValue < _minValue)
{
HandleText(text, cursorPosition);
}
}
catch (Exception)
{
HandleText(text, cursorPosition);
}
}
protected void SetProperties(double minValue = double.MinValue, double maxValue = double.MaxValue)
{
_minValue = minValue;
_maxValue = maxValue;
}
}
PositiveNumberTextBox code:
public class PositiveNumberTextBox : NumberTextBox
{
public PositiveNumberTextBox()
{
SetProperties(0);
}
}
FractionNumberTextBox code:
public class FractionNumberTextBox : NumberTextBox
{
public FractionNumberTextBox()
{
SetProperties(0, 0.999999);
}
}
int Number;
bool isNumber;
isNumber = int32.TryPase(textbox1.text, out Number);
if (!isNumber)
{
(code if not an integer);
}
else
{
(code if an integer);
}
3 solution
1)
//Add to the textbox's KeyPress event
//using Regex for number only textBox
private void txtBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (!System.Text.RegularExpressions.Regex.IsMatch(e.KeyChar.ToString(), "\\d+"))
e.Handled = true;
}
2)
an another solution from msdn
// Boolean flag used to determine when a character other than a number is entered.
private bool nonNumberEntered = false;
// Handle the KeyDown event to determine the type of character entered into the control.
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
// Initialize the flag to false.
nonNumberEntered = false;
// Determine whether the keystroke is a number from the top of the keyboard.
if (e.KeyCode < Keys.D0 || e.KeyCode > Keys.D9)
{
// Determine whether the keystroke is a number from the keypad.
if (e.KeyCode < Keys.NumPad0 || e.KeyCode > Keys.NumPad9)
{
// Determine whether the keystroke is a backspace.
if (e.KeyCode != Keys.Back)
{
// A non-numerical keystroke was pressed.
// Set the flag to true and evaluate in KeyPress event.
nonNumberEntered = true;
}
}
}
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (nonNumberEntered == true)
{
MessageBox.Show("Please enter number only...");
e.Handled = true;
}
}
source http://msdn.microsoft.com/en-us/library/system.windows.forms.control.keypress(v=VS.90).aspx
3) using the MaskedTextBox: http://msdn.microsoft.com/en-us/library/system.windows.forms.maskedtextbox.aspx
I'm looking to accept digits and the decimal point, but no sign.
I've looked at samples using the NumericUpDown control for Windows Forms, and this sample of a NumericUpDown custom control from Microsoft. But so far it seems like NumericUpDown (supported by WPF or not) is not going to provide the functionality that I want. The way my application is designed, nobody in their right mind is going to want to mess with the arrows. They don't make any practical sense, in the context of my application.
So I'm looking for a simple way to make a standard WPF TextBox accept only the characters that I want. Is this possible? Is it practical?
Add a preview text input event. Like so: <TextBox PreviewTextInput="PreviewTextInput" />.
Then inside that set the e.Handled if the text isn't allowed. e.Handled = !IsTextAllowed(e.Text);
I use a simple regex in IsTextAllowed method to see if I should allow what they've typed. In my case I only want to allow numbers, dots and dashes.
private static readonly Regex _regex = new Regex("[^0-9.-]+"); //regex that matches disallowed text
private static bool IsTextAllowed(string text)
{
return !_regex.IsMatch(text);
}
If you want to prevent pasting of incorrect data hook up the DataObject.Pasting event DataObject.Pasting="TextBoxPasting" as shown here (code excerpted):
// Use the DataObject.Pasting Handler
private void TextBoxPasting(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(typeof(String)))
{
String text = (String)e.DataObject.GetData(typeof(String));
if (!IsTextAllowed(text))
{
e.CancelCommand();
}
}
else
{
e.CancelCommand();
}
}
The event handler is previewing text input. Here a regular expression matches the text input only if it is not a number, and then it is not made to entry textbox.
If you want only letters then replace the regular expression as [^a-zA-Z].
XAML
<TextBox Name="NumberTextBox" PreviewTextInput="NumberValidationTextBox"/>
XAML.CS FILE
using System.Text.RegularExpressions;
private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
{
Regex regex = new Regex("[^0-9]+");
e.Handled = regex.IsMatch(e.Text);
}
I used some of what was already here and put my own twist on it using a behavior so I don't have to propagate this code throughout a ton of Views...
public class AllowableCharactersTextBoxBehavior : Behavior<TextBox>
{
public static readonly DependencyProperty RegularExpressionProperty =
DependencyProperty.Register("RegularExpression", typeof(string), typeof(AllowableCharactersTextBoxBehavior),
new FrameworkPropertyMetadata(".*"));
public string RegularExpression
{
get
{
return (string)base.GetValue(RegularExpressionProperty);
}
set
{
base.SetValue(RegularExpressionProperty, value);
}
}
public static readonly DependencyProperty MaxLengthProperty =
DependencyProperty.Register("MaxLength", typeof(int), typeof(AllowableCharactersTextBoxBehavior),
new FrameworkPropertyMetadata(int.MinValue));
public int MaxLength
{
get
{
return (int)base.GetValue(MaxLengthProperty);
}
set
{
base.SetValue(MaxLengthProperty, value);
}
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewTextInput += OnPreviewTextInput;
DataObject.AddPastingHandler(AssociatedObject, OnPaste);
}
private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(DataFormats.Text))
{
string text = Convert.ToString(e.DataObject.GetData(DataFormats.Text));
if (!IsValid(text, true))
{
e.CancelCommand();
}
}
else
{
e.CancelCommand();
}
}
void OnPreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
{
e.Handled = !IsValid(e.Text, false);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PreviewTextInput -= OnPreviewTextInput;
DataObject.RemovePastingHandler(AssociatedObject, OnPaste);
}
private bool IsValid(string newText, bool paste)
{
return !ExceedsMaxLength(newText, paste) && Regex.IsMatch(newText, RegularExpression);
}
private bool ExceedsMaxLength(string newText, bool paste)
{
if (MaxLength == 0) return false;
return LengthOfModifiedText(newText, paste) > MaxLength;
}
private int LengthOfModifiedText(string newText, bool paste)
{
var countOfSelectedChars = this.AssociatedObject.SelectedText.Length;
var caretIndex = this.AssociatedObject.CaretIndex;
string text = this.AssociatedObject.Text;
if (countOfSelectedChars > 0 || paste)
{
text = text.Remove(caretIndex, countOfSelectedChars);
return text.Length + newText.Length;
}
else
{
var insert = Keyboard.IsKeyToggled(Key.Insert);
return insert && caretIndex < text.Length ? text.Length : text.Length + newText.Length;
}
}
}
Here is the relevant view code:
<TextBox MaxLength="50" TextWrapping="Wrap" MaxWidth="150" Margin="4"
Text="{Binding Path=FileNameToPublish}" >
<interactivity:Interaction.Behaviors>
<v:AllowableCharactersTextBoxBehavior RegularExpression="^[0-9.\-]+$" MaxLength="50" />
</interactivity:Interaction.Behaviors>
</TextBox>
This is an improved solution of WilPs answer.
My improvements are:
Improved behaviour on Del and Backspace buttons
Added EmptyValue property, if empty string is inappropriate
Fixed some minor typos
/// <summary>
/// Regular expression for Textbox with properties:
/// <see cref="RegularExpression"/>,
/// <see cref="MaxLength"/>,
/// <see cref="EmptyValue"/>.
/// </summary>
public class TextBoxInputRegExBehaviour : Behavior<TextBox>
{
#region DependencyProperties
public static readonly DependencyProperty RegularExpressionProperty =
DependencyProperty.Register("RegularExpression", typeof(string), typeof(TextBoxInputRegExBehaviour), new FrameworkPropertyMetadata(".*"));
public string RegularExpression
{
get { return (string)GetValue(RegularExpressionProperty); }
set { SetValue(RegularExpressionProperty, value); }
}
public static readonly DependencyProperty MaxLengthProperty =
DependencyProperty.Register("MaxLength", typeof(int), typeof(TextBoxInputRegExBehaviour),
new FrameworkPropertyMetadata(int.MinValue));
public int MaxLength
{
get { return (int)GetValue(MaxLengthProperty); }
set { SetValue(MaxLengthProperty, value); }
}
public static readonly DependencyProperty EmptyValueProperty =
DependencyProperty.Register("EmptyValue", typeof(string), typeof(TextBoxInputRegExBehaviour), null);
public string EmptyValue
{
get { return (string)GetValue(EmptyValueProperty); }
set { SetValue(EmptyValueProperty, value); }
}
#endregion
/// <summary>
/// Attach our behaviour. Add event handlers
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewTextInput += PreviewTextInputHandler;
AssociatedObject.PreviewKeyDown += PreviewKeyDownHandler;
DataObject.AddPastingHandler(AssociatedObject, PastingHandler);
}
/// <summary>
/// Deattach our behaviour. remove event handlers
/// </summary>
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PreviewTextInput -= PreviewTextInputHandler;
AssociatedObject.PreviewKeyDown -= PreviewKeyDownHandler;
DataObject.RemovePastingHandler(AssociatedObject, PastingHandler);
}
#region Event handlers [PRIVATE] --------------------------------------
void PreviewTextInputHandler(object sender, TextCompositionEventArgs e)
{
string text;
if (this.AssociatedObject.Text.Length < this.AssociatedObject.CaretIndex)
text = this.AssociatedObject.Text;
else
{
// Remaining text after removing selected text.
string remainingTextAfterRemoveSelection;
text = TreatSelectedText(out remainingTextAfterRemoveSelection)
? remainingTextAfterRemoveSelection.Insert(AssociatedObject.SelectionStart, e.Text)
: AssociatedObject.Text.Insert(this.AssociatedObject.CaretIndex, e.Text);
}
e.Handled = !ValidateText(text);
}
/// <summary>
/// PreviewKeyDown event handler
/// </summary>
void PreviewKeyDownHandler(object sender, KeyEventArgs e)
{
if (string.IsNullOrEmpty(this.EmptyValue))
return;
string text = null;
// Handle the Backspace key
if (e.Key == Key.Back)
{
if (!this.TreatSelectedText(out text))
{
if (AssociatedObject.SelectionStart > 0)
text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart - 1, 1);
}
}
// Handle the Delete key
else if (e.Key == Key.Delete)
{
// If text was selected, delete it
if (!this.TreatSelectedText(out text) && this.AssociatedObject.Text.Length > AssociatedObject.SelectionStart)
{
// Otherwise delete next symbol
text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart, 1);
}
}
if (text == string.Empty)
{
this.AssociatedObject.Text = this.EmptyValue;
if (e.Key == Key.Back)
AssociatedObject.SelectionStart++;
e.Handled = true;
}
}
private void PastingHandler(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(DataFormats.Text))
{
string text = Convert.ToString(e.DataObject.GetData(DataFormats.Text));
if (!ValidateText(text))
e.CancelCommand();
}
else
e.CancelCommand();
}
#endregion Event handlers [PRIVATE] -----------------------------------
#region Auxiliary methods [PRIVATE] -----------------------------------
/// <summary>
/// Validate certain text by our regular expression and text length conditions
/// </summary>
/// <param name="text"> Text for validation </param>
/// <returns> True - valid, False - invalid </returns>
private bool ValidateText(string text)
{
return (new Regex(this.RegularExpression, RegexOptions.IgnoreCase)).IsMatch(text) && (MaxLength == int.MinValue || text.Length <= MaxLength);
}
/// <summary>
/// Handle text selection
/// </summary>
/// <returns>true if the character was successfully removed; otherwise, false. </returns>
private bool TreatSelectedText(out string text)
{
text = null;
if (AssociatedObject.SelectionLength <= 0)
return false;
var length = this.AssociatedObject.Text.Length;
if (AssociatedObject.SelectionStart >= length)
return true;
if (AssociatedObject.SelectionStart + AssociatedObject.SelectionLength >= length)
AssociatedObject.SelectionLength = length - AssociatedObject.SelectionStart;
text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart, AssociatedObject.SelectionLength);
return true;
}
#endregion Auxiliary methods [PRIVATE] --------------------------------
}
Usage is pretty straightforward:
<i:Interaction.Behaviors>
<behaviours:TextBoxInputRegExBehaviour RegularExpression="^\d+$" MaxLength="9" EmptyValue="0" />
</i:Interaction.Behaviors>
Here is a very simple and easy way to do this using MVVM.
Bind your textBox with an integer property in the view model, and this will work like a gem ... it will even show validation when a non-integer is entered in the textbox.
XAML code:
<TextBox x:Name="contactNoTxtBox" Text="{Binding contactNo}" />
View model code:
private long _contactNo;
public long contactNo
{
get { return _contactNo; }
set
{
if (value == _contactNo)
return;
_contactNo = value;
OnPropertyChanged();
}
}
Here I have a simple solution inspired by Ray's answer. This should be sufficient to identify any form of number.
This solution can also be easily modified if you want only positive numbers, integer values or values accurate to a maximum number of decimal places, etc.
As suggested in Ray's answer, you need to first add a PreviewTextInput event:
<TextBox PreviewTextInput="TextBox_OnPreviewTextInput"/>
Then put the following in the code behind:
private void TextBox_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
var textBox = sender as TextBox;
// Use SelectionStart property to find the caret position.
// Insert the previewed text into the existing text in the textbox.
var fullText = textBox.Text.Insert(textBox.SelectionStart, e.Text);
double val;
// If parsing is successful, set Handled to false
e.Handled = !double.TryParse(fullText, out val);
}
To invalid whitespace, we can add NumberStyles:
using System.Globalization;
private void TextBox_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
var textBox = sender as TextBox;
// Use SelectionStart property to find the caret position.
// Insert the previewed text into the existing text in the textbox.
var fullText = textBox.Text.Insert(textBox.SelectionStart, e.Text);
double val;
// If parsing is successful, set Handled to false
e.Handled = !double.TryParse(fullText,
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign,
CultureInfo.InvariantCulture,
out val);
}
Add in a VALIDATION RULE so that when the text changes, check to determine if the data is numeric, and if it is, allows processing to continue, and if it is not, prompts the user that only numeric data is accepted in that field.
Read more in Validation in Windows Presentation Foundation
The Extented WPF Toolkit has one: NumericUpDown
Could also simply implement a validation rule and apply it to the TextBox:
<TextBox>
<TextBox.Text>
<Binding Path="OnyDigitInput" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<conv:OnlyDigitsValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
With the implementation of the rule as follow (using the same Regex as proposed in other answers):
public class OnlyDigitsValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var validationResult = new ValidationResult(true, null);
if(value != null)
{
if (!string.IsNullOrEmpty(value.ToString()))
{
var regex = new Regex("[^0-9.-]+"); //regex that matches disallowed text
var parsingOk = !regex.IsMatch(value.ToString());
if (!parsingOk)
{
validationResult = new ValidationResult(false, "Illegal Characters, Please Enter Numeric Value");
}
}
}
return validationResult;
}
}
Another approach will be using an attached behavior, I implemented my custom TextBoxHelper class, which can be used on textboxes all over my project. Because I figured that subscribing to the events for every textboxes and in every individual XAML file for this purpose can be time consuming.
The TextBoxHelper class I implemented has these features:
Filtering and accepting only numbers in Double, Int, Uint and Natural format
Filtering and accepting only Even or Odd numbers
Handling paste event handler to prevent pasting invalid text into our numeric textboxes
Can set a Default Value which will be used to prevent invalid data as the last shot by subscribing to the textboxes TextChanged event
Here is the implementation of TextBoxHelper class:
public static class TextBoxHelper
{
#region Enum Declarations
public enum NumericFormat
{
Double,
Int,
Uint,
Natural
}
public enum EvenOddConstraint
{
All,
OnlyEven,
OnlyOdd
}
#endregion
#region Dependency Properties & CLR Wrappers
public static readonly DependencyProperty OnlyNumericProperty =
DependencyProperty.RegisterAttached("OnlyNumeric", typeof(NumericFormat?), typeof(TextBoxHelper),
new PropertyMetadata(null, DependencyPropertiesChanged));
public static void SetOnlyNumeric(TextBox element, NumericFormat value) =>
element.SetValue(OnlyNumericProperty, value);
public static NumericFormat GetOnlyNumeric(TextBox element) =>
(NumericFormat) element.GetValue(OnlyNumericProperty);
public static readonly DependencyProperty DefaultValueProperty =
DependencyProperty.RegisterAttached("DefaultValue", typeof(string), typeof(TextBoxHelper),
new PropertyMetadata(null, DependencyPropertiesChanged));
public static void SetDefaultValue(TextBox element, string value) =>
element.SetValue(DefaultValueProperty, value);
public static string GetDefaultValue(TextBox element) => (string) element.GetValue(DefaultValueProperty);
public static readonly DependencyProperty EvenOddConstraintProperty =
DependencyProperty.RegisterAttached("EvenOddConstraint", typeof(EvenOddConstraint), typeof(TextBoxHelper),
new PropertyMetadata(EvenOddConstraint.All, DependencyPropertiesChanged));
public static void SetEvenOddConstraint(TextBox element, EvenOddConstraint value) =>
element.SetValue(EvenOddConstraintProperty, value);
public static EvenOddConstraint GetEvenOddConstraint(TextBox element) =>
(EvenOddConstraint)element.GetValue(EvenOddConstraintProperty);
#endregion
#region Dependency Properties Methods
private static void DependencyPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is TextBox textBox))
throw new Exception("Attached property must be used with TextBox.");
switch (e.Property.Name)
{
case "OnlyNumeric":
{
var castedValue = (NumericFormat?) e.NewValue;
if (castedValue.HasValue)
{
textBox.PreviewTextInput += TextBox_PreviewTextInput;
DataObject.AddPastingHandler(textBox, TextBox_PasteEventHandler);
}
else
{
textBox.PreviewTextInput -= TextBox_PreviewTextInput;
DataObject.RemovePastingHandler(textBox, TextBox_PasteEventHandler);
}
break;
}
case "DefaultValue":
{
var castedValue = (string) e.NewValue;
if (castedValue != null)
{
textBox.TextChanged += TextBox_TextChanged;
}
else
{
textBox.TextChanged -= TextBox_TextChanged;
}
break;
}
}
}
#endregion
private static void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
var textBox = (TextBox)sender;
string newText;
if (textBox.SelectionLength == 0)
{
newText = textBox.Text.Insert(textBox.SelectionStart, e.Text);
}
else
{
var textAfterDelete = textBox.Text.Remove(textBox.SelectionStart, textBox.SelectionLength);
newText = textAfterDelete.Insert(textBox.SelectionStart, e.Text);
}
var evenOddConstraint = GetEvenOddConstraint(textBox);
switch (GetOnlyNumeric(textBox))
{
case NumericFormat.Double:
{
if (double.TryParse(newText, out double number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.Handled = true;
else
e.Handled = false;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.Handled = true;
else
e.Handled = false;
break;
}
}
else
e.Handled = true;
break;
}
case NumericFormat.Int:
{
if (int.TryParse(newText, out int number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.Handled = true;
else
e.Handled = false;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.Handled = true;
else
e.Handled = false;
break;
}
}
else
e.Handled = true;
break;
}
case NumericFormat.Uint:
{
if (uint.TryParse(newText, out uint number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.Handled = true;
else
e.Handled = false;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.Handled = true;
else
e.Handled = false;
break;
}
}
else
e.Handled = true;
break;
}
case NumericFormat.Natural:
{
if (uint.TryParse(newText, out uint number))
{
if (number == 0)
e.Handled = true;
else
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.Handled = true;
else
e.Handled = false;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.Handled = true;
else
e.Handled = false;
break;
}
}
}
else
e.Handled = true;
break;
}
}
}
private static void TextBox_PasteEventHandler(object sender, DataObjectPastingEventArgs e)
{
var textBox = (TextBox)sender;
if (e.DataObject.GetDataPresent(typeof(string)))
{
var clipboardText = (string) e.DataObject.GetData(typeof(string));
var newText = textBox.Text.Insert(textBox.SelectionStart, clipboardText);
var evenOddConstraint = GetEvenOddConstraint(textBox);
switch (GetOnlyNumeric(textBox))
{
case NumericFormat.Double:
{
if (double.TryParse(newText, out double number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.CancelCommand();
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.CancelCommand();
break;
}
}
else
e.CancelCommand();
break;
}
case NumericFormat.Int:
{
if (int.TryParse(newText, out int number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.CancelCommand();
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.CancelCommand();
break;
}
}
else
e.CancelCommand();
break;
}
case NumericFormat.Uint:
{
if (uint.TryParse(newText, out uint number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.CancelCommand();
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.CancelCommand();
break;
}
}
else
e.CancelCommand();
break;
}
case NumericFormat.Natural:
{
if (uint.TryParse(newText, out uint number))
{
if (number == 0)
e.CancelCommand();
else
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.CancelCommand();
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.CancelCommand();
break;
}
}
}
else
{
e.CancelCommand();
}
break;
}
}
}
else
{
e.CancelCommand();
}
}
private static void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var textBox = (TextBox)sender;
var defaultValue = GetDefaultValue(textBox);
var evenOddConstraint = GetEvenOddConstraint(textBox);
switch (GetOnlyNumeric(textBox))
{
case NumericFormat.Double:
{
if (double.TryParse(textBox.Text, out double number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
textBox.Text = defaultValue;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
textBox.Text = defaultValue;
break;
}
}
else
textBox.Text = defaultValue;
break;
}
case NumericFormat.Int:
{
if (int.TryParse(textBox.Text, out int number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
textBox.Text = defaultValue;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
textBox.Text = defaultValue;
break;
}
}
else
textBox.Text = defaultValue;
break;
}
case NumericFormat.Uint:
{
if (uint.TryParse(textBox.Text, out uint number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
textBox.Text = defaultValue;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
textBox.Text = defaultValue;
break;
}
}
else
textBox.Text = defaultValue;
break;
}
case NumericFormat.Natural:
{
if (uint.TryParse(textBox.Text, out uint number))
{
if(number == 0)
textBox.Text = defaultValue;
else
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
textBox.Text = defaultValue;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
textBox.Text = defaultValue;
break;
}
}
}
else
{
textBox.Text = defaultValue;
}
break;
}
}
}
}
And here is some example of its easy usage:
<TextBox viewHelpers:TextBoxHelper.OnlyNumeric="Double"
viewHelpers:TextBoxHelper.DefaultValue="1"/>
Or
<TextBox viewHelpers:TextBoxHelper.OnlyNumeric="Natural"
viewHelpers:TextBoxHelper.DefaultValue="3"
viewHelpers:TextBoxHelper.EvenOddConstraint="OnlyOdd"/>
Note that my TextBoxHelper resides in the viewHelpers xmlns alias.
I hope that this implementation eases some other one's work :)
I allowed numeric keypad numbers and backspace:
private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
int key = (int)e.Key;
e.Handled = !(key >= 34 && key <= 43 ||
key >= 74 && key <= 83 ||
key == 2);
}
This is the only code needed:
void MyTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = new Regex("[^0-9]+").IsMatch(e.Text);
}
This only allows numbers to be inputted into the text box.
To allow a decimal point or minus sign, you can change the regular expression to [^0-9.-]+.
I will assume that:
Your TextBox for which you want to allow numeric input only has its Text property initially set to some valid number value (for example, 2.7172).
Your Textbox is a child of your main window
Your main window is of class Window1
Your TextBox name is numericTB
Basic idea:
Add: private string previousText; to your main window class (Window1)
Add: previousText = numericTB.Text; to your main window constructor
Create a handler for the numericTB.TextChanged event to be something like this:
private void numericTB_TextChanged(object sender, TextChangedEventArgs e)
{
double num = 0;
bool success = double.TryParse(((TextBox)sender).Text, out num);
if (success & num >= 0)
previousText = ((TextBox)sender).Text;
else
((TextBox)sender).Text = previousText;
}
This will keep setting previousText to numericTB.Text as long as it is valid, and set numericTB.Text to its last valid value if the user writes something that you don't like. Of course, this is just basic idea, and it is just "idiot resistant", not "idiot proof". It doesn't handle the case in which the user messes with spaces, for example. So here is a complete solution which I think is "idiot proof", and if I'm wrong please tell me:
Content of your Window1.xaml file:
<Window x:Class="IdiotProofNumericTextBox.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<TextBox Height="30" Width="100" Name="numericTB" TextChanged="numericTB_TextChanged"/>
</Grid>
</Window>
Content of your Window.xaml.cs file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace IdiotProofNumericTextBox
{
public partial class Window1 : Window
{
private string previousText;
public Window1()
{
InitializeComponent();
previousText = numericTB.Text;
}
private void numericTB_TextChanged(object sender, TextChangedEventArgs e)
{
if (string.IsNullOrEmpty(((TextBox)sender).Text))
previousText = "";
else
{
double num = 0;
bool success = double.TryParse(((TextBox)sender).Text, out num);
if (success & num >= 0)
{
((TextBox)sender).Text.Trim();
previousText = ((TextBox)sender).Text;
}
else
{
((TextBox)sender).Text = previousText;
((TextBox)sender).SelectionStart = ((TextBox)sender).Text.Length;
}
}
}
}
}
And that's it. If you have many TextBoxes then I recommend creating a CustomControl that inherits from TextBox, so you can wrap previousText and numericTB_TextChanged up in a separate file.
If you do not want to write a lot of code to do a basic function (I don't know why people make long methods) you can just do this:
Add namespace:
using System.Text.RegularExpressions;
In XAML, set a TextChanged property:
<TextBox x:Name="txt1" TextChanged="txt1_TextChanged"/>
In WPF under txt1_TextChanged method, add Regex.Replace:
private void txt1_TextChanged(object sender, TextChangedEventArgs e)
{
txt1.Text = Regex.Replace(txt1.Text, "[^0-9]+", "");
}
PreviewTextInput += (s, e) =>
{
e.Handled = !e.Text.All(char.IsDigit);
};
For those looking for a quick and very simple implementation for this type of problem using only integers and decimal, in your XAML file, add a PreviewTextInput property to your TextBoxand then in your xaml.cs file use:
private void Text_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = !char.IsDigit(e.Text.Last()) && !e.Text.Last() == '.';
}
It's kind of redundant to keep checking the entire string every time unless, as others have mentioned, you're doing something with scientific notation (albeit, if you're adding certain characters like 'e', a simple regex adding symbols/characters is really simple and illustrated in other answers). But for simple floating point values, this solution will suffice.
Written as a one-liner with a lambda expression:
private void Text_PreviewTextInput(object sender, TextCompositionEventArgs e) => e.Handled = !char.IsDigit(e.Text.Last() && !e.Text.Last() == '.');
For developers who want their text fields to accept unsigned numbers only such as socket ports and so on:
WPF
<TextBox PreviewTextInput="Port_PreviewTextInput" MaxLines="1"/>
C#
private void Port_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = !int.TryParse(e.Text, out int x);
}
In Windows Forms it was easy; you can add an event for KeyPress and everything works easily. However, in WPF that event isn't there. But there is a much easier way for it.
The WPF TextBox has the TextChanged event which is general for everything. It includes pasting, typing and whatever that can come up to your mind.
So you can do something like this:
XAML:
<TextBox name="txtBox1" ... TextChanged="TextBox_TextChanged"/>
CODE BEHIND:
private void TextBox_TextChanged(object sender, TextChangedEventArgs e) {
string s = Regex.Replace(((TextBox)sender).Text, #"[^\d.]", "");
((TextBox)sender).Text = s;
}
This also accepts . , if you don't want it, just remove it from the regex statement to be #[^\d].
Note: This event can be used on many TextBox elements as it uses the sender object's Text. You only write the event once and can use it for multiple TextBox elements.
e.Handled = (int)e.Key >= 43 || (int)e.Key <= 34;
in preview keydown event of textbox.
We can do validation on text box changed event. The following implementation prevents keypress input other than numeric and one decimal point.
private void textBoxNumeric_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = sender as TextBox;
Int32 selectionStart = textBox.SelectionStart;
Int32 selectionLength = textBox.SelectionLength;
String newText = String.Empty;
int count = 0;
foreach (Char c in textBox.Text.ToCharArray())
{
if (Char.IsDigit(c) || Char.IsControl(c) || (c == '.' && count == 0))
{
newText += c;
if (c == '.')
count += 1;
}
}
textBox.Text = newText;
textBox.SelectionStart = selectionStart <= textBox.Text.Length ? selectionStart : textBox.Text.Length;
}
How about this? Works well for me. Hope I didn't miss any edge cases...
MyTextBox.PreviewTextInput += (sender, args) =>
{
if (!int.TryParse(args.Text, out _))
{
args.Handled = true;
}
};
DataObject.AddPastingHandler(MyTextBox, (sender, args) =>
{
var isUnicodeText = args.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
if (!isUnicodeText)
{
args.CancelCommand();
}
var data = args.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
if (!int.TryParse(data, out _))
{
args.CancelCommand();
}
});
The best and most elegant solution to only allow integer numbers in textbox (even in some range) is:
XAML:
<TextBox PreviewTextInput="ValidationTextBox" TextChanged="TextBox_TextChanged"/>
C#:
private void ValidationTextBox(object sender, TextCompositionEventArgs e)
{
int max = 100;
//do not allow futher incorrect typing
e.Handled = !(int.TryParse(((TextBox)sender).Text + e.Text, out int i) && i >= 1 && i <= max);
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
int max = 100;
if (!int.TryParse(((TextBox)sender).Text, out int j) || j < 1 || j > max)
{
//delete incoret input
((TextBox)sender).Text = "";
}
else
{
//delete leading zeros
((TextBox)sender).Text = j.ToString();
}
}
You can adjust the minimal and maximal acceptable number with max (min) by switch on ((TextBox)sender).Name.
This solution does not allow leading zeros or copy pasting the input. In every scenario you will have a correct number in the textbox.
Now I know this question has an accepted answer, but personally, I find it a bit confusing and I believe it should be easier than that. So I'll try to demonstrate how I got it to work as best as I can:
In Windows Forms, there's an event called KeyPress which is perfectly good for this kind of task. But that doesn't exist in WPF, so instead, we'll be using the PreviewTextInput event. Also, for the validation, I believe one can use a foreach to loop through the textbox.Text and check if it matches ;) the condition, but honestly, this is what regular expressions are for.
One more thing before we dive into the holy code. For the event to be fired, one can do two things:
Use XAML to tell the program which function to call: <PreviewTextInput="textBox_PreviewTextInput/>
Do it in the Loaded event of the form (which the textBox is in):
textBox.PreviewTextInput += onlyNumeric;
I think the second method is better because in situations like this, you'll mostly be required to apply the same condition (regex) to more than one TextBox and you don't want to repeat yourself!.
Finally, here's how you'd do it:
private void onlyNumeric(object sender, TextCompositionEventArgs e)
{
string onlyNumeric = #"^([0-9]+(.[0-9]+)?)$";
Regex regex = new Regex(onlyNumeric);
e.Handled = !regex.IsMatch(e.Text);
}
Here is my version of it. It's based on a base ValidatingTextBox class that just undoes what has been done if it's not "valid". It supports paste, cut, delete, backspace, +, - etc.
For 32-bit integer, there is a Int32TextBox class that just compares with an int. I have also added floating point validation classes.
public class ValidatingTextBox : TextBox
{
private bool _inEvents;
private string _textBefore;
private int _selectionStart;
private int _selectionLength;
public event EventHandler<ValidateTextEventArgs> ValidateText;
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
if (_inEvents)
return;
_selectionStart = SelectionStart;
_selectionLength = SelectionLength;
_textBefore = Text;
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
if (_inEvents)
return;
_inEvents = true;
var ev = new ValidateTextEventArgs(Text);
OnValidateText(this, ev);
if (ev.Cancel)
{
Text = _textBefore;
SelectionStart = _selectionStart;
SelectionLength = _selectionLength;
}
_inEvents = false;
}
protected virtual void OnValidateText(object sender, ValidateTextEventArgs e) => ValidateText?.Invoke(this, e);
}
public class ValidateTextEventArgs : CancelEventArgs
{
public ValidateTextEventArgs(string text) => Text = text;
public string Text { get; }
}
public class Int32TextBox : ValidatingTextBox
{
protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !int.TryParse(e.Text, out var value);
}
public class Int64TextBox : ValidatingTextBox
{
protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !long.TryParse(e.Text, out var value);
}
public class DoubleTextBox : ValidatingTextBox
{
protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !double.TryParse(e.Text, out var value);
}
public class SingleTextBox : ValidatingTextBox
{
protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !float.TryParse(e.Text, out var value);
}
public class DecimalTextBox : ValidatingTextBox
{
protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !decimal.TryParse(e.Text, out var value);
}
Note 1: When using WPF binding, you must make sure you use the class that fits the bound property type otherwise, it may lead to strange results.
Note 2: When using floating point classes with WPF binding, make sure the binding uses the current culture to match the TryParse method I've used.
I modified Rays answer to handle the highlighted text prior to checking the regular expression. I also adjusted the regular expression to only allow for two decimal places (currency).
private static readonly Regex _regex = new Regex(#"^[0-9]\d*(\.\d{0,2})?$");
private static bool IsTextAllowed(string text)
{
return _regex.IsMatch(text);
}
private bool IsAllowed(TextBox tb, string text)
{
bool isAllowed = true;
if (tb != null)
{
string currentText = tb.Text;
if (!string.IsNullOrEmpty(tb.SelectedText))
currentText = currentText.Remove(tb.CaretIndex, tb.SelectedText.Length);
isAllowed = IsTextAllowed(currentText.Insert(tb.CaretIndex, text));
}
return isAllowed;
}
private void Txt_PreviewCurrencyTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = !IsAllowed(sender as TextBox, e.Text);
}
private void TextBoxPasting(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(typeof(String)))
{
String text = (String)e.DataObject.GetData(typeof(String));
if (!IsAllowed(sender as TextBox, text))
e.CancelCommand();
}
else
e.CancelCommand();
}
And the xaml
<TextBox Name="Txt_Textbox" PreviewTextInput="Txt_PreviewCurrencyTextInput" DataObject.Pasting="TextBoxPasting" />
Here is a library for numeric input in WPF
It has properties like NumberStyles and RegexPatternfor validation.
Subclasses WPF TextBox
Use:
Private Sub DetailTextBox_PreviewTextInput( _
ByVal sender As Object, _
ByVal e As System.Windows.Input.TextCompositionEventArgs) _
Handles DetailTextBox.PreviewTextInput
If _IsANumber Then
If Not Char.IsNumber(e.Text) Then
e.Handled = True
End If
End If
End Sub
I was working with an unbound box for a simple project I was working on, so I couldn't use the standard binding approach. Consequently I created a simple hack that others might find quite handy by simply extending the existing TextBox control:
namespace MyApplication.InterfaceSupport
{
public class NumericTextBox : TextBox
{
public NumericTextBox() : base()
{
TextChanged += OnTextChanged;
}
public void OnTextChanged(object sender, TextChangedEventArgs changed)
{
if (!String.IsNullOrWhiteSpace(Text))
{
try
{
int value = Convert.ToInt32(Text);
}
catch (Exception e)
{
MessageBox.Show(String.Format("{0} only accepts numeric input.", Name));
Text = "";
}
}
}
public int? Value
{
set
{
if (value != null)
{
this.Text = value.ToString();
}
else
Text = "";
}
get
{
try
{
return Convert.ToInt32(this.Text);
}
catch (Exception ef)
{
// Not numeric.
}
return null;
}
}
}
}
Obviously, for a floating type, you would want to parse it as a float and so on. The same principles apply.
Then in the XAML file you need to include the relevant namespace:
<UserControl x:Class="MyApplication.UserControls.UnParameterisedControl"
[ Snip ]
xmlns:interfaceSupport="clr-namespace:MyApplication.InterfaceSupport"
>
After that you can use it as a regular control:
<interfaceSupport:NumericTextBox Height="23" HorizontalAlignment="Left" Margin="168,51,0,0" x:Name="NumericBox" VerticalAlignment="Top" Width="120" >
After using some of the solutions here for some time, I developed my own that works well for my MVVM setup. Note that it's not as dynamic as some of the other ones in a sense of still allowing users to enter erroneous characters, but it blocks them from pressing the button and thus doing anything. This goes well with my theme of graying out buttons when actions cannot be performed.
I have a TextBox that a user must enter a number of document pages to be printed:
<TextBox Text="{Binding NumberPagesToPrint, UpdateSourceTrigger=PropertyChanged}"/>
...with this binding property:
private string _numberPagesToPrint;
public string NumberPagesToPrint
{
get { return _numberPagesToPrint; }
set
{
if (_numberPagesToPrint == value)
{
return;
}
_numberPagesToPrint = value;
OnPropertyChanged("NumberPagesToPrint");
}
}
I also have a button:
<Button Template="{DynamicResource CustomButton_Flat}" Content="Set"
Command="{Binding SetNumberPagesCommand}"/>
...with this command binding:
private RelayCommand _setNumberPagesCommand;
public ICommand SetNumberPagesCommand
{
get
{
if (_setNumberPagesCommand == null)
{
int num;
_setNumberPagesCommand = new RelayCommand(param => SetNumberOfPages(),
() => Int32.TryParse(NumberPagesToPrint, out num));
}
return _setNumberPagesCommand;
}
}
And then there's the method of SetNumberOfPages(), but it's unimportant for this topic. It works well in my case because I don't have to add any code into the View's code-behind file and it allows me to control behavior using the Command property.
When checking a number value you can use the VisualBasic.IsNumeric function.