I need a textbox keypress handler which handles a decimal input range of 0 to 9999999999.99 value. I have this code below but is not serving the purpose. With it I cannot enter decimals after 10 digits.
public static void NumericWithDecimalTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) &&
(e.KeyChar != '.'))
{
e.Handled = true;
}
TextBox textBox = sender as TextBox;
string[] parts = textBox.Text.Split('.');
// only allow one decimal point
if (((e.KeyChar == '.') && (textBox.Text.IndexOf('.') > -1)) || (!char.IsControl(e.KeyChar) && ((parts[0].Length >= 10))))
{
e.Handled = true;
}
}
You could simplify the process by having the data validated, along the lines of:
public static void NumericWithDecimalTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
var textBox = sender as TextBox;
var enteredValue = textBox.Text;
var decimalValue = 0M;
if (decimal.TryParse(enteredValue, out decimalValue) && ValueIsWithinRange(decimalValue, 0M, 9999999999.99M))
{
Model.ThePropertyStoringTheValue = decimalValue; // wherever you need to store the value
}
else
{
// Inform the user they have entered invalid data (i.e. change the textbox background colour or show a message box)
}
}
private bool ValueIsWithinRange(decimal valueToValidate, decimal lower, decimal upper)
{
return valueToValidate >= lower && valueToValidate <= upper
}
That way, if the value is valid, it is written to the model (following good MVC design practices) and if it is invalid, the user is informed with a message that would allow them to make corrections (e.g. "the value you have entered isn't a valid decimal" or "the value must not be negative" etc.)
I have a textbox where people have to enter a number but I don't want to them to type the number 0 in first, how do I do this?
Example: If they type, 10 it's ok but if they type 010 it's not ok and I want a window to appear and tell them to try again.
Thanks!
Add TextChanged handler to your textbox (in your form find event for TextBox and type this name: TextBox_TextChanged):
void TextBox_TextChanged(object sender, EventArgs e)
{
var textBox = sender as TextBox;
if (textBox != null)
{
textBox.Text = textBox.Text.Trim();
if (textBox.Text.Length > 0 && textBox.Text[0] == '0')
{
textBox.Text = textBox.Text.TrimStart('0');
MessageBox.Show("Incorrect value");
}
}
}
Or in code:
TextBox textBox = new TextBox();
textBox.TextChanged += TextBox_TextChanged;
Use the TextChanged event. (Double click on the event in the properties of the textbox in the designer) In this example trim any leading spaces before comparison and remove the character if it is a 0
private void textBox1_TextChanged(object sender, EventArgs e)
{
string input = textBox1.Text.TrimStart(' ');
if (input.Length == 1)
{
textBox1.Text = input == "0" ? "" : input;
}
}
EDIT
As pointed out by m.rogalski and Roma the version above allows for entering the 0 after the input of a valid character. This version bellow will correct this mistake:
private void textBox1_TextChanged(object sender, EventArgs e)
{
textBox1.Text = textBox1.Text.TrimStart('0');
}
This should work:
private void MyTextBox_TextChanged(object sender, EventArgs e)
{
var textBox = sender as TextBox;
if (textBox.Text.StartsWith("0"))
//alert user
MessageBox.Show("Invalid starting character");
}
Of course you need to bind TextChanged event in designer or in code like this:
MyTextBox.TextChanged += MyTextBox_TextChanged;
Starting from the beginnign :
If they type, 10 it's ok but if they type 010 it's not ok
If it's okay for user to type integer ranging from 0 to infinity then you can use this :
if(textBox.Text.Length > 1 && textBox.Text[0] == '0')
This assumes that user can type '0' as a first sign only if it is only sign. If that's not what you wanted then change it to this :
if(textBox.Text.Length > 0 && textBox.Text[0] == '0')
Going further :
I want a window to appear and tell them to try again
You can just show MessageBox and clear text inside textBox :
textBox.Text = string.Empty;
MessageBox.Show("Value entered is incorrect. Please try again");
Then combining these you'll have something like :
if(textBox.Text.Length > 1/* or 0 depending on what you need */ && textBox.Text[0] == '0')
{
textBox.Text = string.Empty;
MessageBox.Show("Value entered is incorrect. Please try again");
}
put this code to after Initialize method
textBox1.TextChanged = (s, o) =>
{
if (textBox1.Text.StartsWith("0"))
{
textBox1.Text = textBox1.Text.Remove(0, 1);
MessageBox.Show("Cant start with '0'");
}
};
Perhaps you could have a ViewModel representing some textboxes, one of which is your "Number" textbox. Then you can use an attribute on your Number property to make sure it is validated.
public class MyViewModel
{
...
[Required]
[NumberValidation]
string NumberTextBox {get;set;}
where NumberValidation is another public class implementing a ValidationAttribute
[AttributeUsage(AttributeTargets.Property]
public class NumberValidation : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value.StartsWith("0"))
{
return new ValidationResult(ErrorMessage, "Please enter another number!");
}
return ValidationResult.Success;
}
}
I have the following code:
private void txtNR_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) && (e.KeyChar != '.'))
{
e.Handled = true;
}
else
{
}
// only allow one decimal point
if ((e.KeyChar == '.') && ((sender as TextBox).Text.IndexOf('.') > -1))
{
e.Handled = true;
}
else
{
MessageBox.Show("You cannot type letters!");
}
}
My question is: when I am trying to type letters the warning message is coming up front but the same is happening when i'm trying to type numbers, and after i click ok on message, the number is writtend down inside. Can you help me understand why?
Your code should be like that:
if (!(char.IsControl(e.KeyChar) || char.IsDigit(e.KeyChar) || (e.KeyChar == '.')))
{
e.Handled = true;
MessageBox.Show("You cannot type letters!");
}
Replace
else { }
with
else
Because of extra {} the second if statement is executed even if first one is handled. Due to this you are seeing message box even if the character is digit (which is already handled)
Your condition is alright. You just need to set the Char to nothing
Try this:
// only allow one decimal point
if ((e.KeyChar == '.') && ((sender as TextBox).Text.IndexOf('.') > -1))
{
e.Handled = true;
}
else
{
MessageBox.Show("You cannot type letters!");
e.KeyChar = '\0';
}
I would suggest you to validate the content of the textbox after the user do his input ont it. Like #Sinatr said, for the user this could be an annoying thing to show a messagebox every time he writes wrong input.
It should also simplify your code, I think.
If the text is not numeric, then display the messagebox, something like that.
textbox_Validating(){
decimal d;
if(decimal.TryParse(textBox1.Text, out d))
{
//valid
}
else
{
//invalid
MessageBox.Show("Please enter a valid number");
return;
}
}
Something like that... Sorry for the eventual errors in the code.
Good luck.
You don't say what your overall aim is.
However to correctly handle the event you are after this will work. You don't really want a pop up message all the time, better validate on form submission.
Are you trying to make a decimal text box ?
You've also got to take into account copy and paste which these events just aren't going to cover.
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) && (e.KeyChar != '.'))
{
e.Handled = true;
}
else
{ // only allow one decimal point
if ((e.KeyChar == '.'))
{
if (((TextBox) sender).Text.Contains("."))
{
e.Handled = true;
}
}
else
{
if (char.IsControl(e.KeyChar))
{
return;
}
if (!char.IsDigit(e.KeyChar))
{
MessageBox.Show("You cannot type letters!");
}
}
}
In my specific case, I need the value in propertyPriceTextBox to be numeric only, and a whole number. A value also has to be entered, and I can just Messagebox.Show() a warning and that's all I'd need to do.
This is what I have so far.
private void computeButton_Click(object sender, EventArgs e)
{
decimal propertyPrice;
if ((decimal.TryParse(propertyPriceTextBox.Text, out propertyPrice)))
decimal.Parse(propertyPriceTextBox.Text);
{
if (residentialRadioButton.Checked == true)
commisionLabel.Text = (residentialCom * propertyPrice).ToString("c");
if (commercialRadioButton.Checked == true)
commisionLabel.Text = (commercialCom * propertyPrice).ToString("c");
if (hillsRadioButton.Checked == true)
countySalesTaxTextBox.Text = ( hilssTax * propertyPrice).ToString("c");
if (pascoRadioButton.Checked == true)
countySalesTaxTextBox.Text = (pascoTax * propertyPrice).ToString("c");
if (polkRadioButton.Checked == true)
countySalesTaxTextBox.Text = (polkTax * propertyPrice).ToString("c");
decimal result;
result = (countySalesTaxTextBox.Text + stateSalesTaxTextBox.Text + propertyPriceTextBox.Text + comissionTextBox.Text).ToString("c");
}
else (.)
MessageBox.Show("Property Price must be a whole number.");
}
Rather than using decimal.TryParse use Int32.TryParse this will return false if the value is a non integer:
int propertyPrice;
if (Int32.TryParse(propertyPriceTextBox.Text, out propertyPrice)
{
// use propertyPrice
}
else
{
MessageBox.Show("Property Price must be a whole number.");
}
There's no need to call Parse again as TryParse does the conversion and returns true if it succeeds and false otherwise.
You can achieve it this way
int outParse;
// Check if the point entered is numeric or not
if (Int32.TryParse(propertyPriceTextBox.Text, out outParse) && outParse)
{
// Do what you want to do if numeric
}
else
{
// Do what you want to do if not numeric
}
How are you handling the entry of numeric values in WPF applications?
Without a NumericUpDown control, I've been using a TextBox and handling its PreviewKeyDown event with the code below, but it's pretty ugly.
Has anyone found a more graceful way to get numeric data from the user without relying on a third-party control?
private void NumericEditPreviewKeyDown(object sender, KeyEventArgs e)
{
bool isNumPadNumeric = (e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9) || e.Key == Key.Decimal;
bool isNumeric = (e.Key >= Key.D0 && e.Key <= Key.D9) || e.Key == Key.OemPeriod;
if ((isNumeric || isNumPadNumeric) && Keyboard.Modifiers != ModifierKeys.None)
{
e.Handled = true;
return;
}
bool isControl = ((Keyboard.Modifiers != ModifierKeys.None && Keyboard.Modifiers != ModifierKeys.Shift)
|| e.Key == Key.Back || e.Key == Key.Delete || e.Key == Key.Insert
|| e.Key == Key.Down || e.Key == Key.Left || e.Key == Key.Right || e.Key == Key.Up
|| e.Key == Key.Tab
|| e.Key == Key.PageDown || e.Key == Key.PageUp
|| e.Key == Key.Enter || e.Key == Key.Return || e.Key == Key.Escape
|| e.Key == Key.Home || e.Key == Key.End);
e.Handled = !isControl && !isNumeric && !isNumPadNumeric;
}
How about:
protected override void OnPreviewTextInput(System.Windows.Input.TextCompositionEventArgs e)
{
e.Handled = !AreAllValidNumericChars(e.Text);
base.OnPreviewTextInput(e);
}
private bool AreAllValidNumericChars(string str)
{
foreach(char c in str)
{
if(!Char.IsNumber(c)) return false;
}
return true;
}
This is how I do it. It uses a regular expression to check if the text that will be in the box is numeric or not.
Regex NumEx = new Regex(#"^-?\d*\.?\d*$");
private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
if (sender is TextBox)
{
string text = (sender as TextBox).Text + e.Text;
e.Handled = !NumEx.IsMatch(text);
}
else
throw new NotImplementedException("TextBox_PreviewTextInput Can only Handle TextBoxes");
}
There is now a much better way to do this in WPF and Silverlight. If your control is bound to a property, all you have to do is change your binding statement a bit. Use the following for your binding:
<TextBox Text="{Binding Number, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"/>
Note that you can use this on custom properties too, all you have to do is throw an exception if the value in the box is invalid and the control will get highlighted with a red border. If you click on the upper right of the red border then the exception message will pop up.
I've been using an attached property to allow the user to use the up and down keys to change the values in the text box. To use it, you just use
<TextBox local:TextBoxNumbers.SingleDelta="1">100</TextBox>
This doesn't actually address the validation issues that are referred to in this question, but it addresses what I do about not having a numeric up/down control. Using it for a little bit, I think I might actually like it better than the old numeric up/down control.
The code isn't perfect, but it handles the cases I needed it to handle:
Up arrow, Down arrow
Shift + Up arrow, Shift + Down arrow
Page Up, Page Down
Binding Converter on the text property
Code behind
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
namespace Helpers
{
public class TextBoxNumbers
{
public static Decimal GetSingleDelta(DependencyObject obj)
{
return (Decimal)obj.GetValue(SingleDeltaProperty);
}
public static void SetSingleDelta(DependencyObject obj, Decimal value)
{
obj.SetValue(SingleDeltaProperty, value);
}
// Using a DependencyProperty as the backing store for SingleValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SingleDeltaProperty =
DependencyProperty.RegisterAttached("SingleDelta", typeof(Decimal), typeof(TextBoxNumbers), new UIPropertyMetadata(0.0m, new PropertyChangedCallback(f)));
public static void f(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
TextBox t = o as TextBox;
if (t == null)
return;
t.PreviewKeyDown += new System.Windows.Input.KeyEventHandler(t_PreviewKeyDown);
}
private static Decimal GetSingleValue(DependencyObject obj)
{
return GetSingleDelta(obj);
}
private static Decimal GetDoubleValue(DependencyObject obj)
{
return GetSingleValue(obj) * 10;
}
private static Decimal GetTripleValue(DependencyObject obj)
{
return GetSingleValue(obj) * 100;
}
static void t_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
TextBox t = sender as TextBox;
Decimal i;
if (t == null)
return;
if (!Decimal.TryParse(t.Text, out i))
return;
switch (e.Key)
{
case System.Windows.Input.Key.Up:
if (Keyboard.Modifiers == ModifierKeys.Shift)
i += GetDoubleValue(t);
else
i += GetSingleValue(t);
break;
case System.Windows.Input.Key.Down:
if (Keyboard.Modifiers == ModifierKeys.Shift)
i -= GetDoubleValue(t);
else
i -= GetSingleValue(t);
break;
case System.Windows.Input.Key.PageUp:
i += GetTripleValue(t);
break;
case System.Windows.Input.Key.PageDown:
i -= GetTripleValue(t);
break;
default:
return;
}
if (BindingOperations.IsDataBound(t, TextBox.TextProperty))
{
try
{
Binding binding = BindingOperations.GetBinding(t, TextBox.TextProperty);
t.Text = (string)binding.Converter.Convert(i, null, binding.ConverterParameter, binding.ConverterCulture);
}
catch
{
t.Text = i.ToString();
}
}
else
t.Text = i.ToString();
}
}
}
I decided to simplify the reply marked as the answer on here to basically 2 lines using a LINQ expression.
e.Handled = !e.Text.All(Char.IsNumber);
base.OnPreviewTextInput(e);
Why don't you just try using the KeyDown event rather than the PreviewKeyDown Event. You can stop the invalid characters there, but all the control characters are accepted. This seems to work for me:
private void NumericKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
bool isNumPadNumeric = (e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9);
bool isNumeric =((e.Key >= Key.D0 && e.Key <= Key.D9) && (e.KeyboardDevice.Modifiers == ModifierKeys.None));
bool isDecimal = ((e.Key == Key.OemPeriod || e.Key == Key.Decimal) && (((TextBox)sender).Text.IndexOf('.') < 0));
e.Handled = !(isNumPadNumeric || isNumeric || isDecimal);
}
I use a custom ValidationRule to check if text is numeric.
public class DoubleValidation : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
if (value is string)
{
double number;
if (!Double.TryParse((value as string), out number))
return new ValidationResult(false, "Please enter a valid number");
}
return ValidationResult.ValidResult;
}
Then when I bind a TextBox to a numeric property, I add the new custom class to the Binding.ValidationRules collection. In the example below the validation rule is checked everytime the TextBox.Text changes.
<TextBox>
<TextBox.Text>
<Binding Path="MyNumericProperty" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:DoubleValidation/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
public class NumericTextBox : TextBox
{
public NumericTextBox()
: base()
{
DataObject.AddPastingHandler(this, new DataObjectPastingEventHandler(CheckPasteFormat));
}
private Boolean CheckFormat(string text)
{
short val;
return Int16.TryParse(text, out val);
}
private void CheckPasteFormat(object sender, DataObjectPastingEventArgs e)
{
var isText = e.SourceDataObject.GetDataPresent(System.Windows.DataFormats.Text, true);
if (isText)
{
var text = e.SourceDataObject.GetData(DataFormats.Text) as string;
if (CheckFormat(text))
{
return;
}
}
e.CancelCommand();
}
protected override void OnPreviewTextInput(System.Windows.Input.TextCompositionEventArgs e)
{
if (!CheckFormat(e.Text))
{
e.Handled = true;
}
else
{
base.OnPreviewTextInput(e);
}
}
}
Additionally you may customize the parsing behavior by providing appropriate dependency properties.
Combining the ideas from a few of these answers, I have created a NumericTextBox that
Handles decimals
Does some basic validation to ensure any entered '-' or '.' is valid
Handles pasted values
Please feel free to update if you can think of any other logic that should be included.
public class NumericTextBox : TextBox
{
public NumericTextBox()
{
DataObject.AddPastingHandler(this, OnPaste);
}
private void OnPaste(object sender, DataObjectPastingEventArgs dataObjectPastingEventArgs)
{
var isText = dataObjectPastingEventArgs.SourceDataObject.GetDataPresent(System.Windows.DataFormats.Text, true);
if (isText)
{
var text = dataObjectPastingEventArgs.SourceDataObject.GetData(DataFormats.Text) as string;
if (IsTextValid(text))
{
return;
}
}
dataObjectPastingEventArgs.CancelCommand();
}
private bool IsTextValid(string enteredText)
{
if (!enteredText.All(c => Char.IsNumber(c) || c == '.' || c == '-'))
{
return false;
}
//We only validation against unselected text since the selected text will be replaced by the entered text
var unselectedText = this.Text.Remove(SelectionStart, SelectionLength);
if (enteredText == "." && unselectedText.Contains("."))
{
return false;
}
if (enteredText == "-" && unselectedText.Length > 0)
{
return false;
}
return true;
}
protected override void OnPreviewTextInput(System.Windows.Input.TextCompositionEventArgs e)
{
e.Handled = !IsTextValid(e.Text);
base.OnPreviewTextInput(e);
}
}
You can also try using data validation if users commit data before you use it. Doing that I found was fairly simple and cleaner than fiddling about with keys.
Otherwise, you could always disable Paste too!
My Version of Arcturus answer, can change the convert method used to work with int / uint / decimal / byte (for colours) or any other numeric format you care to use, also works with copy / paste
protected override void OnPreviewTextInput( System.Windows.Input.TextCompositionEventArgs e )
{
try
{
if ( String.IsNullOrEmpty( SelectedText ) )
{
Convert.ToDecimal( this.Text.Insert( this.CaretIndex, e.Text ) );
}
else
{
Convert.ToDecimal( this.Text.Remove( this.SelectionStart, this.SelectionLength ).Insert( this.SelectionStart, e.Text ) );
}
}
catch
{
// mark as handled if cannot convert string to decimal
e.Handled = true;
}
base.OnPreviewTextInput( e );
}
N.B. Untested code.
Add this to the main solution to make sure the the binding is updated to zero when the textbox is cleared.
protected override void OnPreviewKeyUp(System.Windows.Input.KeyEventArgs e)
{
base.OnPreviewKeyUp(e);
if (BindingOperations.IsDataBound(this, TextBox.TextProperty))
{
if (this.Text.Length == 0)
{
this.SetValue(TextBox.TextProperty, "0");
this.SelectAll();
}
}
}
Call me crazy, but why not put plus and minus buttons at either side of the TextBox control and simply prevent the TextBox from receiving cursor focus, thereby creating your own cheap NumericUpDown control?
private void txtNumericValue_PreviewKeyDown(object sender, KeyEventArgs e)
{
KeyConverter converter = new KeyConverter();
string key = converter.ConvertToString(e.Key);
if (key != null && key.Length == 1)
{
e.Handled = Char.IsDigit(key[0]) == false;
}
}
This is the easiest technique I've found to accomplish this. The down side is that the context menu of the TextBox still allows non-numerics via Paste. To resolve this quickly I simply added the attribute/property: ContextMenu="{x:Null}" to the TextBox thereby disabling it. Not ideal but for my scenario it will suffice.
Obviously you could add a few more keys/chars in the test to include additional acceptable values (e.g. '.', '$' etc...)
Private Sub Value1TextBox_PreviewTextInput(ByVal sender As Object, ByVal e As TextCompositionEventArgs) Handles Value1TextBox.PreviewTextInput
Try
If Not IsNumeric(e.Text) Then
e.Handled = True
End If
Catch ex As Exception
End Try
End Sub
Worked for me.
Can you not just use something like the following?
int numericValue = 0;
if (false == int.TryParse(yourInput, out numericValue))
{
// handle non-numeric input
}
void PreviewTextInputHandler(object sender, TextCompositionEventArgs e)
{
string sVal = e.Text;
int val = 0;
if (sVal != null && sVal.Length > 0)
{
if (int.TryParse(sVal, out val))
{
e.Handled = false;
}
else
{
e.Handled = true;
}
}
}
Can also use a converter like:
public class IntegerFormatConverter : IValueConverter
{
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int result;
int.TryParse(value.ToString(), out result);
return result;
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int result;
int.TryParse(value.ToString(), out result);
return result;
}
}