Numerische TextBox in Windows Forms

Derzeit habe ich zusammen mit einem Kollegen das Vergnügen, eine „historisch gewachsene“ VB6-Anwendung nach C#/.NET migrieren zu dürfen. „Migrieren“ steht in den Augen des Kunden hierbei vor allem für „Aufräumen und zum Funktionieren bringen und alles so aussehen lassen wie zuvor“.
Da in der Anwendung sehr viel mit Zahlen jongliert wird, haben wir uns zunächst die MaskedTextBox angesehen, aber ziemlich schnell festgestellt, daß diese nur für Zahlen mit statischem Muster nützlich ist: Nehmen wir an, man kann ein- bis dreistellige Zahlen mit vier Nachkommastellen haben. Die dafür notwendige Maske lautet „999.0000“ oder ähnlich. Über ein DataBinding sollte dann die Zahl 3,0303 in dieser TextBox landen. Was hat Windows daraus gemacht? 303,03! Und warum? Weil diese Textboxen offenbar von links ausgefüllt werden. Also für unsere Zwecke nicht zu gebrauchen.
Wir mußten uns also hinsetzen und mit Hilfe von Hirn und Google etwa eigenes bauen. In diesem Artikel möchte ich nun Schritt für Schritt zeigen, wie man eine TextBox bauen kann, die nur numerische Werte (ganze Zahlen aber auch Dezimalzahlen) akzeptiert.

Die erste Konsultation von Google ergab bereits ein funktionierendes Beispiel für eine TextBox, die nur Ziffern akzeptiert. Die Eingabe von Zeichen wird im OnKeyPress behandelt, alles was nicht Ziffer ist, wird dabei ignoriert:

using System;
using System.Windows.Forms;

namespace NumericTextBox
{
    public class NumericTextBox : TextBox
    {
        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar))
            {
                e.Handled = true;
            }
            base.OnKeyPress(e);
        }
    }
}

Was aber, wenn man neben positiven ganzen Zahlen auch negative zulassen möchte? Hierfür muß neben Ziffern auch das Negativsymbol eingegeben werden können. Dies ist zum einen sprachspezifisch, zum anderen darf das Zeichen auch nur ein einziges Mal vorhanden sein und auch nur vor der Zahl stehen und nicht irgendwo mittendrin. Damit man dieses Verhalten von außen steuern kann, bietet sich eine Eigenschaft an, nennen wir sie Signed. Wir erweitern also das obige Beispiel um folgenden Code:

class NumericTextBox : TextBox
{
    private bool _signed;
    private string negativeSign;

    public NumericTextBox() : base()
    {
        this.negativeSign = 
            CultureInfo.CurrentCulture.NumberFormat.NegativeSign;
    }

    public bool Signed
    {
        get { return _signed; }
        set { _signed = value; }
    }

    protected override void OnKeyPress(KeyPressEventArgs e)
    {
        bool nsAllowed = _signed && base.SelectionStart == 0 && 
            !this.Text.Contains(negativeSign);

        if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) && 
            (e.KeyChar.ToString() != negativeSign || !nsAllowed ))
        {
            e.Handled = true;
        }
        base.OnKeyPress(e);
    }
}

Damit ist unsere numerische TextBox für ganze Zahlen bereits fertig.

Im nächsten und letzten Schritt soll unsere TextBox jedoch nicht nur ganze Zahlen zulassen, sondern auch Dezimalzahlen. Für das dafür notwendige Trennzeichen gelten folgende Bedingungen: Es darf nur einmal vorkommen und des darf nicht vor dem Negativzeichen stehen. Steht es direkt dahinter, ist das kein Problem, die Parse-Methoden kommen mit diesem Konstrukt klar. Auch hier verwenden wir wieder eine bool-Eigenschaft, um festzulegen, ob Dezimalzahlen erlaubt sind (true) oder nicht (false):

class NumericTextBox : TextBox
{
    private bool _signed;
    private bool _decimal;
    private string negativeSign;
    private string decimalSeparator;

    public NumericTextBox() : base()
    {
        this.negativeSign = 
            CultureInfo.CurrentCulture.NumberFormat.NegativeSign;
        this.decimalSeparator = 
            CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
    }

    public bool Decimal
    {
        get { return _decimal; }
        set { _decimal = value; }
    }

    public bool Signed
    {
        get { return _signed; }
        set { _signed = value; }
    }

    protected override void OnKeyPress(KeyPressEventArgs e)
    {
        bool nsAllowed = _signed && base.SelectionStart == 0 && 
            !this.Text.Contains(negativeSign);
        bool dAllowed = _decimal && !this.Text.Contains(decimalSeparator) && 
            (this.SelectionStart != 0 || !this.Text.StartsWith(negativeSign));

        if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) && 
            (e.KeyChar.ToString() != negativeSign || !nsAllowed) && 
            (e.KeyChar.ToString() != decimalSeparator || !dAllowed))
        {
            e.Handled = true;
        }
        base.OnKeyPress(e);
    }
}

Damit ist unsere numerische TextBox bereits fertig. Bei der Verwendung im Designer kann das Verhalten über die beiden hinzugefügten Eigenschaften komfortabel eingestellt werden; damit und mit der einstellbaren maximalen Eingabelänge kann man fast gefahrlos die Eingabe in eine Zahl konvertieren.

2 Kommentare zu “Numerische TextBox in Windows Forms

  1. Guten Abend,

    Ich habe die oberste Version dieser numerischen Textbox in einem Programm zur Berechnung von Primzahlen in einem bestimmten Bereich verwendet, da ich nur postitive ganze Zahlen brauche. Dabei bin ich auf ein Problem, das mich echt frustriert hat: Es war unmölich die Zeichenfolge der Textbox in eine Integer Zahl umzuwandeln. Dafür konnte ich keinen ersichtlichen Grund finden und es endete dann so, dass ich auf eine Standardtextbox gewechselt bin und eine Fehlermeldung ausgebe wenn die Eingabe fehlerhaft ist. Hat mir also nicht geholfen.

  2. Das ist interessant, wir haben diese Box erfolgreich eingesetzt und hatten beim Parsen keine Probleme. Ich werde das bei Gelegenheit aber noch einmal überprüfen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

*