Windows Forms C# - I would like to make a textbox that automatically changes each time a user types or deletes one key from the textbox. I developed part of code.
//This will convert value from textbox to currency format when focus leave textbox
private void txtValormetrocubico_Leave(object sender, EventArgs e)
{
decimal cubic = Convert.ToDecimal(txtValormetrocubico.Text);
txtValormetrocubico.Text = string.Format("{0:c}", Convert.ToDecimal(cubic));
MessageBox.Show(txtValormetrocubico.Text);
}
//this only allow numbers and "." and "," on textimbox imput
private void txtValormetrocubico_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsControl(e.KeyChar)
&& !char.IsDigit(e.KeyChar)
&& e.KeyChar != '.' && e.KeyChar != ',')
{
e.Handled = true;
}
// only allow one decimal point
if (e.KeyChar == '.'
&& (sender as TextBox).Text.IndexOf('.') > -1)
{
e.Handled = true;
}
if (e.KeyChar == ','
&& (sender as TextBox).Text.IndexOf(',') > -1)
{
e.Handled = true;
}
}
The first time I enter a value in the text box, the value is converted to currency format perfectly, like 300 to $ 300.00. But I edit this textbox value again and press enter, it gives an error: "Input String was not in a Correct Format" pointing to the line below:
decimal cubic = Convert.ToDecimal(txtValormetrocubico.Text);
I think the problem is caused by the fact that the value is already in decimal format. So when I click on the field and press enter again, it causes an error because the value cannot be parsed. How do I avoid this error ?
EDIT:
My previous question was my first. As I am new user and don't have much knowledge in C#, I forgot to post my code. After studying some more, I made part of it work. Only this little problem remains. Please vote up, I was banned and cant make new questions because I had 7 down votes.
Thanks guys.
The problem is that the string contains the currency symbol
private void TextBox_LeaveEvent(object sender, EventArgs e)
{
var tb = sender as TextBox;
if(tb.Text.Length>0){
decimal cubic = Convert.ToDecimal(tb.Text);
tb.Text = string.Format("{0:c}", Convert.ToDecimal(cubic));
label1.Text = tb.Text;
}
}
Above the textbox.Text is set to contain the currency information:
tb.Text = string.Format("{0:c}", Convert.ToDecimal(cubic));
Since the textbox now contains a currency symbol (like € or $) the Convert.ToDecimal fails as soon as the TextBox_LeaveEvent fires again:
decimal cubic = Convert.ToDecimal(tb.Text);
If you bing for c# masked textbox you can find articles about masked textboxes. You cold also test if the string contains any non-number-charakters (if(tbText.IndexOf(" ") >-1){...})
Update with basic example
I uploaded a very basic example to remove the currency formating to github:
string RemoveCurrencyFormating(string input)
{
if(input.IndexOf(" ") !=-1){
var money = input.Substring(0, input.IndexOf(" ")-1);
return String.Format("{0:D0}", money);
}
return ""; // Todo: add Error Handling
}
On TextBox Enter Event you can do the following:
void TextBox_EnterEvent(object sender, EventArgs e)
{
var tb = sender as TextBox;
tb.Text = RemoveCurrencyFormating(tb.Text);
}
Related
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]", "");
I have several text boxes and would like to format them all the same way with these rules:
// limits to number, control keys, and decimal
// goes to the next text box when enter
private void tb_text1_KeyPress_1(object sender, KeyPressEventArgs e)
{
string newString = Regex.Replace(tb_text1.Text, "[^.0-9]", "");
tb_text1.MaxLength = 6;
e.Handled = (!char.IsDigit(e.KeyChar) && !Char.IsControl(e.KeyChar) && e.KeyChar != '.');
if (e.KeyChar == (char)(Keys.Enter))
{
this.GetNextControl(ActiveControl, true).Focus();
}
}
// removes restricted chars
private void tb_text1_Enter(object sender, EventArgs e)
{
tb_text1.Text = Regex.Replace(tb_text1.Text, "[^.0-9]", "");
}
// applies format at exit
private void tb_text1_Leave(object sender, EventArgs e)
{
tb_text1.Text = string.Format("{0,-6} [Ohm]", decimal.Parse(tb_text1.Text));
}
What is the best way? create a new text box class based on the text box?
Thanks.
Replace in methods your "tb_text1" variable to the "((TextBox)sender)", and now You can use Your code for any textbox.
It is very easy to do it with javascript . Please try that. I have done it i'm not able to find piece of that code right now . It is worth the effort because it will be very fast and will be running on client side.
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;
}
I'm building a form in a C# WinRT app, and I'd like to restrict the characters in one of the TextBox components to numerals only. (This TextBox would be for a user to enter a year into.)
I've searched for a while, but haven't been able to figure this one out without setting up an event listener on the TextChanged event, and inspecting the text property on every key press. Is there a way to simply say that a user can only enter specific characters into a TextBox?
The simplest thing that could possibly work is to bind to the OnTextChanged event and modify the text according to your rules.
<TextBox x:Name="TheText" TextChanged="OnTextChanged" MaxLength="4"/>
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
if (TheText.Text.Length == 0) return;
var text = TheText.Text;
int result;
var isValid = int.TryParse(text, out result);
if (isValid) return;
TheText.Text = text.Remove(text.Length - 1);
TheText.SelectionStart = text.Length;
}
However, I'd shy away from this approach since the mantra of Metro is touch first UI and you can easy do it in a touch first manner with a FlipView control.
Try setting TextBox.InputScope property to InputScopeNameValue.Number, as mentioned in Guidelines and checklist for text input in MSDN.
Valid Year
DateTime newDate;
var validYear = DateTime.TryParseExact("2012", "yyyy", CultureInfo.InvariantCulture,
DateTimeStyles.None, out newDate); //valid
Invalid Year
var validYear = DateTime.TryParseExact("0000", "yyyy", CultureInfo.InvariantCulture,
DateTimeStyles.None, out newDate); //invalid
This seems to work for me:
private void TextBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
if ((e.Key < VirtualKey.Number0) || (e.Key > VirtualKey.Number9))
{
// If it's not a numeric character, prevent the TextBox from handling the keystroke
e.Handled = true;
}
}
See the documentation for the VirtualKey enumeration for all the values.
Based on a posting at link, adding the tab to allow navigation.
private void decimalTextBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
bool isGoodData; // flag to make the flow clearer.
TextBox theTextBox = (TextBox)sender; // the sender is a textbox
if (e.Key>= Windows.System.VirtualKey.Number0 && e.Key <= Windows.System.VirtualKey.Number9) // allow digits
isGoodData = true;
else if (e.Key == Windows.System.VirtualKey.Tab)
isGoodData = true;
else if (e.Key >= Windows.System.VirtualKey.NumberPad0 && e.Key <= Windows.System.VirtualKey.NumberPad9) // allow digits
isGoodData = true;
else if (e.Key == Windows.System.VirtualKey.Decimal || (int)e.Key == 190) // character is a decimal point, 190 is the keyboard period code
// which is not in the VirtualKey enumeration
{
if (theTextBox.Text.Contains(".")) // search for a current point
isGoodData = false; // only 1 decimal point allowed
else
isGoodData = true; // this is the only one.
}
else if (e.Key == Windows.System.VirtualKey.Back) // allow backspace
isGoodData = true;
else
isGoodData = false; // everything else is bad
if (!isGoodData) // mark bad data as handled
e.Handled = true;
}
Use a MaskedTextBox control. For numerals only, just use the Mask property to specify the characters and the length, if any. e.g. if you want only five numbers to be entered, you set the mask property to "00000". Simple as that. Windows handles the restriction for you.