Zahleneingaben in Textboxen auf Gültigkeit prüfen und ggf. Meldung ausgeben

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 18 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Zahleneingaben in Textboxen auf Gültigkeit prüfen und ggf. Meldung ausgeben

    Hallo liebe Programmiererkollegen,

    ich habe bisher bei den meisten Textboxen, wo ein Anwender Zahlen eingeben kann, diese manuell auf Plausibilität geprüft.
    Bei so mancher Anwendung mit hunderten Eingeabefeldern wird das aber etwas unüberschaubar und Codelastig.
    Daher habe ich mir eine Function geschrieben die das für mich mit nur einer Codezeile erledigt.
    Den Code möchte ich der Community gerne zur Verfügung stellen.

    Man braucht eine Textbox. In meinem Beispiel Textbox_Preiseingabe.
    Beispiel: Der Anwender darf in diese Textbox nur einen Wert zwischen 1 und 1000 eingeben. Gibt der Anwender eine niedrigere Zahl ein, dann wird zumindest 1 übergeben, gibt der Anwender eine höhere Zahl als 1000 ein, dann wird 1000 übergeben. Die Eingabe wird auch auf Kommas, Punkte etc. überprüft um z.B. die versehentliche Eingabe von zwei Punkten oder zwei Kommas abzufangen. Wenn der Anwender einen ungültigen Wert eingibt, dann erscheint kurz im Textfeld der Hinweis über minimum und maximum Wert in Rot.

    VB.NET-Quellcode

    1. Dim GueltigeZahl as Double = PruefeZahl_CDBL(TextBox_Preiseingabe, 1, 1000)



    Hier der Code der Funktion:

    VB.NET-Quellcode

    1. ''' <summary>
    2. ''' Gibt aus einer Textbox immer einen gültigen Double-Wert zwischen minimum und maximum zurück, egal was der User eingegeben hat
    3. ''' </summary>
    4. ''' <param name="TextboxControl"></param>
    5. ''' <param name="minimum"></param>
    6. ''' <param name="maximum"></param>
    7. ''' <returns></returns>
    8. Public Function PruefeZahl_CDBL(TextboxControl As TextBox, minimum As Double, maximum As Double) As Double
    9. Dim Inhalt As String = TextboxControl.Text
    10. Dim Nachricht As String = ""
    11. Dim AnzahlZeichen As Integer
    12. If Trim(Inhalt) = "" Then Inhalt = minimum.ToString : Nachricht = "leer" : GoTo MeldenUndZurueckgeben 'Prüfen ob der Inhalt leer ist
    13. If Not IsNumeric(Inhalt) Then Inhalt = minimum.ToString : Nachricht = "leer" : GoTo MeldenUndZurueckgeben 'Prüfen ob der Inhalt numerisch ist
    14. 'Prüfen ob zwei Punkte enthalten sind, dann ungültig
    15. AnzahlZeichen = 0
    16. For i As Integer = 1 To Len(Inhalt)
    17. If Mid(Inhalt, i, 1) = "." Then AnzahlZeichen += 1
    18. Next i
    19. If AnzahlZeichen > 1 Then Inhalt = minimum.ToString : Nachricht = "falsch" : GoTo MeldenUndZurueckgeben
    20. 'Prüfen ob zwei Kommas enthalten sind, dann ungültig
    21. AnzahlZeichen = 0
    22. For i As Integer = 1 To Len(Inhalt)
    23. If Mid(Inhalt, i, 1) = "," Then AnzahlZeichen += 1
    24. Next i
    25. If AnzahlZeichen > 1 Then Inhalt = minimum.ToString : Nachricht = "falsch" : GoTo MeldenUndZurueckgeben
    26. If InStr(Inhalt, ",") = 0 Then Inhalt = Replace(Inhalt, ".", ",") 'Einen Punkt durch ein Komma ersetzen, wenn kein zusätzliches Komma existiert
    27. If CDbl(Inhalt) < minimum Then Inhalt = minimum.ToString : Nachricht = minimum.ToString + " - " + maximum.ToString : GoTo MeldenUndZurueckgeben 'Prüfen ob Minimum unterschritten wurde
    28. If CDbl(Inhalt) > maximum Then Inhalt = maximum.ToString : Nachricht = minimum.ToString + " - " + maximum.ToString : GoTo MeldenUndZurueckgeben 'Prüfen ob Maximum überschritten wurde
    29. MeldenUndZurueckgeben:
    30. If Nachricht <> "" Then
    31. Dim BackColorAlt As Color = TextboxControl.BackColor
    32. Dim ForeColorAlt As Color = TextboxControl.ForeColor
    33. TextboxControl.BackColor = Color.Red
    34. TextboxControl.ForeColor = Color.White
    35. TextboxControl.Text = Nachricht
    36. Application.DoEvents()
    37. Warte(0.5)
    38. TextboxControl.BackColor = BackColorAlt
    39. TextboxControl.ForeColor = ForeColorAlt
    40. End If
    41. TextboxControl.Text = Inhalt
    42. Return CDbl(Inhalt)
    43. End Function


    Um eine bestimmte Zeit abzuwarten ohne die Anwendung einfrieren zu lassen, verwende ich meine eigene Funktion "Warte". Hier der Code. Natürlich kann jeder seinen eigene "Wartecode" verwenden. Aber thread.sleep() kommt für mich nicht in Frage.

    VB.NET-Quellcode

    1. ''' <summary>
    2. ''' Wartet eine Anzahl an Sekunden
    3. ''' </summary>
    4. ''' <param name="Sekunden">Sekunden als Double</param>
    5. ''' <remarks></remarks>
    6. Public Sub Warte(Sekunden As Double)
    7. Dim ZeitSpanne As Double
    8. Dim Start As Double
    9. ZeitSpanne = Sekunden / 100000
    10. Start = DateTime.Now.ToOADate() ' Anfangszeit setzen.
    11. Do While DateTime.Now.ToOADate() < Start + ZeitSpanne
    12. Application.DoEvents() ' Steuerung an andere Prozesse abgeben
    13. Loop
    14. End Sub




    PS: Numeric up/down Felder sind keine wirkliche optische Alternative <X .

    Der Code oben ist womöglich nicht 100% astreines VB.NET, sondern möglicherweise VB6 lastig. Daher habe ich den Code nicht unter Tipps und Tricks gepostet. Sollte der Code aber einigermaßen "annehmbar" sein, dann darf dieser Beitrag gerne verschoben werden.

    LG Roland
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at
    Sorry, aber spätestens bei Goto hört's bei mir auf. Wenn Du damit gute produktive Fortschritte machst, dann ist alles super. Nutze es. Aber ansonsten wurde dieses Problem schon mehrfach angegangen und gelöst. Ein einfaches Double.TryParse(DerText, DieZielVariable) erspart hier schon sehr viele Deiner Codezeilen.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    VaporiZed schrieb:

    Sorry, aber spätestens bei Goto hört's bei mir auf.


    Für alternative Vorschläge bin ich gerne offen. Dafür habe ich den Code ja hier auch eingestellt.


    Ein einfaches Double.TryParse(DerText, DieZielVariable) erspart hier schon sehr viele Deiner Codezeilen.

    Ja könnte man machen, aber dann wüsste man nicht warum der der Wert 0 ist und könnte dem User keine Nachricht ausgeben.
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at
    @dive26 Du kannst natürlich im KeyDown-Event der Textbox alle nicht erlaubten Tasten rausfiltern, da bleibt wohl nix übrig, was dem User mitgeteilt werden müsste.
    Bei TryParse musst Du nicht den Zahlenwert 0, sondern den Rückgabewert der Prozedur selbst abfangen.
    Außerdem kannst Du TryParse noch einen NumberStyles-Wert mitgeben, der das Aussehen des Inputs besser beschreibt.
    docs.microsoft.com/de-de/dotne…rse?view=netframework-4.8
    @VB1963 Hater doch abgelehnt.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    NumericUpDown-Controls

    Dies wurde von anderen Nutzern bereits in anderen Beiträgen auch schon ausgiebig diskutiert. Die NumericUpDown wären zwar eine technische perfekte Lösung, aber optisch und in der Handhabung für meine Kunden nicht optimal. Die meisten meiner Anwendungen sind mit Touchscreen verwendbar und gerade da sind die beiden updown Pfeilchen a) nicht verwendbar, weil man sie nicht trifft und b) störend, da man diese versehentlich erwischt .

    Daher kommen für mich hier nur Textboxen in Frage.
    (Genau so wie ich comboboxen automatisch beim Klick darauf aufklappen muss - aber das ist ein anderes Thema ;-))

    Bei TryParse musst Du nicht den Zahlenwert 0, sondern den Rückgabewert der Prozedur selbst abfangen.
    Außerdem kannst Du TryParse noch einen NumberStyles-Wert mitgeben, der das Aussehen des Inputs besser beschreibt.


    Aber genau diesen Mehraufwand bei jeder Textbox wollte ich mir mit meiner Funktion sparen. Die Prüft die Eingabe auch gleich auf Einhaltung der
    min- und max Werte. Aber das mit dem NumberStyles-Wert wußte ich z.B. noch nicht. Danke dafür.

    Mein Bestreben für diese Funktion war:
    *Interaktion mit dem Benuzter: der soll wissen dass er etwas falsches eingegeben hat und gleich einen gültigen Wertebereich ablesen können.
    * So wenig Code wie möglich beim Auslesen der Textboxen.

    Also das was die Funktion macht ist (für mich) schon in Ordnung. Nur der Code selbst müsste/könnte noch optimiert werden, da es keine "sauberes" .NET ist. Das angesprochene Goto - was für Alternativen gibt es dazu die den Code nicht gleich 10 Zeilen länger werden lassen @VaporiZed ?
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „dive26“ ()

    dive26 schrieb:

    Die meisten meiner Anwendungen sind mit Touchscreen verwendbar und gerade da sind die beiden updown Pfeilchen a) nicht verwendbar, weil man sie nicht trifft und b) störend, da man diese versehentlich erwischt .

    VB.NET-Quellcode

    1. 'Versuche folgendes, dann sieht's aus wie eine Textbox:
    2. NumericUpDown1.Controls(0).Visible = False
    3. 'oder
    4. NumericUpDown1.Controls.RemoveAt(0)
    @VB1963

    Technisch wäre das alles möglich. Auch ein Minimum und ein Maximum kann man bei NumericUpDown einstellen.
    Aber warum soll man für das alles zusätzliche Codezeilen schreiben, wenns mit einem Textfeld auch ohne zusätzliche Zeilen auch geht?

    Egal was Ihr versucht, NumericUpDown werdet Ihr mir nicht schmackhaft machen können ;).
    Dafür hat hier Microsoft seine Hausaufgaben nicht gut genug gemacht - hier müsste man diese up/down Controls auch in der IDE deaktivieren können.
    Also eine "NumericTextbox" ;-). Zudem würde mir diese auch nicht das Problem mit der Benutzerrückmeldung lösen.

    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    dive26 schrieb:

    Aber warum soll man für das alles zusätzliche Codezeilen schreiben, wenns mit einem Textfeld auch ohne zusätzliche Zeilen auch geht?

    Damit wir, glaub ich, vom selben reden:
    Codezeilen, damit NUD so tut und aussieht wie eine Textbox, ist eine Zeile mit 0 Zeilen Eingabeüberprüfung...
    Textbox, damit die Eingaben passen, sind sehr viele Zeilen als nur eine...

    dive26 schrieb:

    der soll wissen dass er etwas falsches eingegeben hat
    z.B. "Roulade mit Klößen"?
    Bei meinem Vorschlag wäre das erst gar nicht möglich, wie auch bei einem NUD.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Deine Prüfmethoden um sicherzustellen das die Eingabe gültig ist solltest du noch einmal überdenken. Wenn die NumberStyles flags von TryParse ausreichen wäre das eine Option, ansonsten könntest du auch beispielsweise reguläre ausdrücke verwenden. Um deine GoTo sprünge zu vermeiden machst du einfach eine neue Methode die für die Nutzer Benachrichtigung zuständig ist und rufst diese stattdessen an den betroffenen stellen auf.

    dive26 schrieb:

    Aber warum soll man für das alles zusätzliche Codezeilen schreiben, wenns mit einem Textfeld auch ohne zusätzliche Zeilen auch geht?
    Was heißt da "ohne zusätzliche Zeilen"? Dein Prüfcode hat doch schon knapp 50. Und der lässt sich eindampfen. Späßleshalber hab ich mal die verschiedenen Fehlermeldungen mit eingebaut. Neue Konzepte zum Post#1-Code sind: VB6-Freiheit (rotes Tuch, ich weiß), Nebenläufigkeit mit Async/Await (erspart einem das rote Tuch für andere: DoEvents), LINQ (erspart einem die langwierigen Zählungen mit For, Mid, If), Extensions (lässt es so aussehen, als wäre die Funktion Teil des TextBox-Objektes) und vorzeitige Codeabbrüche (die Funktion muss nicht weiter abgearbeitet werden, wenn klar ist, dass der eingegebene Text Murks ist), interpolierte Strings (in den Zeilen#18 und #19; vereinfacht die Ausgabe von Texten mit Variablen). Ob man verschiedene Fehlermeldungen braucht, sei mal dahin gestellt. Und die Fehleranzeige hab ich mal auf 2 Sekunden erhöht. Wer in ner halben Sekunde erkennt, was er falsch gemacht hat, scheint häufiger mal Murks da rein zu schreiben.

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    3. Dim Result As Double = TextBox1.GetNumberFromTextBetween(1, 100)
    4. End Sub
    5. End Class
    6. Public Module Extensions
    7. <Runtime.CompilerServices.Extension>
    8. Public Function GetNumberFromTextBetween(TextBox As TextBox, Minimum As Double, Maximum As Double) As Double
    9. Dim CurrentTextBoxText = TextBox.Text
    10. Dim Result As Double
    11. If String.IsNullOrEmpty(CurrentTextBoxText.Trim) Then ComplainAbout("leer", TextBox, Minimum) : Return Minimum
    12. If CurrentTextBoxText.Count(Function(x) x = "."c) > 1 Then ComplainAbout("zu viele Punkte", TextBox, Minimum) : Return Minimum
    13. If CurrentTextBoxText.Count(Function(x) x = ","c) > 1 Then ComplainAbout("zu viele Kommata", TextBox, Minimum) : Return Minimum
    14. CurrentTextBoxText = CurrentTextBoxText.Replace(".", ",")
    15. If Not Double.TryParse(CurrentTextBoxText, Result) Then ComplainAbout("keine Zahl", TextBox, Minimum) : Return Minimum
    16. If Result < Minimum Then ComplainAbout($"Zahl kleiner als Minimum ({Minimum}).", TextBox, Minimum) : Return Minimum
    17. If Result > Maximum Then ComplainAbout($"Zahl größer als Maximum ({Maximum}).", TextBox, Maximum) : Return Maximum
    18. Return Result
    19. End Function
    20. Private Async Sub ComplainAbout(Message As String, TextBox As TextBox, DefaultValue As Double)
    21. TextBox.Text = Message
    22. Dim OriginalForeColor = TextBox.ForeColor
    23. Dim OriginalBackColor = TextBox.BackColor
    24. TextBox.ForeColor = Drawing.Color.White
    25. TextBox.BackColor = Drawing.Color.Red
    26. Await Threading.Tasks.Task.Delay(2000)
    27. TextBox.ForeColor = OriginalForeColor
    28. TextBox.BackColor = OriginalBackColor
    29. TextBox.Text = DefaultValue.ToString
    30. End Sub
    31. End Module

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Ich muss nicht testen, mehrere Punkte / Kommas drinne sind, wenn ich im KeyDown-Event das jeweils zweite bereits ablehne.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Und wie willst Du das für x TextBoxen machen, ohne das existierende GUI zu ändern und ohne jeder TextBox einen eigenen EventHandler zu schreiben? Denn das ist ja das Anliegen des TE: Funktionalität für ein bestehendes System. Da bliebe ja nur zu Programmstart eine Schleife über alle TextBoxen zu machen und dann ihnen Deinen CustomKeyDownEventHandler an die Hand zu geben. Aber nicht jede TextBox ist eine NumericTextBox. Ud da erstmal alle TargetTextBoxen raussuchen, diese in einem Feld zu sammeln und nur denen einen gemeinsamen EventHandler rauszusuchen, könnte langwierig sein. Ggf. die TargetTextBoxen mit nem passenden Tag versehen und dann alle rausfischen.
    Aber wie ist da Dein Plan?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    @VaporiZed

    Vielen Dank für die Mühe. Habe ich auch gleich ausprobiert und eingebaut. Sieht mehr nach .NET aus ;)
    Mit Extensions hatte ich bis jetzt eigentlich noch nichts zu tun, daher ist es schön das auch mal zu lernen. Das war auch der Grund, warum ich meinen Code hier gepostet habe, weil ich wusste, dass er zu VB6 lastig ist und eine rege Diskussion zum Nachdenken (meinerseits) anregt. Vielen Dank.

    LG Roland



    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „dive26“ ()

    VaporiZed schrieb:

    If CurrentTextBoxText.Count(Function(x) x = "."c) > 1 Then ComplainAbout("zu viele Punkte", TextBox, Minimum) : Return Minimum
    If CurrentTextBoxText.Count(Function(x) x = ","c) > 1 Then ComplainAbout("zu viele Kommata", TextBox, Minimum) : Return Minimum
    CurrentTextBoxText = CurrentTextBoxText.Replace(".", ",")
    Wenn das nicht auf die deutsche Region begrenzt sein soll würde ich da mit Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator bzw. Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator arbeiten.
    Dann kann das Programm auch in der Schweiz oder bei den Amerikanern eingesetzt werden (Punkt-Länder).
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --

    VaporiZed schrieb:

    Und wie willst Du das für x TextBoxen machen, ohne das existierende GUI zu ändern
    Entweder über AddHandler oder ich baue ein UserControl und ersetzte im Design-Code System.Windows.Forms.TextBox durch MyNamespace.MyTextBox.
    Hab ich schon mehrfach gemacht, funktioniert prima, würde ich gegenüber AddHandler präferieren.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!