This question already has answers here:
How do I make a textbox that only accepts numbers?
(41 answers)
Closed 8 years ago.
I have a textbox with both letters and numbers in and other symbols in which you can find on your keyboard. I have this code which works fine when I manually put the data in and it only lets me put numbers in and deletes letters. Everything what I want, apart from none of it works if the data is copied and pasted in. Below is my code.
private void textBox7_TextChanged(object sender, EventArgs e)
{
Exception X = new Exception();
TextBox T = (TextBox)sender;
T.Text = T.Text.Trim();
try
{
if (T.Text != "-")
{
int x = int.Parse(T.Text);
}
}
catch (Exception)
{
try
{
int CursorIndex = T.SelectionStart - 1;
T.Text = T.Text.Remove(CursorIndex, 1);
//Align Cursor to same index
T.SelectionStart = CursorIndex;
T.SelectionLength = 0;
}
catch (Exception) { }
}
}
The problem with your current code is that you're removing only the last entered character from the cursor position. Pasting text with more than a character breaks your algo.
So let's say you're pasting in 9 letters, the CursorIndex is at 9, you remove only one character (that's the T.Text = T.Text.Remove(CursorIndex, 1); line) and you're left with 8 incorrect ones left.
A better approach (which isn't overly complex like yours) would look like this :
private void textBox7_TextChanged(object sender, EventArgs e)
{
textBox7.Text = string.Concat(textBox7.Text.Where(char.IsDigit));
}
So here we're replacing on each change the text with a new text containing each characters that passes the char.IsDigit test. The cursor won't be at the good position however. Unless multiple edits in the middle of the string are expected, it's probably best to only strap a
textBox1.SelectionStart = textBox1.Text.Length;
at the end of the method, it'll handle pasted text too.
To handle the case where you don't want to erase the character - when it is the only text in your textbox, you can add an obvious if condition. Globally, it'll look like this:
private void textBox7_TextChanged(object sender, EventArgs e)
{
if (textbox7.Text != "-")
textBox7.Text = string.Concat(textBox7.Text.Where(char.IsDigit));
textBox1.SelectionStart = textBox1.Text.Length;
}
Try this. It saves the old value in a var and if the new value cannot be parsed, it reverts the text to old. Otherwise it updates old to the most recent valid value. It's a different approach then you took, but much simpler in my opinion.
string old;
private void textBox7_TextChanged(object sender, EventArgs e) {
int i;
if (textBox1.Text == "" || int.TryParse(textBox7.Text, out i)) {
old = textBox7.Text;
} else {
textBox7.Text = old;
}
}
Basically your problem is that pasting a value will only call the TextChanged event once for the entire change. Your code is relying on the event being called for each character that is put into the textbox.
It sounds like all you really want to do is filter all non numeric values after a possible negative sign. If that is the case you can do it like this.
private void textBox7_TextChanged(object sender, EventArgs e)
{
textBox7.Text = string.Concat(
textBox7.Text.Trim().Where((c,i)=>char.IsDigit(c) || (i==0 && c=='-')));
}
This code is using Linq to go through each character, after trimming leading and trailing white space, and only keeping the ones that are digits or a negative sign if it is the first character. It's equivalent to the following.
private void textBox7_TextChanged(object sender, EventArgs e)
{
StringBuilder builder = new StringBuilder()
string trimmed = textBox7.Text.Trim();
for(int i=0; i<trimmed.Length; i++)
{
char c = trimmed.Text[i];
if(char.IsDigit(c) || (i==0 && c=='-'))
{
builder.Append(c);
}
}
textBox7.Text = builder.ToString();
}
To remove non-numeric characters from the textbox's text, try a regular expression replacement using Regex.Replace
private void numericTextbox_TextChanged(object sender, EventArgs e)
{
TextBox tb = (TextBox)sender;
tb.Text = Regex.Replace(tb.Text, "\\-?[^\d]", "");
}
This will replace any non-numeric character (except a dash at the front, for negative numbers) in your text with nothing every time the text in the box changes, whether the user types something directly or pastes in text from somewhere else.
Alternatively, if you want to keep any dashes (for, say, a phone number):
tb.Text = Regex.Replace(tb.Text, "\\[^-\d]", "");
Related
I have implemented some code in my Windows Form C# program, and the problem is that I want to have the following code in TextChangeEvent instead of the Validating event, but the .Focus() and .Select() methods don't work.
What is the solution for this?
private void jTextBox5_TextChangeEvent(object sender, EventArgs e)
{
if (jTextBox5.TextValue != "John")
{
jTextBox5.Focus();
}
}
You could try:
private void jTextBox5_TextChangeEvent(object sender, EventArgs e)
{
if (jTextBox5.Text.ToUpper().Trim() != "JOHN")
{
((Textbox)sender).Focus();
}
If you're trying to enforce that the user can only type the word "John" into the textbox, and you want to validate this on each key press, then you can do something like the following code, which examines the current text, one character at a time, and compares each character to it's counterpart in the word "John".
If a character doesn't match, then we set the text to only the substring of characters that do match, so they can continue typing:
private void jTextBox5_TextChanged(object sender, EventArgs e)
{
var requiredText = "John";
// Don't allow user to type (or paste) extra characters after correct word
if (jTextBox5.Text.StartsWith(requiredText))
{
jTextBox5.Text = requiredText;
}
else
{
// Compare each character to our text, and trim the text to only the correct entries
for (var i = 0; i < jTextBox5.TextLength; i++)
{
if (jTextBox5.Text[i] != requiredText[i])
{
jTextBox5.Text = jTextBox5.Text.Substring(0, i);
break;
}
}
}
// Set the selection to the end of the text so they can keep typing
jTextBox5.SelectionStart = jTextBox5.TextLength;
}
private void inputBox_KeyPress(object sender, KeyPressEventArgs e)
{
stringScan();
var regex1 = new Regex(#"[^+^\-^\b^\r\n]");
var regex2 = new Regex(#"[^0-9^+^\-^/^*^#^\b^\r\n]");
if (ListBox.Items.Count == 0 && string.IsNullOrWhiteSpace(inputBox.Text))
{
if (regex1.IsMatch(e.KeyChar.ToString()))
{
e.Handled = true;
toolTip1.Show("Plus or minus first then followed by numbers.", inputBox, 1500);
}
}
else
{
if (regex2.IsMatch(e.KeyChar.ToString()))
{
e.Handled = true;
}
}
}
public void stringScan()
{
char last_char = inputBox.ToString()[inputBox.ToString().Length - 1];
Console.WriteLine(last_char);
}
How can i get the last letter/number of a string?. Its really hard to explain so I'll show some screenshots.
the output should show "0" not "1".
It always show the "previews last" and not the latest one that i typed in the textbox.
Remember, when the event inputBox_KeyPress is raised, the typed key isn't added yet. Also, don't use regex for this. It will be overcomplicated.
Try the TextChanged event.
Text box key change will be executed every time u change the content of the text box
So for every value u enter u will be calling the string scan
This can be limited if u know what length the text is going to be
What about you try this:
public void stringScan()
{
String last_char = inputBox.ToString();
Console.WriteLine(last_char[last_char.Length-1]);
}
If you want last typed char I suggest:
private void inputBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
Console.WriteLine(e.Text);
}
If you want last char in textbox I suggest TextChanged event as already told by Jeroen van Langen:
private void inputBox_TextChanged(object sender, TextChangedEventArgs e)
{
string inputString = ((TextBox)sender).Text;
char lastChar = inputString.Last();
Console.WriteLine(lastChar);
}
Hope code samples help you
Edit:
Now I get that you probably want to get integer from textbox, if that's the matter, Get integer from Textbox could help you, code sample:
private void inputBox_TextChanged(object sender, TextChangedEventArgs e)
{
string inputString = ((TextBox)sender).Text;
int valueFromTextBox;
if (int.TryParse(inputString, out valueFromTextBox))
{
//parsing successful
}
else
{
//parsing failed.
}
}
I'm struggling with copying only letters from clipboard to textbox. I've tried to filter the clipboard and replacing it with only letters with regex, but I want to clipboard stay untouched.
I've tried:
private void Schowek()
{
string schoweczek;
if (Clipboard.ContainsText())
{
schoweczek = Clipboard.GetText();
var schoweczek2 = Regex.Replace(schoweczek, #"[\d-]", string.Empty);
Clipboard.SetText(schoweczek2);
}
}
and
private void textBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
Schowek();
}
}
but I think it's not the most efficient way to do that.
UPDATE:
I've come up with sth like this, tho it's not working:
private void textBox1_TextChanged(object sender, EventArgs e)
{
string checkstr=textBox1.Text;
char[] checkchar = checkstr.ToCharArray();
for (int i = 0; i < checkstr.Length; i++)
{
if (Char.IsLetter(checkchar[i])){
}
else
{
checkchar[i] = '\0';
}
}
checkstr=String.Join("", checkchar);
textBox1.Text = textBox1.Text + checkstr;
Instead of trying to hack the native paste function into pasting something other than what is in the clipboard, or changing the contents of the clipboard, you can just subscribe to an event of the textbox and "correct" it's value there.
If you are okay with allowing the user to input anything into the textbox and then cleaning the value once they move to the next control, use the validation events which will only fire after you move to the next field.
If you want instant correction, use the text changed event. But be careful about the amount of code you run in this event as it will fire multiple times rapidly when the user is typing in it. If possible, use the event that only fires after the textbox looses focus.
There is no need to touch the Clipboard at any point for what you are trying to do.
[EDIT]
Also, be wary of disallowing characters in people's names. There are lots of people who have numbers and weird characters in their names.
Falsehoods Programmers Beleive About Names
try this code
string strClip = Clipboard.GetText();
string textvalue = Regex.Replace(strClip, #"\d", "");
first you copy the text from the Clipboard the you replace the digits with "".It work fine i test it.
Take another point of view:
Consider "being" the textbox and watching what is entered or pasted. Then drop anything that is not allowed or (better user experience): allow any input, but deny "OK" and show an error until only valid input is present.
A very common way to achive this for a specific textbox instance is this:
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
// pseudo-code, fell free to implement it yourself.
if(!IsAllowed(e.KeyChar))
e.Handled = true;
}
private readonly char[] allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
private bool IsAllowed(char c)
{
return allowed.Contains(c);
}
Update
You could use a MaskedTextBox but I think your teacher will consider this cheating ;)
I could think of:
private bool reenter = false;
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (reenter || String.IsNullOrEmpty(textBox1.Text)) return;
var in = textBox1.Text.ToCharArray();
var out = Filter(input).ToArray();
var output = new String(out);
if (!textBox1.Text.Equals(output))
{
reenter = true;
textBox1.Text = output;
reenter = false;
}
}
private IEnumerable<char> Filter(IEnumerable<char> input)
{
foreach(var c in input)
if (IsAllowed(c))
yield return c;
}
Update 2
I just modified your own solution a bit.
First you have to prevent the TextChanged event from fireing multiple times (it fires also if you set the Text from code). You can either set a boolean variable like I did with reenter in my previous example or compare text and new text and only set it's value if different.
Setting a value inside the array to '\0' is wrong because \0 is the string terminator and every char after that is ignored. So if you paste a1b from the clipboard you get a char array { 'a', '\0', 'b' }. NET does not need a string terminator so you would see the result as "a\0b" but if you set the textbox value it will be only a not ab
Anyway, this is my solution. It uses another array and I also memorize SelectionStart to prevent the cursor from jumping to the first position after the text changes.
private void textBox1_TextChanged(object sender, EventArgs e)
{
string checkstr = textBox1.Text;
char[] checkchar = checkstr.ToCharArray();
char[] newchar = new char[checkstr.Length];
int pos = 0;
for (int i = 0; i < checkstr.Length; i++)
{
if (Char.IsLetter(checkchar[i]))
{
newchar[pos] = checkchar[i];
pos++;
}
}
checkstr = String.Join("", newchar);
if (textBox1.Text != checkstr)
{
var start = textBox1.SelectionStart;
textBox1.Text = checkstr;
textBox1.SelectionStart = start;
}
}
I have one TextBox with binding on DateTime type. I need to get a dot after first 2 chars and second 2 chars, for example: 12.12.1990.
I'm using behavior in TextChanged event, that code:
void tb_TextChanged(object sender, TextChangedEventArgs e)
{
int i = tb.SelectionStart;
if (i == 2 || i == 5)
{
tb.Text += ".";
tb.SelectionStart = i + 1;
}
}
That is working, but if I want to delete text by backspace, obviously I can't delete dots, because event is called again.
What is better way to solve it?
Solved
It works
But if you can, you may fix my algorithm.
public string oldText = "";
public string currText = "";
private void TextBox1_TextChanged(object sender, TextChangedEventArgs e)
{
oldText = currText;
currText = TextBox1.Text;
if (oldText.Length > currText.Length)
{
oldText = currText;
return;
}
if (TextBox1.Text.Length == currText.Length)
{
if (TextBox1.SelectionStart == 2 || TextBox1.SelectionStart == 5)
{
TextBox1.Text += ".";
TextBox1.SelectionStart = TextBox1.Text.Length;
}
}
}
I would do it in the KeyPress event, so you can filter by what kind of key it was (using the KeyChar argument with Char.IsLetter() and similar functions).
Also, add the dot when the next key is pressed. If the user has typed "12", don't add a dot yet. When the user presses 1 to add the second "12", add it then (before the new character).
Use String Format in the xaml control like so
StringFormat='{}{0:dd.MM.yyyy}'
I just tested it and this will even convert slashes to the dots.
For example
<TextBox.Text>
<Binding Path="Person.DateOfBirth" UpdateSourceTrigger="LostFocus" StringFormat='{}{0:dd.MM.yyyy}'></Binding>
</TextBox.Text>
If you are using a datepicker then you will need to override its textbox template as in the link below with the String Format above.
This link may help if if you are trying to apply it to a datepicker.
I recommend you to use a DateTimePicker and change its Format property to Short. Another option is to change your TextBox to a MaskedTextBox and changing its Mask property to ShortDate (00/00/0000) .DateTimePicker allows you not to do much about validating datetime values. But if you use a MaskedTextBox you should validate it. Sample link shows how to do validation.
I have modified above code
private void txt_in1_TextChanged(object sender, TextChangedEventArgs e)
{
int i = txt_in1.SelectionStart;
if (bsp1 != 1)
{
if (i == 2)
{
txt_in1.Text += ":";
txt_in1.SelectionStart = i + 1;
}
}
}
private void txt_in1_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Back)
{
bsp1 = 1;
}
else
{
bsp1 = 0;
}
}
I have taken another event which is keyup (equivalent keypress event), In that whenever backspace is detected it will flag bsp1 variable, which intern stop the text change event to put ":". here "bsp1" is define as global variable. (Code is for wpf, c#).
I have a problem that is haunting me for a while. I tried some solutions but they didn't worked.
I have a textbox that is for cash input ($999,99 for example). However I need to automatically input the "," and "." to display the value correctly.
I tried two solutions. One of them is this:
private void tx_ValorUnidade_TextChanged(object sender, EventArgs e)
{
string value = tx_ValorUnidade.Text.Replace(",", "").Replace("R$", "");
decimal ul;
//Check we are indeed handling a number
if (decimal.TryParse(value, out ul))
{
//Unsub the event so we don't enter a loop
tx_ValorUnidade.TextChanged -= tx_ValorUnidade_TextChanged;
//Format the text as currency
tx_ValorUnidade.Text = string.Format(System.Globalization.CultureInfo.CreateSpecificCulture("pt-BR"), "{0:C2}", ul);
tx_ValorUnidade.TextChanged += tx_ValorUnidade_TextChanged;
}
}
The result, however, is very weird.
The other one is this:
private void tx_ValorUnidade_KeyUp(object sender, KeyEventArgs e)
{
if (!string.IsNullOrEmpty(tx_ValorUnidade.Text))
{
System.Globalization.CultureInfo culture = new System.Globalization.CultureInfo("en-US");
int valueBefore = Int32.Parse(tx_ValorUnidade.Text, System.Globalization.NumberStyles.AllowThousands);
tx_ValorUnidade.Text = String.Format(culture, "{0:N0}", valueBefore);
tx_ValorUnidade.Select(tx_ValorUnidade.Text.Length, 0); *
}
}
This one kinda works, but there is a issue: if the user wants to insert somethink like $10,00 it can't. It also crashes after 5 numbers.
For original reference, I got the 2 codes from other questions here.
How can I fix it? Am I using the examples wrong? Any thought is welcome.
I think you will be better off when formatting when the user moves to the next control, e.g. like below. Otherwise it will be very confusing as the text will change itself as the user is typing:
private void textBox1_Leave(object sender, EventArgs e)
{
Double value;
if (Double.TryParse(textBox1.Text, out value))
textBox1.Text = String.Format(System.Globalization.CultureInfo.CurrentCulture, "{0:C2}", value);
else
textBox1.Text = String.Empty;
}
Some people might want to actually format a textbox as they type. So this is my solution if anyone is looking for one.
It actually assumes you are entering one digit at a time so therefore as you press "1" it assumes "$0.01" and when they press "2" it then assumes "$0.12" and so on and so forth.
I could not find anything online about formatting as they typed. It has been tested and if any errors let me know.
private void textBox1_TextChanged(object sender, EventArgs e)
{
//Remove previous formatting, or the decimal check will fail including leading zeros
string value = textBox1.Text.Replace(",", "")
.Replace("$", "").Replace(".", "").TrimStart('0');
decimal ul;
//Check we are indeed handling a number
if (decimal.TryParse(value, out ul))
{
ul /= 100;
//Unsub the event so we don't enter a loop
textBox1.TextChanged -= textBox1_TextChanged;
//Format the text as currency
textBox1.Text = string.Format(CultureInfo.CreateSpecificCulture("en-US"), "{0:C2}", ul);
textBox1.TextChanged += textBox1_TextChanged;
textBox1.Select(textBox1.Text.Length, 0);
}
bool goodToGo = TextisValid(textBox1.Text);
enterButton.Enabled = goodToGo;
if (!goodToGo)
{
textBox1.Text = "$0.00";
textBox1.Select(textBox1.Text.Length, 0);
}
}
private bool TextisValid(string text)
{
Regex money = new Regex(#"^\$(\d{1,3}(\,\d{3})*|(\d+))(\.\d{2})?$");
return money.IsMatch(text);
}
To make it look nice I'd recommend starting the text box with the text $0.00 on the form load like so:
private void Form1_Load(object sender, EventArgs e)
{
textBox1.Text = "$0.00";
textBox1.SelectionStart = inputBox.Text.Length;
}
Just a slight modification to GreatNates answer.
private bool KeyEnteredIsValid(string key)
{
Regex regex;
regex = new Regex("[^0-9]+$"); //regex that matches disallowed text
return regex.IsMatch(key);
}
and insert this method into the textboxs preview input event like this.
private void TextBox1_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = KeyEnteredIsValid(e.Text);
}
That way you make sure that you can't make any mistakes when typing anything. You are limited to numbers only with my methods, while nates methods are formatting your string.
Cheers.
We can try following one as well.
txtCost.Text = String.Format("{0:c2}", myObj.Cost);
I struggled with this for hours too. I tried to use maskedTextBox but that was just clunky for the users to enter text. I also didn't like having to deal with the masking for calculations. I also looked into using the databinding formatting but that just seemed overkill.
The way I ended up going was not to use a TextBox for inputting numbers. Use the NumericUpDown object instead. No need conversion and you can set your decimals and thousands commas in the properties if you like ;) I set my increment to 1000 since i was dealing with income.
Do be aware that the .Text that comes through will have commas when there is a penny decimal and amount over 1000 (i.e. 1,000.01) , otherwise the decimal and trailing 0s are dropped.
I also found this short and sweet solution which worked well but was unneccesary with numericUpDown. You can put this on leave event.
Decimal val;
if (Decimal.TryParse(TxtCosPrice.Text, out val))
TxtCosPrice.Text = val.ToString("C");
else
MessageBox.Show("Oops! Bad input!");
This is my solution, it puts only dots, not money symbol. Hope can help somenone.
private void textBox1_KeyUp(object sender, KeyEventArgs e)
{
e.SuppressKeyPress = TextBox2Currency((TextBox)sender, e.KeyValue);
}
private bool TextBox2Currency(TextBox sender,int keyval)
{
if ((keyval >= 48 && keyval <= 58) || keyval == 46 || keyval == 8)
{
int pos = sender.SelectionStart;
int oriLen = sender.Text.Length;
string currTx = sender.Text.Replace(".", "").ToCurrency();
int modLen = currTx.Length;
if (modLen != oriLen)
pos += (modLen - oriLen);
sender.Text = currTx;
if ( pos>=0)
sender.SelectionStart = pos;
return false;
}
return true;
}