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: }