- Numeric TextBox - using Double.TryParse
- private int oldIndex = 0;
- private string oldText = String.Empty;
- private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
- {
- double val;
- if (!Double.TryParse(textBox1.Text, out val))
- {
- textBox1.TextChanged -= textBox1_TextChanged;
- textBox1.Text = oldText;
- textBox1.CaretIndex = oldIndex;
- textBox1.TextChanged += textBox1_TextChanged;
- }
- }
- private void textBox1_KeyDown(object sender, KeyEventArgs e)
- {
- oldIndex = textBox1.CaretIndex;
- oldText = textBox1.Text;
- }
- private void textBox1_PreviewKeyDown(object sender, KeyEventArgs e)
- {
- if (e.Key == Key.Space)
- {
- e.Handled = true;
- }
- }
- private void textBox1_PreviewTextInput(object sender, TextCompositionEventArgs e)
- {
- //Create a string combining the text to be entered with what is already there.
- //Being careful of new text positioning here, though it isn't truly necessary for validation of number format.
- int cursorPos = textBox1.CaretIndex;
- string nextText;
- if (cursorPos > 0)
- {
- nextText = textBox1.Text.Substring(0, cursorPos) + e.Text + textBox1.Text.Substring(cursorPos);
- }
- else
- {
- nextText = textBox1.Text + e.Text;
- }
- double testVal;
- if (!Double.TryParse(nextText, out testVal))
- {
- e.Handled = true;
- }
- }
- private void OnPaste(object sender, DataObjectPastingEventArgs e)
- {
- double testVal;
- bool ok = false;
- var isText = e.SourceDataObject.GetDataPresent(System.Windows.DataFormats.Text, true);
- if (isText)
- {
- var text = e.SourceDataObject.GetData(DataFormats.Text) as string;
- if (Double.TryParse(text, out testVal))
- {
- ok = true;
- }
- }
- if (!ok)
- {
- e.CancelCommand();
- }
- }
- DataObject.AddPastingHandler(textBox1, new DataObjectPastingEventHandler(OnPaste));
- private static void PreviewTextInputForDouble(object sender,
- TextCompositionEventArgs e)
- {
- // e.Text contains only new text and we should create full text manually
- var textBox = (TextBox)sender;
- string fullText;
- // If text box contains selected text we should replace it with e.Text
- if (textBox.SelectionLength > 0)
- {
- fullText = textBox.Text.Replace(textBox.SelectedText, e.Text);
- }
- else
- {
- // And only otherwise we should insert e.Text at caret position
- fullText = textBox.Text.Insert(textBox.CaretIndex, e.Text);
- }
- // Now we should validate our fullText, but not with
- // Double.TryParse. We should use more complicated validation logic.
- bool isTextValid = TextBoxDoubleValidator.IsValid(fullText);
- // Interrupting this event if fullText is invalid
- e.Handled = !isTextValid;
- }
- [TestCase("", Result = true)]
- [TestCase(".", Result = true)]
- [TestCase("-.", Result = true)]
- [TestCase("-.1", Result = true)]
- [TestCase("+", Result = true)]
- [TestCase("-", Result = true)]
- [TestCase(".0", Result = true)]
- [TestCase("1.0", Result = true)]
- [TestCase("+1.0", Result = true)]
- [TestCase("-1.0", Result = true)]
- [TestCase("001.0", Result = true)]
- [TestCase(" ", Result = false)]
- [TestCase("..", Result = false)]
- [TestCase("..1", Result = false)]
- [TestCase("1+0", Result = false)]
- [TestCase("1.a", Result = false)]
- [TestCase("1..1", Result = false)]
- [TestCase("a11", Result = false)]
- [SetCulture("en-US")]
- public bool TestIsTextValid(string text)
- {
- bool isValid = TextBoxDoubleValidator.IsValid(text);
- Console.WriteLine("'{0}' is {1}", text, isValid ? "valid" : "not valid");
- return isValid;
- }
- /// <summary>
- /// Helper class that validates text box input for double values.
- /// </summary>
- internal static class TextBoxDoubleValidator
- {
- private static readonly ThreadLocal<NumberFormatInfo> _numbersFormat = new ThreadLocal<NumberFormatInfo>(
- () => Thread.CurrentThread.CurrentCulture.NumberFormat);
- /// <summary>
- /// Returns true if input <param name="text"/> is accepted by IsDouble text box.
- /// </summary>
- public static bool IsValid(string text)
- {
- // First corner case: null or empty string is a valid text in our case
- if (text.IsNullOrEmpty())
- return true;
- // '.', '+', '-', '+.' or '-.' - are invalid doubles, but we should accept them
- // because user can continue typeing correct value (like .1, +1, -0.12, +.1, -.2)
- if (text == _numbersFormat.Value.NumberDecimalSeparator ||
- text == _numbersFormat.Value.NegativeSign ||
- text == _numbersFormat.Value.PositiveSign ||
- text == _numbersFormat.Value.NegativeSign + _numbersFormat.Value.NumberDecimalSeparator ||
- text == _numbersFormat.Value.PositiveSign + _numbersFormat.Value.NumberDecimalSeparator)
- return true;
- // Now, lets check, whether text is a valid double
- bool isValidDouble = StringEx.IsDouble(text);
- // If text is a valid double - we're done
- if (isValidDouble)
- return true;
- // Text could be invalid, but we still could accept such input.
- // For example, we should accepted "1.", because after that user will type 1.12
- // But we should not accept "..1"
- int separatorCount = CountOccurances(text, _numbersFormat.Value.NumberDecimalSeparator);
- // If text is not double and we don't have separator in this text
- // or if we have more than one separator in this text, than text is invalid
- if (separatorCount != 1)
- return false;
- // Lets remove first separator from our input text
- string textWithoutNumbersSeparator = RemoveFirstOccurrance(text, _numbersFormat.Value.NumberDecimalSeparator);
- // Second corner case:
- // '.' is also valid text, because .1 is a valid double value and user may try to type this value
- if (textWithoutNumbersSeparator.IsNullOrEmpty())
- return true;
- // Now, textWithoutNumbersSeparator should be valid if text contains only one
- // numberic separator
- bool isModifiedTextValid = StringEx.IsDouble(textWithoutNumbersSeparator);
- return isModifiedTextValid;
- }
- /// <summary>
- /// Returns number of occurances of value in text
- /// </summary>
- private static int CountOccurances(string text, string value)
- {
- string[] subStrings = text.Split(new[] { value }, StringSplitOptions.None);
- return subStrings.Length - 1;
- }
- /// <summary>
- /// Removes first occurance of valud from text.
- /// </summary>
- private static string RemoveFirstOccurrance(string text, string value)
- {
- if (string.IsNullOrEmpty(text))
- return String.Empty;
- if (string.IsNullOrEmpty(value))
- return text;
- int idx = text.IndexOf(value, StringComparison.InvariantCulture);
- if (idx == -1)
- return text;
- return text.Remove(idx, value.Length);
- }
- }
- - - invalid
- -1 - valid
- . - invalid
- .1 - valid
- 1 - valid
- 1e - invalid
- 1e+ - invalid
- 1e+5 - valid