Don't like ads? PRO users don't see any ads ;-)
Guest

Untitled

By: a guest on May 2nd, 2012  |  syntax: None  |  size: 7.02 KB  |  hits: 33  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. Numeric TextBox - using Double.TryParse
  2. private int oldIndex = 0;
  3. private string oldText = String.Empty;
  4.  
  5. private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
  6. {
  7.     double val;
  8.     if (!Double.TryParse(textBox1.Text, out val))
  9.     {
  10.         textBox1.TextChanged -= textBox1_TextChanged;
  11.         textBox1.Text = oldText;
  12.         textBox1.CaretIndex = oldIndex;
  13.         textBox1.TextChanged += textBox1_TextChanged;
  14.     }
  15. }
  16.  
  17. private void textBox1_KeyDown(object sender, KeyEventArgs e)
  18. {
  19.     oldIndex = textBox1.CaretIndex;
  20.     oldText = textBox1.Text;
  21. }
  22.        
  23. private void textBox1_PreviewKeyDown(object sender, KeyEventArgs e)
  24. {
  25.     if (e.Key == Key.Space)
  26.     {
  27.         e.Handled = true;
  28.     }
  29. }
  30.  
  31. private void textBox1_PreviewTextInput(object sender, TextCompositionEventArgs e)
  32. {
  33.     //Create a string combining the text to be entered with what is already there.
  34.     //Being careful of new text positioning here, though it isn't truly necessary for validation of number format.
  35.     int cursorPos = textBox1.CaretIndex;
  36.     string nextText;
  37.     if (cursorPos > 0)
  38.     {
  39.         nextText = textBox1.Text.Substring(0, cursorPos) + e.Text + textBox1.Text.Substring(cursorPos);
  40.     }
  41.     else
  42.     {
  43.         nextText = textBox1.Text + e.Text;
  44.     }
  45.     double testVal;
  46.     if (!Double.TryParse(nextText, out testVal))
  47.     {
  48.         e.Handled = true;
  49.     }
  50. }
  51.        
  52. private void OnPaste(object sender, DataObjectPastingEventArgs e)
  53. {
  54.     double testVal;
  55.     bool ok = false;
  56.  
  57.     var isText = e.SourceDataObject.GetDataPresent(System.Windows.DataFormats.Text, true);
  58.     if (isText)
  59.     {
  60.         var text = e.SourceDataObject.GetData(DataFormats.Text) as string;
  61.         if (Double.TryParse(text, out testVal))
  62.         {
  63.             ok = true;
  64.         }
  65.     }
  66.  
  67.     if (!ok)
  68.     {
  69.         e.CancelCommand();
  70.     }
  71. }
  72.        
  73. DataObject.AddPastingHandler(textBox1, new DataObjectPastingEventHandler(OnPaste));
  74.        
  75. private static void PreviewTextInputForDouble(object sender,
  76.     TextCompositionEventArgs e)
  77. {
  78.     // e.Text contains only new text and we should create full text manually
  79.  
  80.     var textBox = (TextBox)sender;
  81.     string fullText;
  82.  
  83.     // If text box contains selected text we should replace it with e.Text
  84.     if (textBox.SelectionLength > 0)
  85.     {
  86.         fullText = textBox.Text.Replace(textBox.SelectedText, e.Text);
  87.     }
  88.     else
  89.     {
  90.         // And only otherwise we should insert e.Text at caret position
  91.         fullText = textBox.Text.Insert(textBox.CaretIndex, e.Text);
  92.     }
  93.  
  94.     // Now we should validate our fullText, but not with
  95.     // Double.TryParse. We should use more complicated validation logic.
  96.     bool isTextValid = TextBoxDoubleValidator.IsValid(fullText);
  97.  
  98.     // Interrupting this event if fullText is invalid
  99.     e.Handled = !isTextValid;
  100. }
  101.        
  102. [TestCase("", Result = true)]
  103. [TestCase(".", Result = true)]
  104. [TestCase("-.", Result = true)]
  105. [TestCase("-.1", Result = true)]
  106. [TestCase("+", Result = true)]
  107. [TestCase("-", Result = true)]
  108. [TestCase(".0", Result = true)]
  109. [TestCase("1.0", Result = true)]
  110. [TestCase("+1.0", Result = true)]
  111. [TestCase("-1.0", Result = true)]
  112. [TestCase("001.0", Result = true)]
  113. [TestCase(" ", Result = false)]
  114. [TestCase("..", Result = false)]
  115. [TestCase("..1", Result = false)]
  116. [TestCase("1+0", Result = false)]
  117. [TestCase("1.a", Result = false)]
  118. [TestCase("1..1", Result = false)]
  119. [TestCase("a11", Result = false)]
  120. [SetCulture("en-US")]
  121. public bool TestIsTextValid(string text)
  122. {
  123.     bool isValid = TextBoxDoubleValidator.IsValid(text);
  124.     Console.WriteLine("'{0}' is {1}", text, isValid ? "valid" : "not valid");
  125.     return isValid;
  126. }
  127.        
  128. /// <summary>
  129. /// Helper class that validates text box input for double values.
  130. /// </summary>
  131. internal static class TextBoxDoubleValidator
  132. {
  133.     private static readonly ThreadLocal<NumberFormatInfo> _numbersFormat = new ThreadLocal<NumberFormatInfo>(
  134.         () => Thread.CurrentThread.CurrentCulture.NumberFormat);
  135.  
  136.     /// <summary>
  137.     /// Returns true if input <param name="text"/> is accepted by IsDouble text box.
  138.     /// </summary>
  139.     public static bool IsValid(string text)
  140.     {
  141.         // First corner case: null or empty string is a valid text in our case
  142.         if (text.IsNullOrEmpty())
  143.             return true;
  144.  
  145.         // '.', '+', '-', '+.' or '-.' - are invalid doubles, but we should accept them
  146.         // because user can continue typeing correct value (like .1, +1, -0.12, +.1, -.2)
  147.         if (text == _numbersFormat.Value.NumberDecimalSeparator ||
  148.             text == _numbersFormat.Value.NegativeSign ||
  149.             text == _numbersFormat.Value.PositiveSign ||
  150.             text == _numbersFormat.Value.NegativeSign + _numbersFormat.Value.NumberDecimalSeparator ||
  151.             text == _numbersFormat.Value.PositiveSign + _numbersFormat.Value.NumberDecimalSeparator)
  152.             return true;
  153.  
  154.         // Now, lets check, whether text is a valid double
  155.         bool isValidDouble = StringEx.IsDouble(text);
  156.  
  157.         // If text is a valid double - we're done
  158.         if (isValidDouble)
  159.             return true;
  160.  
  161.         // Text could be invalid, but we still could accept such input.
  162.         // For example, we should accepted "1.", because after that user will type 1.12
  163.         // But we should not accept "..1"
  164.         int separatorCount = CountOccurances(text, _numbersFormat.Value.NumberDecimalSeparator);
  165.  
  166.         // If text is not double and we don't have separator in this text
  167.         // or if we have more than one separator in this text, than text is invalid
  168.         if (separatorCount != 1)
  169.             return false;
  170.  
  171.         // Lets remove first separator from our input text
  172.         string textWithoutNumbersSeparator = RemoveFirstOccurrance(text, _numbersFormat.Value.NumberDecimalSeparator);
  173.  
  174.         // Second corner case:
  175.         // '.' is also valid text, because .1 is a valid double value and user may try to type this value
  176.         if (textWithoutNumbersSeparator.IsNullOrEmpty())
  177.             return true;
  178.  
  179.         // Now, textWithoutNumbersSeparator should be valid if text contains only one
  180.         // numberic separator
  181.         bool isModifiedTextValid = StringEx.IsDouble(textWithoutNumbersSeparator);
  182.         return isModifiedTextValid;
  183.     }
  184.  
  185.     /// <summary>
  186.     /// Returns number of occurances of value in text
  187.     /// </summary>
  188.     private static int CountOccurances(string text, string value)
  189.     {
  190.         string[] subStrings = text.Split(new[] { value }, StringSplitOptions.None);
  191.         return subStrings.Length - 1;
  192.  
  193.     }
  194.  
  195.     /// <summary>
  196.     /// Removes first occurance of valud from text.
  197.     /// </summary>
  198.     private static string RemoveFirstOccurrance(string text, string value)
  199.     {
  200.         if (string.IsNullOrEmpty(text))
  201.             return String.Empty;
  202.         if (string.IsNullOrEmpty(value))
  203.             return text;
  204.  
  205.         int idx = text.IndexOf(value, StringComparison.InvariantCulture);
  206.         if (idx == -1)
  207.             return text;
  208.         return text.Remove(idx, value.Length);
  209.     }
  210.  
  211. }
  212.        
  213. -       - invalid
  214. -1      - valid
  215.  
  216. .       - invalid
  217. .1      - valid
  218.  
  219. 1       - valid
  220. 1e      - invalid
  221. 1e+     - invalid
  222. 1e+5    - valid