using System; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Windows.Forms; namespace Developpez.Dotnet.Windows.Forms { /// /// Un contrôle TextBox qui n'autorise que les nombres /// public class NumericTextBox : TextBox { #region Données privées private const int WM_KEYDOWN = 0x100; private const int WM_KEYUP = 0x101; private const int WM_CHAR = 0x102; private const int WM_PASTE = 0x302; private bool _allowDecimal = true; private bool _allowNegative = true; private bool _allowEmpty = true; private Keys lastKeyDown = Keys.None; #endregion #region Propriétés publiques /// /// Indique si les nombres décimaux sont autorisés /// [DefaultValue(true)] public bool AllowDecimal { get { return _allowDecimal; } set { _allowDecimal = value; FixCurrentText(); } } /// /// Indique si les nombres négatifs sont autorisés /// [DefaultValue(true)] public bool AllowNegative { get { return _allowNegative; } set { _allowNegative = value; FixCurrentText(); } } /// /// Indique si une saisie vide est valide /// [DefaultValue(true)] public bool AllowEmpty { get { return _allowEmpty; } set { _allowEmpty = value; FixCurrentText(); } } #endregion #region Méthodes publiques /// /// Tente d'obtenir la valeur du nombre saisi en tant qu'entier signé sur 8 bits /// /// Paramètre de sortie où est placé le résultat /// true si le nombre saisi est valide. Sinon, false. public bool TryGetValue(out sbyte value) { if (_allowEmpty && this.TextLength == 0) { value = 0; return true; } return sbyte.TryParse(this.Text, out value); } /// /// Tente d'obtenir la valeur du nombre saisi en tant qu'entier signé sur 16 bits /// /// Paramètre de sortie où est placé le résultat /// true si le nombre saisi est valide. Sinon, false. public bool TryGetValue(out short value) { if (_allowEmpty && this.TextLength == 0) { value = 0; return true; } return short.TryParse(this.Text, out value); } /// /// Tente d'obtenir la valeur du nombre saisi en tant qu'entier signé sur 32 bits /// /// Paramètre de sortie où est placé le résultat /// true si le nombre saisi est valide. Sinon, false. public bool TryGetValue(out int value) { if (_allowEmpty && this.TextLength == 0) { value = 0; return true; } return int.TryParse(this.Text, out value); } /// /// Tente d'obtenir la valeur du nombre saisi en tant qu'entier signé sur 64 bits /// /// Paramètre de sortie où est placé le résultat /// true si le nombre saisi est valide. Sinon, false. public bool TryGetValue(out long value) { if (_allowEmpty && this.TextLength == 0) { value = 0; return true; } return long.TryParse(this.Text, out value); } /// /// Tente d'obtenir la valeur du nombre saisi en tant qu'entier non signé sur 8 bits /// /// Paramètre de sortie où est placé le résultat /// true si le nombre saisi est valide. Sinon, false. public bool TryGetValue(out byte value) { if (_allowEmpty && this.TextLength == 0) { value = 0; return true; } return byte.TryParse(this.Text, out value); } /// /// Tente d'obtenir la valeur du nombre saisi en tant qu'entier non signé sur 16 bits /// /// Paramètre de sortie où est placé le résultat /// true si le nombre saisi est valide. Sinon, false. public bool TryGetValue(out ushort value) { if (_allowEmpty && this.TextLength == 0) { value = 0; return true; } return ushort.TryParse(this.Text, out value); } /// /// Tente d'obtenir la valeur du nombre saisi en tant qu'entier non signé sur 32 bits /// /// Paramètre de sortie où est placé le résultat /// true si le nombre saisi est valide. Sinon, false. public bool TryGetValue(out uint value) { if (_allowEmpty && this.TextLength == 0) { value = 0; return true; } return uint.TryParse(this.Text, out value); } /// /// Tente d'obtenir la valeur du nombre saisi en tant qu'entier non signé sur 64 bits /// /// Paramètre de sortie où est placé le résultat /// true si le nombre saisi est valide. Sinon, false. public bool TryGetValue(out ulong value) { if (_allowEmpty && this.TextLength == 0) { value = 0; return true; } return ulong.TryParse(this.Text, out value); } /// /// Tente d'obtenir la valeur du nombre saisi en tant que nombre à virgule flottante simple précision /// /// Paramètre de sortie où est placé le résultat /// true si le nombre saisi est valide. Sinon, false. public bool TryGetValue(out float value) { if (_allowEmpty && this.TextLength == 0) { value = 0; return true; } return float.TryParse(this.Text, out value); } /// /// Tente d'obtenir la valeur du nombre saisi en tant que nombre à virgule flottante double précision /// /// Paramètre de sortie où est placé le résultat /// true si le nombre saisi est valide. Sinon, false. public bool TryGetValue(out double value) { if (_allowEmpty && this.TextLength == 0) { value = 0; return true; } return double.TryParse(this.Text, out value); } /// /// Tente d'obtenir la valeur du nombre saisi en tant que nombre décimal /// /// Paramètre de sortie où est placé le résultat /// true si le nombre saisi est valide. Sinon, false. public bool TryGetValue(out decimal value) { if (_allowEmpty && this.TextLength == 0) { value = 0; return true; } return decimal.TryParse(this.Text, out value); } #endregion #region Méthodes privées private void FixCurrentText() { string text = StripNonNumerics(this.Text); if (!_allowEmpty && text.IsNullOrEmpty()) text = "0"; if (text != this.Text) this.Text = text; } private void PasteClipboard() { if (Clipboard.ContainsText()) { string text = Clipboard.GetText(); if (text != null) { text = StripNonNumerics(text); } this.SelectedText = text; } } private string StripNonNumerics(string text) { string decimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; string groupSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator; string s = text; if (decimalSeparator != "." && groupSeparator != ".") s = s.Replace(".", decimalSeparator); if (!_allowDecimal) { int index = s.IndexOf(decimalSeparator); if (index < 0) index = s.IndexOf('.'); if (index >= 0) s = s.Substring(0, index); } var validChars = from c in s where IsNumericChar(c) select c; s = new string(validChars.ToArray()); return s; } private bool IsNumericChar(char c) { if (Char.IsDigit(c)) return true; if (_allowDecimal && c == CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator[0]) return true; if (_allowNegative && c == CultureInfo.CurrentCulture.NumberFormat.NegativeSign[0]) return true; return false; } #endregion #region Overrides de Control /// /// Traite les messages Windows. /// /// Message à traiter protected override void WndProc(ref Message m) { if (m.Msg == WM_PASTE) { PasteClipboard(); } else { base.WndProc(ref m); } } /// /// Déclenche l'événement KeyPress. /// /// Paramètres de l'évènement protected override void OnKeyPress(KeyPressEventArgs e) { char c = e.KeyChar; if (!IsNumericChar(c) && !char.IsControl(c)) { e.Handled = true; } base.OnKeyPress(e); } /// /// Traite un message de touche et génère les événements de contrôle appropriés. Remplace /// un point ou une virgule par le séparateur décimal de la culture courante /// /// Message à traiter /// true si le message a été traité par le contrôle ; sinon, false. protected override bool ProcessKeyEventArgs(ref Message m) { if (m.Msg == WM_KEYDOWN) { lastKeyDown = (Keys)((int)((long)m.WParam)); } else if (m.Msg == WM_CHAR) { if (lastKeyDown == Keys.Decimal && (char)m.WParam == '.') m.WParam = (IntPtr)(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator[0]); } return base.ProcessKeyEventArgs(ref m); } /// /// Déclenche l'évènement Validating /// /// Paramètres de l'évènement protected override void OnValidating(CancelEventArgs e) { FixCurrentText(); base.OnValidating(e); } #endregion } }