vereinfachte Datumseingabe in Textbox - eigenes TextboxControl?

  • VB.NET

Es gibt 24 Antworten in diesem Thema. Der letzte Beitrag () ist von tragl.

    vereinfachte Datumseingabe in Textbox - eigenes TextboxControl?

    Moin zusammen.

    Ich würde gerne den DateTimePicker durch eine Textbox mit folgenden Features ersetzen:

    - Leertaste drücken -> aktuelles Datum wird im ShortDateString eingetragen
    Das klappt schon über das KeyDown event:

    VB.NET-Quellcode

    1. Private Sub TextBox_KeyDown(sender As Object, e As KeyEventArgs) Handles tb1.KeyDown, tb2.KeyDown
    2. Select Case e.KeyCode
    3. Case Keys.Space
    4. ActiveControl.Text = Date.Today.ToShortDateString
    5. Case Keys.Enter
    6. SelectNextControl(ActiveControl, True, True, True, True)
    7. End Select
    8. End Sub


    - nur Zahlen eingeben und er soll ein gültiges Datum daraus generieren
    Beispiele: (Bilder zeigen immer die Eingabe und danach das Ergebnis)
    Eingabe "20" für den Tag 20.:
    Eingabe "2008" für 20.08.:
    Eingabe "200819" für 20.08.2019:
    Eingabe "20082017" für 20.08.2017:

    In unserem WaWi-System (da sind die Bildchen raus) funzt das so und erleichtert die Eingabe von Datum enorm!
    Dagegen sind die DateTimePicker ein "Witz", denn da muss man rumklicken oder die Pfeiltasten benutzen (ich find die seeehr umständlich, wenn auch das Feature mit dem Kalender
    zur alternativen Auswahl ganz nett ist. Aber im Alltag wird man bei vielen Eingaben bei der Tastatureingabe bleiben weil's einfach schneller geht ;) ) .
    Einen Kalender zum Auswählen bekommt man dennoch beim klick auf "..." oder durch Drücken von F2.

    Hat jemand einen Ansatz für mich, wie man sich ein solches Control basteln kann?
    Die Umrechnung sollte dann stattfinden, wenn die Textbox den Focus verliert (z.B. bei Textbox.leave). Wenn irgendein Müll eingegeben wird
    (z.B. "Hallo" oder sowas) dann darf die gerne leer bleiben - damit wird der User aufgefordert was ordentliches einzugeben. Von einer Messagebox
    würde ich absehen.

    Vielleicht gibt's sowas in der Art ja schon "fertig" im VisualStudio - ich hab' aber noch nichts dergleichen gefunden.
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „tragl“ ()

    @tragl Was fertiges gibt es da nicht, das musst Du alles selbst erledigen.
    Arbeite mit dem KeyDown-Event und ignoriere zunächst alle Tasten, die nicht gedrückt werden dürfen.
    Am besten, Du baust Dir eine eigene DateTextBox, ein von TextBox abgeleitetes Control, die genau das macht, was Du brauchst.
    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!
    In meim Stechuhr-Projekt habich ein "TextboxBehavior" drinne, also etwas, was von Textbox bestimmte Events verarbeitet und dann Logik ausführt.
    Son Behavior ist besser als ein selbstgebasteltes Control, weil man kann die Standard-Controls belassen, und modifiziert nur ihr Verhalten. Und so könnte man dieselbe Textbox prinzipiell auch mit mehreren Behaviors ausrüsten - was man beim Vererbungs-Ansatz nur mit Mehrfachvererbung hinbekäme, und das kann vb.net garnet.
    Gucks dir mal an - meine Logik wandelt Eingaben ins "hh:mm"-Format um, und dieses wiederum in Double - dassis praktisch um im Dataset mit Zeitspannen zu rechnen.
    Mein Ansatz basiert auch auf Databinding - nämlich im Hintergrund von Databinding werkeln sog. Binding-Objekte - und die werfen auch Events - ein sehr nützlicher Mechanismus, wenn man Äpfel behandeln muss wie Birnen.
    Vollzitat des direkten Vorposts an dieser Stelle durch Ansprechfunktion ersetzt ~VaporiZed
    @ErfinderDesRades: Das ist für mich noch alles recht spanisch und ich blick da noch nicht wirklich durch - versuch's aber weiterhin

    RodFromGermany schrieb:

    @tragl Was fertiges gibt es da nicht, das musst Du alles selbst erledigen.
    Arbeite mit dem KeyDown-Event und ignoriere zunächst alle Tasten, die nicht gedrückt werden dürfen.
    Am besten, Du baust Dir eine eigene DateTextBox, ein von TextBox abgeleitetes Control, die genau das macht, was Du brauchst.

    Das hab' ich mir schon gedacht :D und die hässliche und unhantliche Masked-Textbox kommt nicht in Frage.

    RodFromGermany schrieb:

    Am besten, Du baust Dir eine eigene DateTextBox, ein von TextBox abgeleitetes Control, die genau das macht, was Du brauchst.

    Ich versuch das bisher erstmal auf einer Form mit 2 Textboxen, ein abgeleitetes Control (da brauch' ich vermutlich sowieso Hilfe) kann man immernoch bauen.
    Zum Testen ob das gecodete funzt ist mir das die einfachste Methode.
    Bisher gibt's das:

    VB.NET-Quellcode

    1. Public Class frmTextboxtest
    2. Private Sub TextBox_KeyDown(sender As Object, e As KeyEventArgs) Handles tb1.KeyDown, tb2.KeyDown
    3. Select Case e.KeyCode
    4. Case Keys.Space
    5. ActiveControl.Text = Date.Today.ToShortDateString
    6. Case Keys.Enter
    7. SelectNextControl(ActiveControl, True, True, True, True)
    8. End Select
    9. End Sub
    10. Private Sub Textbox_KeyPress(sender As Object, e As KeyPressEventArgs) Handles tb1.KeyPress, tb2.KeyPress
    11. Select Case True
    12. Case (e.KeyChar >= "0" AndAlso e.KeyChar <= "9") Or e.KeyChar = "." Or e.KeyChar = Convert.ToChar(Keys.Back)
    13. e.Handled = False
    14. Case Else
    15. e.Handled = True
    16. End Select
    17. End Sub
    18. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    19. msgInformation($"{tb1.Text} | {tb2.Text}")
    20. End Sub
    21. Private Sub TextBox_Leave(sender As Object, e As EventArgs) Handles tb1.Leave, tb2.Leave
    22. Dim dateResult As Date = Nothing
    23. Date.TryParse(ActiveControl.Text, dateResult)
    24. msgInformation(dateResult.ToString)
    25. End Sub
    26. End Class


    Also ich kann mit Leertaste das aktuelle Datum reinhauen und mit Enter auch in die nächste Textbox springen.
    Des Weiteren werden alle Numerischen Eingaben, sowie "." und Backspace berücksichtigt - alle anderen Zeichen lassen sich garnicht erst eingeben -
    das is schonmal gut.

    Jetzt muss ich daran, dass er mir die Zahlen umwandelt in Date und zwar egal in welchem Fall :P
    Also 00 = Tag
    000 tag, monat
    0000 tag, monat
    000000 tag, monat, jahr
    00000000 tag, monat, jahr

    Naja, mal gucken - muss ja zu schaffen sein ;)
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()

    @tragl Die KeyPress-Logik (nicht den Code!) kannst Du in der KeyDown unterbringen, sieh Dir dazu die Properties von KeyEventArgs im Gegensatz zu KeyPressEventArgs an.
    Um zur nächsten TextBox zu kommen wird bei Windows üblicherweise die Tab-Taste verwendet.
    Da Konvertieren von der Ziffernfolge zu Tag, Monat, Jahr ist dann nur noch ne Fleißarbeit.
    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!

    RodFromGermany schrieb:

    @tragl Die KeyPress-Logik (nicht den Code!) kannst Du in der KeyDown unterbringen, sieh Dir dazu die Properties von KeyEventArgs im Gegensatz zu KeyPressEventArgs an.

    versteh' ich nicht ?(

    RodFromGermany schrieb:

    Um zur nächsten TextBox zu kommen wird bei Windows üblicherweise die Tab-Taste verwendet.

    Ja, das ist korrekt - ich wollte Enter aber noch als Möglichkeit bereitstellen

    RodFromGermany schrieb:

    Da Konvertieren von der Ziffernfolge zu Tag, Monat, Jahr ist dann nur noch ne Fleißarbeit.

    jupp - für mich als Anfänger aber sehr schwer aber muss ja irgendwie gehen ;)
    Komm ich da über die Length bzw. Char-zählen weiter?

    Wenn len <3 dann Date.Parse(text.monat.jahr)
    wenn len >3 und <=4 dann Date.Parse(text.jahr)
    usw?

    muss ich mal testen

    Edit:
    @RodFromGermany so sieht das als Anfänger aus ;(
    also sehr unschön aber tut genau so wie ich es mir vorstelle:

    VB.NET-Quellcode

    1. Private Sub TextBox_Leave(sender As Object, e As EventArgs) Handles tb1.Leave, tb2.Leave
    2. Dim dateResult As New Date
    3. Dim tb = TryCast(sender, TextBox)
    4. Select Case True
    5. Case tb.TextLength = 0
    6. dateResult = Nothing
    7. Case tb.TextLength > 1 AndAlso tb.TextLength < 3
    8. dateResult = Date.Parse($"{tb.Text}.{Date.Now.Month}.{Date.Now.Year}")
    9. Case tb.TextLength = 3
    10. Dim tag = $"{tb.Text(0)}{tb.Text(1)}", monat = tb.Text(2)
    11. dateResult = Date.Parse($"{tag}.0{monat}.{Date.Now.Year}")
    12. Case tb.TextLength = 4
    13. Dim tag = $"{tb.Text(0)}{tb.Text(1)}", monat = $"{tb.Text(2)}{tb.Text(3)}"
    14. dateResult = Date.Parse($"{tag}.{monat}.{Date.Now.Year}")
    15. Case tb.TextLength = 5
    16. dateResult = Nothing
    17. Case tb.TextLength = 6
    18. Dim tag = $"{tb.Text(0)}{tb.Text(1)}", monat = $"{tb.Text(2)}{tb.Text(3)}", jahr = $"{tb.Text(4)}{tb.Text(5)}"
    19. dateResult = Date.Parse($"{tag}.{monat}.{jahr}")
    20. Case tb.TextLength = 7
    21. dateResult = Nothing
    22. Case tb.TextLength = 8
    23. Dim tag = $"{tb.Text(0)}{tb.Text(1)}", monat = $"{tb.Text(2)}{tb.Text(3)}", jahr = $"{tb.Text(4)}{tb.Text(5)}{tb.Text(6)}{tb.Text(7)}"
    24. dateResult = Date.Parse($"{tag}.{monat}.{jahr}")
    25. Case Else
    26. dateResult = Nothing
    27. End Select
    28. If dateResult = Nothing Then
    29. tb.SelectAll()
    30. Else
    31. tb.Text = dateResult.ToShortDateString
    32. End If
    33. End Sub
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „tragl“ ()

    tragl schrieb:

    versteh' ich nicht
    Die KeyPressEventArgs haben eine Property Handled, die KeyEventArgs haben eine Property SuppressKeyPress.
    Dann kommt so was raus (Achtung: Die Aufzählung der erlaubten Tasten ist nicht vollständig):

    VB.NET-Quellcode

    1. Private Sub TextBox_KeyDown(sender As Object, e As KeyEventArgs) Handles TextBox1.KeyDown, TextBox2.KeyDown
    2. Dim tb As TextBox = CType(sender, TextBox)
    3. Select Case e.KeyCode
    4. Case Keys.Space
    5. tb.Text = Date.Today.ToShortDateString
    6. Case Keys.Enter
    7. SelectNextControl(tb, True, True, True, True)
    8. Case Keys.D0 To Keys.D9, Keys.Oemcomma, Keys.Back
    9. ' nix tun
    10. ' Diese Aufzählung ist nicht vollständig!
    11. Case Else
    12. e.SuppressKeyPress = True
    13. End Select
    14. End Sub
    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!
    Danke, SuppressKeyPress war mir durchgerutscht

    funktioniert nun so und ich kann KeyPress weglassen :)

    VB.NET-Quellcode

    1. Private Sub TextBox_KeyDown(sender As Object, e As KeyEventArgs) Handles tb1.KeyDown, tb2.KeyDown
    2. Dim tb = TryCast(sender, TextBox)
    3. Select Case e.KeyCode
    4. Case Keys.Space
    5. tb.Text = Date.Today.ToShortDateString 'Füllt das aktuelle Datum im ShortDateString-Format in die Textbox
    6. Case Keys.Enter
    7. SelectNextControl(tb, True, True, True, True) 'Selektiert das nächste Control via Enter-Taste
    8. Case Keys.D0 To Keys.D9, Keys.NumPad0 To Keys.NumPad9, Keys.OemPeriod, Keys.Back 'nur Ziffern "." und Backspace zulassen
    9. 'nix tun
    10. Case Else
    11. e.SuppressKeyPress = True 'alle anderen Eingaben ablehnen
    12. End Select
    13. End Sub


    ich hab das Datumgefummel mal noch auf Überprüfung umgebaut via TryParse. So wie ich das seh' kann man da jetzt keinen Müll mehr eingeben.

    VB.NET-Quellcode

    1. Private Sub TextBox_Leave(sender As Object, e As EventArgs) Handles tb1.Leave, tb2.Leave
    2. Dim dateResult As New Date
    3. Dim tb = TryCast(sender, TextBox)
    4. Select Case True
    5. Case tb.TextLength > 1 AndAlso tb.TextLength < 3
    6. Date.TryParse($"{tb.Text}.{Date.Now.Month}.{Date.Now.Year}", dateResult)
    7. Case tb.TextLength = 3
    8. Dim tag = $"{tb.Text(0)}{tb.Text(1)}", monat = tb.Text(2)
    9. Date.TryParse($"{tag}.0{monat}.{Date.Now.Year}", dateResult)
    10. Case tb.TextLength = 4
    11. Dim tag = $"{tb.Text(0)}{tb.Text(1)}", monat = $"{tb.Text(2)}{tb.Text(3)}"
    12. Date.TryParse($"{tag}.{monat}.{Date.Now.Year}", dateResult)
    13. Case tb.TextLength = 6
    14. Dim tag = $"{tb.Text(0)}{tb.Text(1)}", monat = $"{tb.Text(2)}{tb.Text(3)}", jahr = $"{tb.Text(4)}{tb.Text(5)}"
    15. Date.TryParse($"{tag}.{monat}.{jahr}", dateResult)
    16. Case tb.TextLength = 8
    17. Dim tag = $"{tb.Text(0)}{tb.Text(1)}", monat = $"{tb.Text(2)}{tb.Text(3)}", jahr = $"{tb.Text(4)}{tb.Text(5)}{tb.Text(6)}{tb.Text(7)}"
    18. Date.TryParse($"{tag}.{monat}.{jahr}", dateResult)
    19. Case Else
    20. dateResult = Nothing
    21. End Select
    22. If dateResult = Nothing Then
    23. tb.Text = "ungültiges Datum"
    24. tb.ForeColor = Color.Red
    25. Else
    26. tb.Text = dateResult.ToShortDateString
    27. tb.ForeColor = SystemColors.WindowText
    28. End If
    29. End Sub
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „VaporiZed“ ()

    ziemlich unkonventionell, in einem String die einzelnen Chars aneinanderzusetzen, und als Datum zu parsen - aber geht durchaus. Damit nicht 100 mal tb.Text abgerufen wird, und um das originelle Verfahren deutlicher erkennbar zu machen, täte ich eine lokale Variable chars bilden, und die benützen:

    VB.NET-Quellcode

    1. Private Sub TextBox_Leave(sender As Object, e As EventArgs) Handles tb1.Leave, tb2.Leave
    2. Dim dateResult As Date
    3. Dim tb = TryCast(sender, TextBox)
    4. Dim chars = tb.Text.ToCharArray
    5. Dim tag, monat, jahr As String
    6. jahr = Date.Today.Year.ToString
    7. Select Case True
    8. Case tb.TextLength > 1 AndAlso tb.TextLength < 3
    9. dateResult = Date.Today ' da muss man nix parsen
    10. Case tb.TextLength = 3
    11. tag = $"{chars(0)}{chars(1)}" : monat = chars(2)
    12. Date.TryParse($"{tag}.0{monat}.{Date.Now.Year}", dateResult)
    13. Case tb.TextLength = 4
    14. tag = $"{chars(0)}{chars(1)}" : monat = $"{chars(2)}{chars(3)}"
    15. Date.TryParse($"{tag}.{monat}.{Date.Now.Year}", dateResult)
    16. Case tb.TextLength = 6
    17. tag = $"{chars(0)}{chars(1)}" : monat = $"{chars(2)}{chars(3)}" : jahr = $"{chars(4)}{chars(5)}"
    18. Date.TryParse($"{tag}.{monat}.{jahr}", dateResult)
    19. Case tb.TextLength = 8
    20. tag = $"{chars(0)}{chars(1)}" : monat = $"{chars(2)}{chars(3)}" : jahr = $"{chars(4)}{chars(5)}{chars(6)}{chars(7)}"
    21. Date.TryParse($"{tag}.{monat}.{jahr}", dateResult)
    22. Case Else
    23. dateResult = Nothing
    24. End Select
    25. If dateResult = Nothing Then
    26. tb.Text = "ungültiges Datum"
    27. tb.ForeColor = Color.Red
    28. Else
    29. tb.Text = dateResult.ToShortDateString
    30. tb.ForeColor = SystemColors.WindowText
    31. End If
    32. End Sub
    Konventioneller wäre wohl, mit .SubString zu arbeiten (guck dir unbedingt String im ObjectBrowser an!).

    Noch hübscher wäre so:

    VB.NET-Quellcode

    1. Private Sub TextBox_Leave(sender As Object, e As EventArgs) Handles tb1.Leave, tb2.Leave
    2. Dim dateResult As Date
    3. Dim tb = TryCast(sender, TextBox)
    4. Dim chars = tb.Text.ToCharArray
    5. Dim tag = Date.Today.Day.ToString, monat = Date.Today.Month.ToString, jahr = Date.Today.Year.ToString
    6. Select Case tb.TextLength
    7. Case 2 ' nix zu tun
    8. Case 3
    9. tag = {chars(0), chars(1)} : monat = chars(2)
    10. Case 4
    11. tag = {chars(0), chars(1)} : monat = {chars(2), chars(3)}
    12. Case 6
    13. tag = {chars(0), chars(1)} : monat = {chars(2), chars(3)} : jahr = {chars(4), chars(5)}
    14. Case 8
    15. tag = {chars(0), chars(1)} : monat = {chars(2), chars(3)} : jahr = {chars(4), chars(5), chars(6), chars(7)}
    16. Case Else
    17. tag = "ungültig-Text"
    18. End Select
    19. Date.TryParse($"{tag}.{monat}.{jahr}", dateResult)
    20. If dateResult = Nothing Then
    21. tb.Text = "ungültiges Datum"
    22. tb.ForeColor = Color.Red
    23. Else
    24. tb.Text = dateResult.ToShortDateString
    25. tb.ForeColor = SystemColors.WindowText
    26. End If
    27. End Sub
    Also dann auch konsequent mit Char-Arrays zu arbeiten, und nicht mit $"String-Interpolation".
    Beachte auch die nun korrekte Verwendung von Select Case.
    Select Case auch unbedingt recherchieren - das ist unerhört vielseitig und mächtig!

    Und noch ein':

    VB.NET-Quellcode

    1. Private Sub TextBox_Leave(sender As Object, e As EventArgs) Handles tb1.Leave, tb2.Leave
    2. Dim dateResult As Date
    3. Dim tb = TryCast(sender, TextBox)
    4. Dim chars = tb.Text.ToCharArray
    5. Dim tag = Date.Today.Day.ToString, monat = Date.Today.Month.ToString, jahr = Date.Today.Year.ToString
    6. Select Case tb.TextLength
    7. Case 2 ' nix zu tun
    8. Case 3
    9. tag = {chars(0), chars(1)} : monat = chars(2)
    10. Case 4, 6, 8
    11. tag = {chars(0), chars(1)} : monat = {chars(2), chars(3)}
    12. If tb.TextLength > 4 Then jahr = tb.Text.Substring(4)
    13. Case Else
    14. tag = "ungültig-Text"
    15. End Select
    16. If Date.TryParse($"{tag}.{monat}.{jahr}", dateResult) Then
    17. tb.Text = dateResult.ToShortDateString
    18. tb.ForeColor = SystemColors.WindowText
    19. Else
    20. tb.Text = "ungültiges Datum"
    21. tb.ForeColor = Color.Red
    22. End If
    23. End Sub

    Ein vielzitiertes, ganz allgemeines Programmier-Prinzip lautet: DRY - Dont Repeat Yourself!!

    HachGott - TryParse haste ja auch ganz falsch benutzt! Guck die Sachen im OB an!

    Dieser Beitrag wurde bereits 12 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ErfinderDesRades schrieb:

    ziemlich unkonventionell, in einem String die einzelnen Chars aneinanderzusetzen, und als Datum zu parsen - aber geht durchaus. Damit nicht 100 mal tb.Text abgerufen wird, und um das originelle Verfahren deutlicher erkennbar zu machen, täte ich eine lokale Variable chars bilden, und die benützen:

    Na dann lag ich ja doch nicht ganz so falsch mit meiner Ausführung :D
    Macht sich natürlich schöner, wenn's kürzer gehalten ist und liest sich einfacher. Ich hab bisher nur noch nie Strings zerstückelt,
    geschweigedenn Chars benutzt.

    ErfinderDesRades schrieb:

    dateResult = Date.Today ' da muss man nix parsen

    doch, denn die ersten beiden Ziffern oder halt wenn nur 2 oder 1 eingegeben werden, können ja einen beliebigen Tag darstellen. (01., 15.,18. usw) Mit Date.Today stellst du ja den aktuellen Tag ein.
    So auch in den anderen beiden Beispielen. Ich möchte ja z.B. nur "20" eingeben, dann soll er auf den 20.04.2020 umschalten oder mit "2" auf den 02.04.2020. In deinen Varianten geht er dann auf den 26.04.2020 (heute) ;)

    ErfinderDesRades schrieb:

    Also dann auch konsequent mit Char-Arrays zu arbeiten, und nicht mit $"String-Interpolation".

    Das werd' ich ab jetzt auch so tun, falls erforderlich :thumbup:

    ErfinderDesRades schrieb:

    Beachte auch die nun korrekte Verwendung von Select Case.
    Select Case auch unbedingt recherchieren - das ist unerhört vielseitig und mächtig!

    War mein Fehler, normal weiß ich dass ich da Select Case tb.TextLength zu benutzen habe - ist mir durchgerutscht.

    ErfinderDesRades schrieb:

    Ein vielzitiertes, ganz allgemeines Programmier-Prinzip lautet: DRY - Dont Repeat Yourself!!

    wie meinen?

    ErfinderDesRades schrieb:

    HachGott - TryParse haste ja auch ganz falsch benutzt! Guck die Sachen im OB an!

    Was ist an TryParse falsch? Ich hatte das aus dem OB entnommen. Und wenn ich die ausgeschriebene Variante DateTime.TryParse benutze, dann sagt mir Intellisense dass das verkürzt werden kann
    auf Date.TryParse - ich hab' lediglich die Boolesche Funktion davon nicht genutzt, denn wenn TryParse fehlgeschlagen war ist mein dateResult ja Nothing geblieben und damit hab' ich dann gearbeitet :P

    Public Shared Function TryParse(s As String, ByRef result As Date) As Boolean
    Member von "System.DateTime"

    Zusammenfassung:
    Konvertiert die angegebene Zeichenfolgendarstellung einer Datums- und Uhrzeitangabe in deren System.DateTime-Entsprechung und gibt einen Wert zurück, der angibt, ob die Konvertierung erfolgreich ausgeführt wurde.

    Parameter:
    s: Eine Zeichenfolge, die eine zu konvertierende Datums- und Zeitangabe enthält.
    result: Diese Methode gibt bei erfolgreicher Konvertierung den System.DateTime-Wert zurück, der dem Datum und der Zeit in s entspricht, oder System.DateTime.MinValue, wenn die Konvertierung nicht durchgeführt werden konnte. Die Konvertierung schlägt fehl, wenn der s-Parameter null bzw. eine leere Zeichenfolge ("") ist oder keine gültige Zeichenfolgendarstellung eines Datums und einer Uhrzeit enthält. Dieser Parameter wird nicht initialisiert übergeben.

    Rückgabewerte:
    true, wenn der s-Parameter erfolgreich konvertiert wurde, andernfalls false.



    Wiedem auch sein, der Code ist nun folgender: (was da noch fehlt ist, das abgefangen wird wenn der User dann doch mal mit Punkten arbeitet und
    z.B. 20.04 eingibt - dann sollte da auch das korrekte Datum erscheinen

    VB.NET-Quellcode

    1. Private Sub TextBox_Leave(sender As Object, e As EventArgs) Handles tb1.Leave, tb2.Leave
    2. Dim dateResult As New Date
    3. Dim tb = TryCast(sender, TextBox)
    4. Dim chars = tb.Text.ToCharArray
    5. Dim tag = "", monat = Date.Today.Month.ToString, jahr = Date.Today.Year.ToString
    6. Select Case tb.TextLength
    7. Case 1 : tag = {chars(0)}
    8. Case 2 : tag = {chars(0), chars(1)}
    9. Case 3 : tag = {chars(0), chars(1)} : monat = {chars(2)}
    10. Case 4, 6, 8
    11. tag = {chars(0), chars(1)} : monat = {chars(2), chars(3)}
    12. If tb.TextLength > 4 Then jahr = tb.Text.Substring(4)
    13. Case Else
    14. 'nix tun
    15. End Select
    16. If Date.TryParse($"{tag}.{monat}.{jahr}", dateResult) Then
    17. tb.Text = dateResult.ToShortDateString
    18. tb.ForeColor = SystemColors.WindowText
    19. Else
    20. tb.Text = "ungültiges Datum"
    21. tb.ForeColor = Color.Red
    22. End If
    23. End Sub


    Hast du die Datumseingabe in der Variante mal getestet? Das macht sich echt richtig gut - man benutzt viel weniger die Maus und es geht richtig schnell :) Solltest du mal tun, falls noch nicht geschehen.
    Ich versuch' jetzt noch einen Kalender (wie bei DateTimePicker) daneben auf den Button zu setzen bzw. den könnte man via F2 oder so aufrufen und daraus dann das Datum zu wählen.
    Der klassiche DateTimePicker ist für mich jedenfalls mit dieser Variante Geschichte :D
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    Wie meinen?
    Wörtlicher als du dir vorstellen kannst.
    Beim Programmieren soll man sich nicht wiederholen. In meim letzten Snippet habe ich einige solcher Wiederholungen ausgemerzt.
    DRY Das wird dir noch öfter begegnen.



    TryParse
    Das ist aber der Witz an Tryparse, dass die Methode dir zurückgibt, obs geklappt hat. Das ist ein Pattern, also Tryparse gibts auch für Timespan, Double, Integer,....
    Alles was man parsen kann (also aus einem String auslesen), das kann man auch TryParsen.

    tragl schrieb:

    (was da noch fehlt ist, das abgefangen wird wenn der User dann doch mal mit Punkten arbeitet und...
    Für korrekt eingegebene Datumse wüsste ich ein:

    VB.NET-Quellcode

    1. Private Sub TextBox_Leave(sender As Object, e As EventArgs) Handles tb1.Leave, tb2.Leave
    2. Dim dateResult As Date ' As Date - nicht As New Date
    3. Dim tb = DirectCast(sender, TextBox) ' DirectCast - nicht TryCast
    4. If DateTryParseSpecial(tb.Text, dateResult) Then
    5. tb.Text = dateResult.ToShortDateString
    6. tb.ForeColor = SystemColors.WindowText
    7. Else
    8. tb.Text = "ungültiges Datum"
    9. tb.ForeColor = Color.Red
    10. End If
    11. End Sub
    12. Private Shared Function DateTryParseSpecial(txt As String, ByRef result As Date) As Boolean
    13. If Date.TryParse(txt, result) Then Return True
    14. Dim chars = txt.ToCharArray
    15. Dim tag = "", monat = Date.Today.Month.ToString, jahr = Date.Today.Year.ToString
    16. Select Case txt.Length
    17. Case 1 : tag = {chars(0)}
    18. Case 2 : tag = {chars(0), chars(1)}
    19. Case 3 : tag = {chars(0), chars(1)} : monat = {chars(2)}
    20. Case 4, 6, 8
    21. tag = {chars(0), chars(1)} : monat = {chars(2), chars(3)}
    22. If txt.Length > 4 Then jahr = txt.Substring(4)
    23. Case Else : Return False
    24. End Select
    25. Return Date.TryParse($"{tag}.{monat}.{jahr}", result)
    26. End Function
    Das ist jetzt auch noch das SOC - Prinzip: "Separation of Concerns" - auch da nachzulesen, wo das DRY erläutert ist.
    jdfs. Zeile #14 löst das "Problem", falls der User sich untersteht, ein korrektes Datum einzugeben.



    Ist der Hammer, wie sich das teil immer weiter entwickelt, oder? :)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ErfinderDesRades schrieb:

    Wörtlicher als du dir vorstellen kannst.
    Beim Programmieren soll man sich nicht wiederholen. In meim letzten Snippet habe ich einige solcher Wiederholungen ausgemerzt.
    DRY Das wird dir noch öfter begegnen.

    war jetzt auf die ganzen tb.text bezogen? ansonsten versuch' ich immer so wenig Wiederholungen wie möglich zu machen, deshalb fass'
    ich gerne alles in Functions oder Subs zusammen :) aber ich werd' drauf achten!

    ErfinderDesRades schrieb:

    TryParse
    Das ist aber der Witz an Tryparse, dass die Methode dir zurückgibt, obs geklappt hat. Das ist ein Pattern, also Tryparse gibts auch für Timespan, Double, Integer,....
    Alles was man parsen kann (also aus einem String auslesen), das kann man auch TryParsen.

    also die Abfragefunktion / Boolesche Funktion davon auch nutzen

    ErfinderDesRades schrieb:

    Für korrekt eingegebene Datumse wüsste ich ein:

    Ist dann das "magische" mit dem Punkt einfach, dass TryParse ggf. 2 Punkte (20..04..2020) übergeben bekommt und macht daraus dann 20.04.2020? - weil es jetzt String txt ist? Interessant

    ErfinderDesRades schrieb:

    Ist der Hammer, wie sich das teil immer weiter entwickelt, oder?

    Das kannst du laut sagen! Vor Allem ICH entwickle mich - und das Tag für Tag. Und nebenbei die beiden Projekte "LogistikTool" und "Urlaubsplaner" (eigentlich gehört der Urlaubsplaner zu LogistikTool aber ich möchte den Urlaubsplaner separat durch entwickeln
    und dann hier im Forum als Source bereitstellen
    - dann hat jeder etwas davon, der sowas sucht :thumbup: )

    Und natürlich nicht zu vergessen - alles dank vor allem Deiner und natürlich auch der anderen Forenmembers Hilfe. Ich find's gut, dass ihr auch Neulinge an die Hand nehmt und denen direkt den richtigen Weg
    zeigt. Und vor allem erstmal den Neuling machen und versuchen lassen, bevor eine Lösung von euch kommt die der Neuling dann zum Großteil so übernehmen kann. Respect!
    :) :) :)
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    tragl schrieb:

    DRY - war jetzt auf die ganzen tb.text bezogen?
    Nein - v.a. auf
    tag = $"{chars(0)}{chars(1)}" : monat = $"{chars(2)}{chars(3)}" - kam zuvor 3 mal vor
    und Date.TryParse() - kam zuvor 4 mal vor.

    Das ist ganz interessant, wie man solche Sachen gelegentlich aus einem Select Case oder einer komplexen If-Else-Konstruktion quasi "rausziehen" kann.
    Ähnelt dem "Ausklammern"-Verfahren, mit dem man inne Algebra Ausdrücke zu vereinfachen sucht.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ErfinderDesRades schrieb:

    Zitat von tragl: „DRY - war jetzt auf die ganzen tb.text bezogen?“Nein - v.a. auf
    <code class="inlineCode">tag = $&quot;{chars(0)}{chars(1)}&quot; : monat = $&quot;{chars(2)}{chars(3)}&quot;</code> - kam zuvor 3 mal vor
    und<code class="inlineCode"> Date.TryParse()</code> - kam zuvor 4 mal vor.

    Ja das stimmt - alles Neuland ;) Aber ich mach' mir Gedanken drum und versuch alles umzusetzen - wo's nur geht.
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    Könnte man denn mit den o.g. Codes eine abgewandelte TextBox (dateTextBox) basteln als Control?
    Du hast sowas ähnliches ja mit dem DateTimePicker gemacht, allerdings hat der außer Nullable zu sein
    nix weiter zu tun - die Textbox wäre was umfangreicher

    VB.NET-Quellcode

    1. Imports System.ComponentModel
    2. Namespace System.Windows.Forms
    3. <DesignerCategory("Code")> _
    4. Public Class NullableDateTimePicker : Inherits DateTimePicker
    5. <Bindable(True)> _
    6. Public Shadows Property Value As Date?
    7. Get
    8. If Not Checked Then Return Nothing
    9. Return MyBase.Value
    10. End Get
    11. Set( value As Date?)
    12. If value.Equals(Me.Value) Then Return
    13. Checked = value.HasValue
    14. If value.HasValue Then MyBase.Value = value.Value
    15. End Set
    16. End Property
    17. End Class
    18. End Namespace
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    Wie gesagt: ein eigenes Control basteln befürworte ich nur, wenns echt nicht anders geht.
    Viel flexibler ist hier der mögliche Ansatz, so einen Eventhandler beliebigen bereits vorhandenen Textboxen zuzuweisen. Sowas ist inne Wpf als "Behavior-Pattern" bekannt, folgt aber auch dem CleanCode-Prinzip "Composition over Inheritance".

    Ansonsten: klar, ist natürlich möglich.

    (Beim NullabelDatePicker wars nicht anders lösbar als mit einem eigenen Control, weil es braucht eine geeignete Property, an die gebunden werden kann - sowas kann man natürlcih nicht mit Event-Zuweisungen lösen)
    Naja, aktuell mach' ich so bei der Form, die solche Boxen enthält:

    VB.NET-Quellcode

    1. #Region "TextBoxBehavior"
    2. Private Sub TextBox_KeyDown(sender As Object, e As KeyEventArgs) Handles dtbVon.KeyDown, dtbBis.KeyDown
    3. Dim tb = TryCast(sender, TextBox)
    4. Select Case e.KeyCode
    5. Case Keys.Space
    6. tb.Text = Date.Today.ToShortDateString 'Füllt das aktuelle Datum im ShortDateString-Format in die Textbox
    7. Case Keys.Enter
    8. SelectNextControl(tb, True, True, True, True) 'Selektiert das nächste Control via Enter-Taste
    9. Case Keys.D0 To Keys.D9, Keys.NumPad0 To Keys.NumPad9, Keys.OemPeriod, Keys.Back 'nur Ziffern "." und Backspace zulassen
    10. 'nix tun
    11. Case Else
    12. e.SuppressKeyPress = True 'alle anderen Eingaben ablehnen
    13. End Select
    14. End Sub
    15. Private Sub TextBox_Leave(sender As Object, e As EventArgs) Handles dtbVon.Leave, dtbBis.Leave
    16. Dim dateResult As Date ' As Date - nicht As New Date
    17. Dim tb = DirectCast(sender, TextBox) ' DirectCast - nicht TryCast
    18. If DateTryParseSpecial(tb.Text, dateResult) Then
    19. tb.Text = dateResult.ToShortDateString
    20. tb.ForeColor = SystemColors.WindowText
    21. Else
    22. tb.Text = "ungültiges Datum"
    23. tb.ForeColor = Color.Red
    24. End If
    25. End Sub
    26. Private Shared Function DateTryParseSpecial(txt As String, ByRef result As Date) As Boolean
    27. If Date.TryParse(txt, result) Then Return True
    28. Dim chars = txt.ToCharArray
    29. Dim tag = "", monat = Date.Today.Month.ToString, jahr = Date.Today.Year.ToString
    30. Select Case txt.Length
    31. Case 1 : tag = {chars(0)}
    32. Case 2 : tag = {chars(0), chars(1)}
    33. Case 3 : tag = {chars(0), chars(1)} : monat = {chars(2)}
    34. Case 4, 6, 8
    35. tag = {chars(0), chars(1)} : monat = {chars(2), chars(3)}
    36. If txt.Length > 4 Then jahr = txt.Substring(4)
    37. Case Else : Return False
    38. End Select
    39. Return Date.TryParse($"{tag}.{monat}.{jahr}", result)
    40. End Function
    41. Private Sub TextBox_Enter(sender As Object, e As EventArgs) Handles dtbVon.Enter, dtbBis.Enter
    42. Dim tb = TryCast(sender, TextBox) 'wandelt sender von Object in Textbox um, damit auf alle Propertys zugegriffen werden kann
    43. tb.SelectAll() 'markiert den gesamten Textboxinhalt - beschleunigt die Eingaben
    44. End Sub
    45. #End Region


    lässt sich aber vermulich in ein Modul oder ein Klasse packen und dann was mit Override oder?
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    Jo, eine Extension-Method in einem Modul täte sich anbieten - also so ähnlich wie meine Dataset.Register()-Extension-Methode.
    guck - in meine WinformHelpers gibts ja schon ein Modul namens ControlX - da könnte das auch rein:

    VB.NET-Quellcode

    1. Public Module ControlX
    2. #Region "Textbox"
    3. <Extension>
    4. Public Sub RegisterDateInput(tb As TextBox)
    5. AddHandler tb.Enter, AddressOf tb_Enter
    6. AddHandler tb.KeyDown, AddressOf tb_KeyDown
    7. AddHandler tb.Leave, AddressOf tb_Leave
    8. End Sub
    9. Private Sub tb_Enter(sender As Object, e As EventArgs)
    10. '...
    11. End Sub
    12. Private Sub tb_KeyDown(sender As Object, e As KeyEventArgs)
    13. '...
    14. End Sub
    15. Private Sub tb_Leave(sender As Object, e As EventArgs)
    16. '...
    17. End Sub
    18. Private Function DateTryParseSpecial(txt As String, ByRef result As Date) As Boolean
    19. If Date.TryParse(txt, result) Then Return True
    20. Dim chars = txt.ToCharArray
    21. Dim tag = "", monat = Date.Today.Month.ToString, jahr = Date.Today.Year.ToString
    22. Select Case txt.Length
    23. Case 1 : tag = {chars(0)}
    24. Case 2 : tag = {chars(0), chars(1)}
    25. Case 3 : tag = {chars(0), chars(1)} : monat = {chars(2)}
    26. Case 4, 6, 8
    27. tag = {chars(0), chars(1)} : monat = {chars(2), chars(3)}
    28. If txt.Length > 4 Then jahr = txt.Substring(4)
    29. Case Else : Return False
    30. End Select
    31. Return Date.TryParse($"{tag}.{monat}.{jahr}", result)
    32. End Function
    33. #End Region' Textbox
    34. #Region "DataGridView"
    35. '...
    36. #End Region 'DataGridView
    37. #Region "TabControl"
    38. '...
    39. #End Region 'TabControl
    so in der Art
    Schick, schick. Hab das nun so gelöst:

    VB.NET-Quellcode

    1. Imports System.Runtime.CompilerServices
    2. Module ControlBehavior
    3. #Region "DateTextBox"
    4. <Extension>
    5. Public Sub RegisterDateInput(dtb As TextBox)
    6. AddHandler dtb.Enter, AddressOf dtb_Enter
    7. AddHandler dtb.KeyDown, AddressOf dtb_KeyDown
    8. AddHandler dtb.Leave, AddressOf dtb_Leave
    9. End Sub
    10. Private Sub dtb_Enter(sender As Object, e As EventArgs)
    11. Dim tb = TryCast(sender, TextBox) 'wandelt sender von Object in Textbox um, damit auf alle Propertys zugegriffen werden kann
    12. tb.SelectAll() 'markiert den gesamten Textboxinhalt - beschleunigt die Eingaben
    13. End Sub
    14. Private Sub dtb_KeyDown(sender As Object, e As KeyEventArgs)
    15. Dim tb = TryCast(sender, TextBox)
    16. Select Case e.KeyCode
    17. Case Keys.Space
    18. tb.Text = Date.Today.ToShortDateString 'Füllt das aktuelle Datum im ShortDateString-Format in die Textbox
    19. Case Keys.Enter
    20. tb.SelectNextControl(tb, True, True, True, True) 'Selektiert das nächste Control via Enter-Taste
    21. Case Keys.D0 To Keys.D9, Keys.NumPad0 To Keys.NumPad9, Keys.OemPeriod, Keys.Back 'nur Ziffern "." und Backspace zulassen
    22. 'nix tun
    23. Case Else
    24. e.SuppressKeyPress = True 'alle anderen Eingaben ablehnen
    25. End Select
    26. End Sub
    27. Private Sub dtb_Leave(sender As Object, e As EventArgs)
    28. Dim dateResult As Date ' As Date - nicht As New Date
    29. Dim tb = DirectCast(sender, TextBox) ' DirectCast - nicht TryCast
    30. If DateTryParseSpecial(tb.Text, dateResult) Then
    31. tb.Text = dateResult.ToShortDateString
    32. tb.ForeColor = SystemColors.WindowText
    33. Else
    34. tb.Text = "ungültiges Datum"
    35. tb.ForeColor = Color.Red
    36. End If
    37. End Sub
    38. Private Function DateTryParseSpecial(txt As String, ByRef result As Date) As Boolean
    39. If Date.TryParse(txt, result) Then Return True
    40. Dim chars = txt.ToCharArray
    41. Dim tag = "", monat = Date.Today.Month.ToString, jahr = Date.Today.Year.ToString
    42. Select Case txt.Length
    43. Case 1 : tag = {chars(0)}
    44. Case 2 : tag = {chars(0), chars(1)}
    45. Case 3 : tag = {chars(0), chars(1)} : monat = {chars(2)}
    46. Case 4, 6, 8
    47. tag = {chars(0), chars(1)} : monat = {chars(2), chars(3)}
    48. If txt.Length > 4 Then jahr = txt.Substring(4)
    49. Case Else : Return False
    50. End Select
    51. Return Date.TryParse($"{tag}.{monat}.{jahr}", result)
    52. End Function
    53. #End Region 'DateTextBox
    54. End Module


    Kann man noch dtb As Textbox irgendwie als "ParamArray" einrichten? (Hab ich keine Ahnung von)
    Denn aktuell muss ich ja im FormLoad jede der Textboxen registrieren:

    VB.NET-Quellcode

    1. Private Sub dlgPlanDetail_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    2. RegisterDateInput(dtbVon)
    3. RegisterDateInput(dtbBis)
    4. dtbVon.Select()
    5. End Sub


    schöner wäre das mit RegisterDateInput(dtbVon, dtbBis)
    :)
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    tragl schrieb:

    Kann man noch dtb As Textbox irgendwie als "ParamArray" einrichten?
    Ja, mach doch:

    VB.NET-Quellcode

    1. Public Sub RegisterDateInput(ParamArray dtbs As TextBox())
    2. For Each dtb In dtbs
    3. AddHandler dtb.Enter, AddressOf dtb_Enter
    4. AddHandler dtb.KeyDown, AddressOf dtb_KeyDown
    5. AddHandler dtb.Leave, AddressOf dtb_Leave
    6. Next
    7. End Sub

    (Natürlich kannst du dtb nicht als ParamArray deklarieren, denn dtb ist ja nur eine Textbox - kein Array
    /Klugscheiss)

    Beachte aber, dasses dann keine Extension mehr ist - oder aber ist grad noch nicht so wichtig, was eine Extension ist.



    Hey - ich kann noch bisserl rummeckern!
    Zb in #32 schrieb ich dir: "DirectCast - nicht TryCast!!". Aber in zeile #12 und 17 machstes schon wieder.
    Alle Kommentare können weg - ausser zeile #57 (was ich einen "Orientierungs-Kommentar" nenne)
    ah - zeile #23 darf auch bleiben

    Hingegen fehlen tut ein Summary-Comment, der überhaupt erklärt, was Sub RegisterDateInput machen soll.

    Im Detail die Weg-Kommentare: #12 - das issn vb.net-Schlüsselwort - das muss man vorraussetzen. Kommentare können nicht die ganze Sprache erklären.
    #13 - das ist bereits im ObjectBrowser nachzulesen, bzw. Intellisense zeigt auch an, was die Methode tut.
    #20 naja - da wird ein String einem Textbox-Text zugewiesen - steht da doch.
    #22 - den könnte man lassen, aber eine Zeile höher. Dass man weiss, dass das bei Enter passieren soll. (Streng genommen steht das da auch schon, aber ich bin ja nicht streng)
    #23 der kann so bleiben wie ist.
    #26 auch eine Zeile höher

    Ist so ähnlich wie beim Kommentar-Fimmel-Effekt, wo ich mit die Leuts immer Streit anfange: Alles und jedes ist kommentiert - nur das wichtigste vergessen.
    (Stimmt hier nicht ganz - richtiges Kommentar-Gefimmel besteht viel mehr aus gelaberten Selbstverständlichkeiten - aber das wichtigste ist eben doch vergessen.)

    Aber vlt. kennste Summaries garnet - das geht so:

    VB.NET-Quellcode

    1. ''' <summary> bewirkt, dass nur noch Datumse eingegeben werden können - akzeptiert auch verkürzte Datum-Syntax:
    2. ''' "d", "dd", "dMM", "ddMM", und Space: Heute ... ja, sieh mal zu, wiede das vernünftig erklärt bekommst :P </summary>
    3. <Extension>
    4. Public Sub RegisterDateInput(dtb As TextBox)
    5. AddHandler dtb.Enter, AddressOf dtb_Enter
    6. AddHandler dtb.KeyDown, AddressOf dtb_KeyDown
    7. AddHandler dtb.Leave, AddressOf dtb_Leave
    8. End Sub


    Aber gesehen haben müssteste das schon irgendwo.

    Dieser Beitrag wurde bereits 12 mal editiert, zuletzt von „ErfinderDesRades“ ()