private static void HandleInputMaskDecimalKeyDown(Key key, TextBox textbox, string mask) { var startPosition = textbox.SelectionStart; var isCurrency = mask == "$"; var isDecimal = mask == "%"; var numberFormat = CultureInfo.CurrentUICulture.NumberFormat; var decimalSeparator = isCurrency ? numberFormat.CurrencyDecimalSeparator : numberFormat.NumberDecimalSeparator; var lastInsertWasDecimalSeparator = false; var valueIsNegative = false; var valueWasNegativeOnEnter = false; // Need to memorize how many digits there were to the left before this keystroke var allChars = textbox.Text.ToCharArray().ToList(); var numberOfDigitsToTheLeft = 0; for (var charCounter = 0; charCounter < startPosition; charCounter++) { if (char.IsDigit(allChars[charCounter]) || allChars[charCounter] == '-') { numberOfDigitsToTheLeft++; } } // We check if the value is negative if (allChars.Count > 0 && allChars[0] == '-') { valueIsNegative = true; valueWasNegativeOnEnter = true; } // Special custom decimal handling var currencySymbol = GetInputMaskCurrencySymbol(textbox); if (string.IsNullOrEmpty(currencySymbol)) { currencySymbol = numberFormat.CurrencySymbol; SetInputMaskCurrencySymbol(textbox, currencySymbol); // So we have it for later } var newChar = KeyboardHelper.GetCharFromKey(key); var sb = new StringBuilder(textbox.Text); if (key == Key.Delete) { if (textbox.SelectionLength > 0) { sb.Remove(startPosition, textbox.SelectionLength); for (var counter = 0; counter < textbox.SelectionLength; counter++) { allChars.RemoveAt(startPosition); } } else { if (startPosition < sb.Length) { sb.Remove(startPosition, 1); allChars.RemoveAt(startPosition); } } if (allChars.Count == 0) { sb.Append("0"); } } else if (key == Key.Back) { if (textbox.SelectionLength > 0) { sb.Remove(startPosition, textbox.SelectionLength); for (var counter = 0; counter < textbox.SelectionLength; counter++) { allChars.RemoveAt(startPosition); } } else if (startPosition > 0) { if (startPosition - 1 < sb.Length) { sb.Remove(startPosition - 1, 1); allChars.RemoveAt(startPosition - 1); numberOfDigitsToTheLeft--; } } if (allChars.Count == 0) { sb.Append("0"); } } else { // Need to handle selected text properly by replacing it with the new input if (textbox.SelectionLength > 0 && !(newChar == '-' || newChar == '+')) { sb.Remove(startPosition, textbox.SelectionLength); for (var counter = 0; counter < textbox.SelectionLength; counter++) { allChars.RemoveAt(startPosition); } if (allChars.Count == 0 && valueWasNegativeOnEnter) { valueWasNegativeOnEnter = false; valueIsNegative = false; } } if (char.IsDigit(newChar)) { sb.Insert(startPosition, newChar); numberOfDigitsToTheLeft++; } else if (GetInputMaskSupportsNegative(textbox) && newChar == '-') { // We need to make sure that the value is negative valueIsNegative = true; if (!valueWasNegativeOnEnter) { numberOfDigitsToTheLeft++; } } else if (GetInputMaskSupportsNegative(textbox) && newChar == '+') { // We need to make sure that the value is positive valueIsNegative = false; if (valueWasNegativeOnEnter) { numberOfDigitsToTheLeft--; if (numberOfDigitsToTheLeft < 0) { numberOfDigitsToTheLeft = 0; } } } else if (newChar.ToString() == decimalSeparator) { // If there is a decimal indicator to the left, we ignore the input for (var counter = 0; counter < startPosition; counter++) { if (allChars[counter].ToString() == decimalSeparator) { SystemSounds.Beep.Play(); return; } } // If there already is another decimal in the string (to the right), we need to get rid of it var existingDecimalPosition = -1; for (var counter = startPosition; counter < allChars.Count; counter++) { if (allChars[counter].ToString() == decimalSeparator) { existingDecimalPosition = counter; } } if (existingDecimalPosition > -1) { sb.Remove(startPosition, existingDecimalPosition - startPosition + 1); } sb.Insert(startPosition, newChar); lastInsertWasDecimalSeparator = true; } else { SystemSounds.Beep.Play(); } } var newText = sb.ToString(); if (isCurrency && newText.StartsWith(currencySymbol)) { newText = newText.Substring(currencySymbol.Length).Trim(); } if (valueIsNegative && !newText.StartsWith("-")) { newText = "-" + newText; } else if (!valueIsNegative && newText.StartsWith("-")) { newText = newText.Substring(1); } if (isDecimal && newText.EndsWith("%")) { newText = newText.Substring(0, newText.Length - 1); } decimal parsedValue; if (decimal.TryParse(newText, out parsedValue)) { var rightFormat = isCurrency ? StringHelper.Replicate("0", numberFormat.CurrencyDecimalDigits) : GetInputMaskDecimalFractionalMask(textbox); newText = parsedValue.ToString(GetInputMaskDecimalCharacteristicMask(textbox) + "." + rightFormat); if (isCurrency) { newText = currencySymbol + " " + newText; } if (isDecimal) { newText += "%"; } textbox.SetValue(InputMaskIsValidatingProperty, true); textbox.Text = newText; SetTextUnmasked(textbox, parsedValue.ToString("###########0." + rightFormat)); textbox.SetValue(InputMaskIsValidatingProperty, false); var newCaretIndex = 0; var digitsFound = 0; var newAllChars = newText.ToCharArray(); foreach (var newAllChar in newAllChars) { if (digitsFound >= numberOfDigitsToTheLeft) { break; } if (isCurrency) { if (newCaretIndex > currencySymbol.Length && char.IsDigit(newAllChar)) { digitsFound++; } } else if (char.IsDigit(newAllChar) || newAllChar == '-') { digitsFound++; } newCaretIndex++; } if (lastInsertWasDecimalSeparator) { newCaretIndex++; } textbox.CaretIndex = newCaretIndex; } else { SystemSounds.Beep.Play(); } }
private static void HandleInputMaskKeyDown(object sender, KeyEventArgs e) { var textbox = sender as TextBox; if (textbox == null) { return; } var mask = GetInputMask(textbox); var provider = GetMaskedTextProvider(textbox, mask); var startPosition = textbox.SelectionStart; var startSelectionLength = textbox.SelectionLength; var ignoreKeys = new List <Key> { Key.LeftShift, Key.RightShift, Key.Tab, Key.Capital, Key.CapsLock, Key.Left, Key.Right, Key.Home, Key.End }; if (ignoreKeys.Contains(e.Key)) { return; } if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) { return; } if (e.Key == Key.Delete) { if (startSelectionLength > 0) { provider.RemoveAt(startPosition, startPosition + startSelectionLength); } else { provider.RemoveAt(startPosition); } } else if (e.Key == Key.Back) { if (startPosition > 0) { if (startSelectionLength > 0) { provider.RemoveAt(startPosition, startPosition + startSelectionLength); } else { provider.RemoveAt(startPosition - 1); } } } else { var newChar = KeyboardHelper.GetCharFromKey(e.Key); // TOOO: Want to handle special scenarios when the user hits .,/ or something similar, and those are indeed in the input mask. This will make it easier to enter numeric values and dates in natural fashion. var specialChars = new List <char> { '.', ',', '-', '/', '\\', '(', ')' }; if (specialChars.Contains(newChar) && textbox.CaretIndex < mask.Length) { // We found a special character that may require special handling. var remainingMask = mask.Substring(textbox.CaretIndex); var shiftDistance = remainingMask.IndexOf(newChar); if (shiftDistance > -1) { // We have discovered a keystroke with a special meaning, so we simply move the cursor to that position and carry on if (newChar == '.' || newChar == ',') { // We handle this as a numeric entry, which means we have to clear out all the characters between the current location and the special char provider.RemoveAt(startPosition, startPosition + shiftDistance); provider.InsertAt(" ".Replicate(shiftDistance), 0); textbox.SetValue(InputMaskIsValidatingProperty, true); textbox.Text = provider.ToDisplayString(); textbox.SetValue(InputMaskIsValidatingProperty, false); } textbox.CaretIndex += shiftDistance + 1; e.Handled = true; return; } } if (startSelectionLength > 0) // Looks like we are in replace mode { provider.RemoveAt(startPosition, startPosition + startSelectionLength); } provider.InsertAt(newChar, startPosition); textbox.SetValue(InputMaskIsValidatingProperty, true); textbox.Text = provider.ToDisplayString(); textbox.SetValue(InputMaskIsValidatingProperty, false); var newCaretPosition = provider.FindAssignedEditPositionFrom(startPosition, true); newCaretPosition++; if (newCaretPosition > textbox.Text.Length - 1) { newCaretPosition = textbox.Text.Length - 1; } if (newCaretPosition < 0) { newCaretPosition = textbox.CaretIndex > 0 ? textbox.Text.Length : 0; } textbox.CaretIndex = newCaretPosition; e.Handled = true; } }
private static void HandleInputMaskKeyDown(object sender, KeyEventArgs e) { if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) { return; } if (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)) { return; } var ignoreKeys = new List <Key> { Key.LeftShift, Key.RightShift, Key.Tab, Key.Capital, Key.CapsLock, Key.Left, Key.Right, Key.Home, Key.End, Key.LeftAlt, Key.RightAlt, Key.LeftCtrl, Key.RightCtrl }; if (ignoreKeys.Contains(e.Key)) { return; } var textbox = sender as TextBox; if (textbox == null) { return; } var mask = GetInputMask(textbox); if (mask == "d" || mask == "$" || mask == "%") { HandleInputMaskDecimalKeyDown(e.Key, textbox, mask); e.Handled = true; } else { var startPosition = textbox.SelectionStart; // We are using the default .NET MaskedTextProvider to provide this functionality var provider = GetMaskedTextProvider(textbox, mask); // Need to handle selected text properly by replacing it with the new input if (e.Key == Key.Delete) { if (textbox.SelectionLength > 0) { provider.RemoveAt(startPosition, startPosition + textbox.SelectionLength - 1); } else { provider.RemoveAt(startPosition); } } else if (e.Key == Key.Back) { if (startPosition > 0 || textbox.SelectionLength > 0) { if (textbox.SelectionLength > 0) { provider.RemoveAt(startPosition, startPosition + textbox.SelectionLength - 1); } else { provider.RemoveAt(startPosition - 1); } } } else { if (textbox.SelectionLength > 0) { provider.RemoveAt(startPosition, startPosition + textbox.SelectionLength - 1); } var newChar = KeyboardHelper.GetCharFromKey(e.Key); if (provider.MaskFull) { provider.RemoveAt(provider.Length - 1); } provider.InsertAt(newChar, startPosition); textbox.SetValue(InputMaskIsValidatingProperty, true); textbox.Text = provider.ToDisplayString(); textbox.SetValue(InputMaskIsValidatingProperty, false); var newCarotPosition = provider.FindAssignedEditPositionFrom(startPosition, true); newCarotPosition++; if (newCarotPosition > textbox.Text.Length - 1) { newCarotPosition = textbox.Text.Length - 1; } if (newCarotPosition < 0) { newCarotPosition = textbox.CaretIndex > 0 ? textbox.Text.Length : 0; } textbox.CaretIndex = newCarotPosition; e.Handled = true; } textbox.SetValue(InputMaskIsValidatingProperty, true); SetTextUnmasked(textbox, provider.ToString(false, false)); textbox.SetValue(InputMaskIsValidatingProperty, false); } }