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