Neuigkeiten:

Wenn ihr euch für eine gute Antwort bedanken möchtet, im entsprechenden Posting einfach den Knopf "sag Danke" drücken!

Mobiles Hauptmenü

ermitteln welches STE als nächstes den Fokus hat

Begonnen von KonradR, November 03, 2024, 08:40:32

⏪ vorheriges - nächstes ⏩

KonradR

Zitat von: PhilS am November 05, 2024, 08:39:57Das BeforeUpdate-Event des Steuerelementes ist da besser geeignet, weil der Benutzer immerhin schon mal eine Änderung an dem Wert des Steuerelementes vorgenommen hat. - Auch hier könnte man überlegen, ob es nicht sinnvoller (benutzerfreundlicher) ist, wenn man das Problem anders kommuniziert, z.B. mit einer farblichen Markierung des/der Steuerelemente(s).
Erst im BeforeUpdate-Event des Formulars ist eine MsgBox (meistens) gerechtfertigt, weil das Programm keinen ungültigen/unvollständigen Datensatz speichern sollte.
Danke für deine ausführliche Erklärung. Das war sehr hilfreich. Also heißt das für mich, dass ich grundlegend versuchen sollte den Einsatz von MSG-Boxen zu vermeiden und eher auf die optische Änderung der STE zu setzen, sofern das überhaupt notwendig ist.

KonradR

#16
Zitat von: PhilS am November 04, 2024, 08:40:45Den Datensatz als ganzes, also die Kombination aller Steuerelemente, validiert man am besten im BeforeUpdate-Event des Formulars. In beiden BeforeUpdate-Events kannst du über das Cancel Argument abbrechen und somit verhindern, dass der Benutzer eine ungültige Eingabe speichert.
Ich habe jetzt die Prozedur in das Before-Update-Ereignis des Formulars übernommen. Leider passiert da nichts. Die von dir angesprochene optische Anpassung der STE's würde ich noch anpassen. Mir ging es erst mal nur um die Funktionalität. Die beiden STE's sind jeweils ungebunden und die Datensatzänderung, die aufgrund der Änderung der Daten in den benannten STE's passiert habe ich noch nicht eingearbeitet. Kann das ein Grund dafür sein, dass der Code im Before-Update-Ereignis des Formulars nicht angewendet wird?
Hier mein Code:
Private Sub Form_BeforeUpdate(Cancel As Integer)
    Select Case True
        Case IsNull(Me.txtZugabeMenge.Value) And IsNull(Me.txtZugabeEinheit.Value): MsgBox "Bitte eine Zugabemenge und eine Zugabeinheit eingeben"
        Case IsNull(Me.txtZugabeMenge.Value) And Not IsNull(Me.txtZugabeEinheit.Value): MsgBox "Bitte eine Zugabemenge eingeben"
        Case Me.txtZugabeMenge.Value = 0 And IsNull(Me.txtZugabeEinheit.Value): MsgBox "Bitte eine Zugabemenge > 0 und eine Zugabeinheit eingeben"
        Case Me.txtZugabeMenge.Value = 0 And Not IsNull(Me.txtZugabeEinheit.Value): MsgBox "Bitte eine Zugabemenge > 0 eingeben"
'        Case Not IsNull(Me.txtZugabeMenge.Value) And IsNull(Me.txtZugabeEinheit.Value): MsgBox "Bitte eine Zugabeeinheit eingeben"
        Case Not IsNull(Me.txtZugabeMenge.Value) And Not IsNull(Me.txtZugabeEinheit.Value): MsgBox "SQL-String wird befüllt"
        Cancel = True
    End Select
End Sub

PhilS

Zitat von: KonradR am November 07, 2024, 05:43:19Die beiden STE's sind jeweils ungebunden und die Datensatzänderung, die aufgrund der Änderung der Daten in den benannten STE's passiert habe ich noch nicht eingearbeitet.
Das BeforeUpdate-Ereignis des Formulars tritt ein, unmittelbar bevor der Datensatz, der in den gebundenen Steuerelementen angezeigt wird, gespeichert wird. - Ohne Änderung, kein BeforeUpdate.

Wenn die Datenänderungen gar nicht über gebundene Steuerelemente passieren, dass sollte die Prüfung laufen, bevor du anderweitig den Datensatz speicherst. In diesem, für Access eher ungewöhnlichen, Fall ist BeforeUpdate nicht hilfreich.
Neue Videoserie: Windows API in VBA

Klassische CommandBars visuell bearbeiten: Access DevTools CommandBar Editor

markusxy

Beim Einfügen musst du das Form.BeforeInsert Event verwenden.
Also einfach aus beiden Events heraus den Code ansprechen.
Die Prüfung schaut auch eher grauenvoll aus...

PhilS

Zitat von: markusxy am November 07, 2024, 16:16:59Beim Einfügen musst du das Form.BeforeInsert Event verwenden.
Nicht wirklich. Jedenfalls nicht zum Zweck der Eingabevalidierung.
BeforeInsert tritt ein, wenn in einem neuen, noch leeren Datensatz das allererste Zeichen eingegeben wird. - Viel zu früh für eine sinnvolle Validierung.
BeforeUpdate wird auch bei neuen Datensätzen ausgelöst, wenn der neuen Datensatz gespeichert werden soll.
Neue Videoserie: Windows API in VBA

Klassische CommandBars visuell bearbeiten: Access DevTools CommandBar Editor

KonradR

Zitat von: markusxy am November 07, 2024, 16:16:59Die Prüfung schaut auch eher grauenvoll aus...
Bitte beschreibe das doch etwas genauer. Denn ich finde den Code sehr übersichtlich. Von welchem Standpunkt aus betrachtest du den Code? Wie wäre es denn, wenn der Code deinen Qualitätsanforderungen entspricht?

knobbi38

Hallo Konrad,

ganz einfach, weil für eine Validierung jede Regel geprüft werden muß. Trifft bei einem Select ein Case Fall zu, werden die anderen nicht mehr bearbeitet bzw. geprüft.

KonradR

Zitat von: knobbi38 am November 08, 2024, 11:37:44Trifft bei einem Select ein Case Fall zu, werden die anderen nicht mehr bearbeitet bzw. geprüft.
Also müsste man das dann mit dem "Or" Operator in einer Zeile Code lösen oder mit der Gültigkeitsregel bzw. Gültigkeitsmeldung für ein Textfeld einer Tabelle lösen, damit alle möglichen Fälle durchlaufen werden?

knobbi38

Im einfachsten Fall für jede Regel ein IF Statement, also
IF Regel1 THEN
  ...
END IF

IF Regel2 THEN
  ...
END IF
...
oder man fast eine paar Prüfbedingungen zusammen und kaskadiert das etwas:
IF IsNull(Me.txtZugabeMenge.Value) THEN
  IF IsNull(Me.txtZugabeEinheit.Value) THEN
    ...
  ELSE
    ...
  END IF
END IF
Wenn dann noch anstatt eines fixen Textes eine Message_ID verwendet wird, kann der Meldungstext am Ende der IF Blöcke mit dieser Message_ID aus einer Tabelle mit DLookup geladen und ausgegeben werden. So hat man nur noch einen einzelnen Block mit der Meldungsausgabe und nicht verstreut überall 'Msgbox()' im Code. Außderdem können die Texte so in den "Stammdaten" angepaßt werden, ohne das der Code neu angefasst werden muß.

Grüße
Knobbi38


KonradR

Zitat von: knobbi38 am November 08, 2024, 15:18:24Im einfachsten Fall für jede Regel ein IF Statement, also
Code [Auswählen] Erweitern
IF Regel1 THEN
  ...
END IF

IF Regel2 THEN
  ...
END IF
...
Dann wird wirklich jeder Fall durchlaufen. Der einzige Nutzen davon erschließt sich für mich, dass wenn mehrere Regeln erfüllt sind, am Ende eine MSG-Box alle Regeln oder Fehleingaben für den Nutzer aufzeigt und er nicht jeden Fehler einzeln abarbeiten muss. Ist das der Nutzen von diesem Vorgehen?
Zitat von: knobbi38 am November 08, 2024, 15:18:24Wenn dann noch anstatt eines fixen Textes eine Message_ID verwendet wird, kann der Meldungstext am Ende der IF Blöcke mit dieser Message_ID aus einer Tabelle mit DLookup geladen und ausgegeben werden
Da werde ich mal drüber nachdenken. Allerdings sind die MSG-Boxen dann isoliert vom Code irgendwo zentral angeordnet. Diese dann zuzuordnen braucht auch wieder Zeit. Es sei denn es wird eindeutig kommentiert. Spart das so viel Zeit in der Wartung?
 

knobbi38

Hallo Konrad,

wichtig ist doch, daß alle Regeln mit einbezogen werden müssen. Es kann aber durchaus auch eine Priorisierung geben, nach dem Motto, wenn diese Regel nicht erfüllt ist, brauchen die anderen sowieso nicht mehr geprüft werden.

Neben den einfachen IF Blöcken stehen auch noch weiterhin die Werkzeuge IF-THEN-ELSEIF und SELECT-CASE zur Verfügung. Wie du das geschickt umsetzt, bleibt schlichtweg deiner Kreativität überlassen.

ZitatAllerdings sind die MSG-Boxen dann isoliert vom Code irgendwo zentral angeordnet. Diese dann zuzuordnen braucht auch wieder Zeit. Es sei denn es wird eindeutig kommentiert. Spart das so viel Zeit in der Wartung?

Nicht "die MSG-Boxen" sondern nur ein Codesegment mit einer Msgbox, eventuell sogar eine globale Funktion. Da wird auch nichts mehr zugeordnet, sondern die Meldung aus der Tabelle geladen und angezeigt. Dies kann auch erweitert werden, sodass mehrere Meldungstexte ausgegeben werden können. Beispielsweise können die Message-IDs in einer Collection gesammelt und dann die Meldungstexte dazu in einem Rutsch ausgegeben werden.

Gruß
Knobbi38

KonradR

Zitat von: knobbi38 am November 08, 2024, 22:57:55wichtig ist doch, daß alle Regeln mit einbezogen werden müssen.
Wenn der Nutzer im Before-Update-Ereignis eine Regel erfüllt und eine Meldung oder eine Aktion erfolgt, reicht das doch. Wenn er z.B. nichts in das Feld eingetragen hat und der Wert NULL ist, dann reicht doch eine Meldung "bitte etwas eintragen". Wenn ein oder mehrere Leerzeichen drin sind, dann ist es doch besser, wenn er eine Meldung bekommt, "Es  sind Leerzeichen im STE. Bitte eine Zahl außer 0 eintragen." oder so ähnlich erhält. Für jede Fehleingabe die entsprechende Meldung. Es kann nur ein Fall davon auftreten. Daher ist es aus meiner Sicht absolut ok, wenn der Code bei der entsprechenden Regel abbricht und die gewünschte Aktion ausführt.
Zitat von: knobbi38 am November 08, 2024, 22:57:55Beispielsweise können die Message-IDs in einer Collection gesammelt und dann die Meldungstexte dazu in einem Rutsch ausgegeben werden
Ok, dann habe ich die Meldungstexte für die MSG-Boxen in einer globalen Funktion und weis, dass wenn ich den von einer MSG-Box ändern will, dass nur in dieser globalen Function machen kann. Dann muss ich für die Anpassung immer in diese globale Function wechseln. Bei der Übergabe von Werten erscheint mir das sinnvoll, weil ich mir dann den Einsatz von öffentlichen Variablen spare und damit eine Verwechslungsgefahr bzw. Doppelbelegung umgehe, aber in diesem Fall wäre der Meldungstext an einer anderen Stelle. So wie ich es jetzt habe, kann ich ihn direkt an der Codestelle anpassen, wo die Regelprüfung stattfindet. Das erscheint mir komfortabler in der Wartung, weil ich die ID für den Meldungstext in der globalen Function in einem anderen öffentlichen Modul nicht suchen muss.

knobbi38

Hallo Konrad,

hier liegt ein Missverständnis vor. Die Meldungstexte stehen nicht in einer globalen Funktion, sondern die globale Funktion zeigt den zu den MessageID(s) gehörenden Meldungstext an. Die Meldungstexte selber stehen, wir haben immerhin ein Datenbank, natürlich in einer Tabelle mit der MessageID als PK. Du kannst also die Meldungstexte ändern, ohne das der Code neu kompiliert werden muß.

Es ist eine "Good Practice", solche Konstanten nicht im Code selber zu pflegen, sondern zentral in Modulen oder Tabellen. Diese lassen sich besser Warten und ggf. auch austauschen.


KonradR

Zitat von: knobbi38 am November 09, 2024, 11:27:27Die Meldungstexte selber stehen, wir haben immerhin ein Datenbank, natürlich in einer Tabelle mit der MessageID als PK. Du kannst also die Meldungstexte ändern, ohne das der Code neu kompiliert werden muß
Das habe ich tatsächlich missverstanden. Danke für den Hinweis. Das es natürlich nützlich ist, öfters genutzte Informationen einmal zentral abzulegen, macht für mich absolut Sinn. Dann wird die Information einmal zentral, hier in der Tabelle, einmal geändert und kann dann überall im Projekt verwendet werden. Das ist sehr komfortabel. Allerdings finde ich meinen Code aktuell hier sehr spezifisch auf zwei STE's programmiert. Wenn ich eine weitere Validierungsprüfung in einem anderen Formular mache, könnten auch mehr oder weniger als zwei STE's von der Validierung betroffen sein. Dann könnte ich natürlich den Namen der STE's in Variablen unterbringen und weitere Schleifen, etc. durchlaufen, aber aktuell will ich es mal dabei belassen und behalte es im Hinterkopf. Spätestens, wenn eine weitere Validierung in einem weiteren Formular dazu kommt, werde ich die von dir emfohlene Variante zu Anwendung bringen. Aktuell habe ich die Empfehlung von Phils eingearbeitet und den Code im Before-Update-Ereignis des Formulars untergebracht. Das funktioniert sehr gut, wie hier zu sehen:
Private Sub Form_BeforeUpdate(Cancel As Integer)
    Select Case True
        Case IsNull(Me.txtZugabeMenge.Value) And IsNull(Me.txtZugabeEinheit.Value)
            MsgBox "Es sind Leerzeichen in den Textfeldern ""Zugabemenge"" und ""Zugabeeinheit""" & vbCrLf & _
            "Bitte in beide Felder einen Wert eintragen."
            Cancel = True
        Case IsNull(Me.txtZugabeMenge.Value) And Me.txtZugabeEinheit.Value <> ""
            MsgBox "Es sind Leerzeichen im Textfeld ""Zugabemenge"". Bitte einen Wert eintragen."
            Cancel = True
        Case IsNull(Me.txtZugabeMenge.Value) And Not IsNull(Me.txtZugabeEinheit.Value)
            MsgBox "Bitte eine Zugabemenge eingeben"
            Cancel = True
        Case Me.txtZugabeMenge.Value = 0 And IsNull(Me.txtZugabeEinheit.Value)
            MsgBox "Bitte eine Zugabemenge > 0 und eine Zugabeinheit eingeben"
            Cancel = True
        Case Me.txtZugabeMenge.Value = 0 And Not IsNull(Me.txtZugabeEinheit.Value)
            MsgBox "Bitte eine Zugabemenge > 0 eingeben"
            Cancel = True
'        Case Not IsNull(Me.txtZugabeMenge.Value) And IsNull(Me.txtZugabeEinheit.Value): MsgBox "Bitte eine Zugabeeinheit eingeben"
            Cancel = True
        Case Not IsNull(Me.txtZugabeMenge.Value) And Not IsNull(Me.txtZugabeEinheit.Value)
            MsgBox "SQL-String wird befüllt"
            Cancel = False
        Case Else
            Cancel = False
            MsgBox "Case Else"
    End Select
End Sub
Danke für eure Hilfe. Das hat mich ein großes Stück weiter gebracht. Ok, die Validierung wird natürlich an der Stelle abgebrochen wo die Case-Anweisung zutrifft, weil ich weiter bei der Select-Case-Anwendung geblieben bin. Aber aktuell scheint das für mich ok. Wenn ich einen Fall habe, wo alle Kriterien geprüft werden müssen, werde ich die einzelnen If-Anweisungen für jede Fallprüfung verwenden.

PhilS



Nur mal am Rande angemerkt:
Aus meiner Sicht kannst du die komplette Prüfung auf das Folgende reduzieren und verlierst dabei weder Prüfabdeckung noch Informationsgehalt für den Benutzer.
If Nz(Me.txtZugabeMenge.Value,0) > 0 And Not IsNull(Me.txtZugabeEinheit.Value)
   MsgBox "SQL-String wird befüllt"
Else  
   MsgBox "Bitte eine Zugabemenge > 0 und eine Zugabeinheit eingeben"
   Cancel = True
End If

Neue Videoserie: Windows API in VBA

Klassische CommandBars visuell bearbeiten: Access DevTools CommandBar Editor