moel@345: using System; moel@345: using System.ComponentModel; moel@345: using System.Windows.Forms; moel@345: using System.Globalization; moel@345: moel@345: moel@345: namespace Aga.Controls moel@345: { moel@345: /// moel@345: /// Restricts the entry of characters to digits, the negative sign, moel@345: /// the decimal point, and editing keystrokes (backspace). moel@345: /// It does not handle the AltGr key so any keys that can be created in any moel@345: /// combination with AltGr these are not filtered moel@345: /// moel@345: public class NumericTextBox : TextBox moel@345: { moel@345: private const int WM_PASTE = 0x302; moel@345: private NumberStyles numberStyle = NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign; moel@345: moel@345: /// moel@345: /// Restricts the entry of characters to digits, the negative sign, moel@345: /// the decimal point, and editing keystrokes (backspace). moel@345: /// It does not handle the AltGr key moel@345: /// moel@345: /// moel@345: protected override void OnKeyPress(KeyPressEventArgs e) moel@345: { moel@345: base.OnKeyPress(e); moel@345: moel@345: e.Handled = invalidNumeric(e.KeyChar); moel@345: } moel@345: moel@345: moel@345: /// moel@345: /// Main method for verifying allowed keypresses. moel@345: /// This does not catch cut paste copy ... operations. moel@345: /// moel@345: /// moel@345: /// moel@345: private bool invalidNumeric(char key) moel@345: { moel@345: bool handled = false; moel@345: moel@345: NumberFormatInfo numberFormatInfo = CultureInfo.CurrentCulture.NumberFormat; moel@345: string decimalSeparator = numberFormatInfo.NumberDecimalSeparator; moel@345: string negativeSign = numberFormatInfo.NegativeSign; moel@345: moel@345: string keyString = key.ToString(); moel@345: moel@345: if (Char.IsDigit(key)) moel@345: { moel@345: // Digits are OK moel@345: } moel@345: else if (AllowDecimalSeparator && keyString.Equals(decimalSeparator)) moel@345: { moel@345: if (Text.IndexOf(decimalSeparator) >= 0) moel@345: { moel@345: handled = true; moel@345: } moel@345: } moel@345: else if (AllowNegativeSign && keyString.Equals(negativeSign)) moel@345: { moel@345: if (Text.IndexOf(negativeSign) >= 0) moel@345: { moel@345: handled = true; moel@345: } moel@345: } moel@345: else if (key == '\b') moel@345: { moel@345: // Backspace key is OK moel@345: } moel@345: else if ((ModifierKeys & (Keys.Control)) != 0) moel@345: { moel@345: // Let the edit control handle control and alt key combinations moel@345: } moel@345: else moel@345: { moel@345: // Swallow this invalid key and beep moel@345: handled = true; moel@345: } moel@345: return handled; moel@345: } moel@345: moel@345: moel@345: /// moel@345: /// Method invoked when Windows sends a message. moel@345: /// moel@345: /// Message from Windows. moel@345: /// moel@345: /// This is over-ridden so that the user can not use moel@345: /// cut or paste operations to bypass the TextChanging event. moel@345: /// This catches ContextMenu Paste, Shift+Insert, Ctrl+V, moel@345: /// While it is generally frowned upon to override WndProc, no moel@345: /// other simple mechanism was apparent to simultaneously and moel@345: /// transparently intercept so many different operations. moel@345: /// moel@345: protected override void WndProc(ref Message m) moel@345: { moel@345: // Switch to handle message... moel@345: switch (m.Msg) moel@345: { moel@345: case WM_PASTE: moel@345: { moel@345: // Get clipboard object to paste moel@345: IDataObject clipboardData = Clipboard.GetDataObject(); moel@345: moel@345: // Get text from clipboard data moel@345: string pasteText = (string)clipboardData.GetData( moel@345: DataFormats.UnicodeText); moel@345: moel@345: // Get the number of characters to replace moel@345: int selectionLength = SelectionLength; moel@345: moel@345: // If no replacement or insertion, we are done moel@345: if (pasteText.Length == 0) moel@345: { moel@345: break; moel@345: } moel@345: else if (selectionLength != 0) moel@345: { moel@345: base.Text = base.Text.Remove(SelectionStart, selectionLength); moel@345: } moel@345: moel@345: bool containsInvalidChars = false; moel@345: foreach (char c in pasteText) moel@345: { moel@345: if (containsInvalidChars) moel@345: { moel@345: break; moel@345: } moel@345: else if (invalidNumeric(c)) moel@345: { moel@345: containsInvalidChars = true; moel@345: } moel@345: } moel@345: moel@345: if (!containsInvalidChars) moel@345: { moel@345: base.Text = base.Text.Insert(SelectionStart, pasteText); moel@345: } moel@345: moel@345: return; moel@345: } moel@345: moel@345: } moel@345: base.WndProc(ref m); moel@345: } moel@345: moel@345: moel@345: public int IntValue moel@345: { moel@345: get moel@345: { moel@345: int intValue; moel@345: Int32.TryParse(this.Text, numberStyle, CultureInfo.CurrentCulture.NumberFormat, out intValue); moel@345: return intValue; moel@345: } moel@345: } moel@345: moel@345: public decimal DecimalValue moel@345: { moel@345: get moel@345: { moel@345: decimal decimalValue; moel@345: Decimal.TryParse(this.Text, numberStyle, CultureInfo.CurrentCulture.NumberFormat, out decimalValue); moel@345: return decimalValue; moel@345: } moel@345: } moel@345: moel@345: moel@345: private bool allowNegativeSign; moel@345: [DefaultValue(true)] moel@345: public bool AllowNegativeSign moel@345: { moel@345: get { return allowNegativeSign; } moel@345: set { allowNegativeSign = value; } moel@345: } moel@345: moel@345: private bool allowDecimalSeparator; moel@345: [DefaultValue(true)] moel@345: public bool AllowDecimalSeparator moel@345: { moel@345: get { return allowDecimalSeparator; } moel@345: set { allowDecimalSeparator = value; } moel@345: } moel@345: moel@345: } moel@345: moel@345: }