/// <summary> /// Applies format and range rules as the text changes. /// </summary> private void OnTextChanged(object sender, TextChangedEventArgs args) { // Save selection position relative to right edge var originalTextLength = _originalText.Length; var selectionOffset = originalTextLength - _originalSelectionStart; // Call before event handler var change = new DynamicTextChangedEventArgs(Text, _originalText); OnBeforeTextChanged(sender, change); // Adjust text value when changed by event handler if (Text != change.Text) { Text = _originalText = change.Text; } _originalText = Text; // Restore selection var textLength = change.Text.Length; var selectionStart = textLength - selectionOffset; if (selectionStart < 0) { selectionStart = 0; } else if (selectionStart > textLength) { selectionStart = textLength; } if (SelectionStart != selectionStart) { SelectionStart = selectionStart; } _originalSelectionStart = selectionStart; // Update cursor LayoutCursor(); // Call after event handlers OnAfterTextChanged(sender, change); }
/// <summary> /// Called before the base text box changed event is processed but this class, but after the core text box has /// already processed a change (because it provides no text change handler itself to override). /// This class performs trimming or padding here, so must be called when overridden to retain this functionality. /// </summary> protected virtual void OnBeforeTextChanged(object sender, DynamicTextChangedEventArgs args) { // Trim text when too long var text = args.Text; var maxLength = MaxLength; if (maxLength > 0 && text.Length > maxLength) { // Trim text text = text.Substring(0, maxLength); } // Pad text when enabled and too short var minLength = MinLength; if (minLength > 0 && text.Length < MinLength && PadChar.HasValue) { text = text.PadLeft(minLength, PadChar.Value); } // Return result args.Text = text; }
/// <summary> /// Strips non-numeric characters according to <see cref="NumberBase"/> before processing text changes. /// </summary> protected override void OnBeforeTextChanged(object sender, DynamicTextChangedEventArgs args) { // Apply decimal sign rules var originalText = args.OriginalText; var textValue = args.Text; bool?sign = null; if (NumberBase == NumericTextBoxNumberBase.Decimal && NumberSigned) { // Detect sign if (textValue.StartsWith("+", StringComparison.OrdinalIgnoreCase)) { sign = true; } else if (textValue.StartsWith("-", StringComparison.OrdinalIgnoreCase)) { sign = false; } // Apply sign rules if (sign.HasValue) { // Remove sign when only remaining character if (textValue.Length == 1) { sign = null; } // Prevent selection of sign itself else if (SelectionStart == 0) { SelectionStart = 1; } } } // Strip invalid characters var validChars = GetValidNumberChars((int)NumberBase); var text = ""; foreach (var textChar in args.Text) { // Allow sign as first char if (sign.HasValue && text.Length == 0 && (textChar == '+' || textChar == '-')) { text += textChar; } else { // Allow only valid number chars for rest of string if (validChars.IndexOf(textChar.ToString(), StringComparison.OrdinalIgnoreCase) >= 0) { // Add valid char with uppercase correction if necessary if (Char.IsLetter(textChar) && !Char.IsUpper(textChar)) { // Correct lower to upper case text += Char.ToUpperInvariant(textChar); } else { // No change necessary text += textChar; } } else { // Skip or pad invalid char if (PadChar.HasValue) { text += PadChar.Value; } } } } // Validate number when set (format and sign, minimum and maximum) if (!String.IsNullOrEmpty(text)) { var numberBase = (int)NumberBase; if (!Number.TryParse(text, numberBase, out Number value) || value < NumberMin || value > NumberMax) { // Discard changes when invalid text = originalText; } } // Call base class to trim or pad remaining text args.Text = text; base.OnBeforeTextChanged(sender, args); }
/// <summary> /// Called after the base text box changed event has been processed. /// </summary> protected virtual void OnAfterTextChanged(object sender, DynamicTextChangedEventArgs args) { }