Hallo liebe Accessfreunde,
ich habe ein Pop-up-Formular mit mehreren gebundenen Textfeldern. Die Datensatzquelle des Pop-Up-Formulars wird anhand der Auswahl eines Kombinationsfeldes vom vorherigen Einzelformular gefiltert. Wenn ich jetzt im Pop-Up-Formular in das Textfeld "txtZugabeMenge" einen Wert eingebe, wird dieser in der verknüpften Datensatzquelle bzw. Abfrage nicht aktualisiert. Wenn ich den Wert jedoch in dem Feld in der Abfrage eingebe, wird dieser im Pop-Up-Formular sofort aktualisiert. Wenn ich das Pop-Up-Formular schließe und es anschließend wieder öffne, steht der neu eingegebene Wert im o.g. Textfeld und die Abfrage ist aktualisiert. Wenn ich nach der Eingabe im o.g. Textfeld auf dem Pop-Up-Formular in der Abfrage auf "alle aktualisieren" klicke, ändert sich der eingegebene Wert auch nicht. Habt ihr eine Idee, woran das liegen kann, dass die Aktualisierung der Daten nur nach dem Schließen und anschließenden Öffnen des Pop-Up-Formulars funktioniert?
Hallo,
das Formular (bzw. die Datenquelle) muss mit
Me.Requery (VBA) aktualisiert werden.
Hallo Konrad,
was meinst du denn mit
Zitat... in der verknüpften Datensatzquelle bzw. Abfrage
?
Bevor andere Objekten auf deine Eingabe zugreifen können, muß diese natürlich erst in die DB geschrieben worden sein. Beim Schließen eines Formulars passiert das automatisch, ansonsten mußt du das händisch per Code veranlasssen.
Genau, am Einfachsten direkt nach der Eingabe
Private txtZugabeMenge_AfterUpdate()
Me.Dirty = False
End Sub
Hallo,
eine der schönen Automatismus-Funktionen von Access ist, daß ein Undo eingebaut ist.
Es ist eine ätzende Methode moderner Software (wie z.B. in den Windows-Systemeinstellungen), nach einer Änderung sofort alles zu speichern. Wenn man auf einer Einstellungsseite (oder eben einem Datensatz) diverse, ungespeicherte Einstellungen hat, und einem dann auffällt, das man das doch nicht speichern will, kann man eben in Windows (früher) einfach nicht speichern bzw. in Access ESC drücken, einmal für das aktuelle Feld, zweimal für den ganzen Datensatz. Und dann ist auf der DB nix passiert.
Das sofortige Speichern verhindert die Undo-Möglichkeit, man muß dann im Zweifelsfall alles manuell zurückdrehen, wenn man denn noch weiß, was vorher dringestanden hat in den Feldern.
Ganz nebenbei verhindert man so auch, eine datensatzweise Validierung durchführen zu können mit Form_BeforeUpdate. Oft sind Daten erst im Zusammenhang mit anderen Feldern gültig oder nicht und das kann man hier sehr gut austesten und den Datensatz speichern oder ablehnen.
Wenn man aber nach jedem Feld den Datensatz speichert, ist das nicht möglich und führt dann z.B. dazu, daß andere Felder als falsch markiert wurden. Etwa hat man den Vornamen eingegeben, wird sofort der Nachname als falsch (weil fehlend) markiert, obwohl man den noch gar nicht eingegeben hat. Diesen Blödsinn sieht man leider mittlerweile auch auf etlichen Formular-Websites. Es genügt, Felder als notwendig zu bezeichnen und erst dann als Fehler anzuzeigen, wenn man versucht zu speichern. Datensatzvalidierung eben.
Gruß
Christian
Zitat von: MzKlMu am Januar 11, 2025, 11:28:25Hallo,
das Formular (bzw. die Datenquelle) muss mit
Me.Requery (VBA) aktualisiert werden.
Danke, das war die Lösung. Die Aktualisierung wird an die Abfrage weitergegeben.
Zitat von: knobbi38 am Januar 11, 2025, 12:03:24was meinst du denn mit
Damit meine ich, dass das Formular eine Abfrage als Datensatzquelle hat und das diese nicht aktualisiert wird, wenn ich in dem benannten Textfeld einen anderen Wert reinschreibe.
Zitat von: knobbi38 am Januar 11, 2025, 12:03:24Bevor andere Objekten auf deine Eingabe zugreifen können, muß diese natürlich erst in die DB geschrieben worden sein. Beim Schließen eines Formulars passiert das automatisch, ansonsten mußt du das händisch per Code veranlasssen.
Das ist wahrscheinlich das von Klaus erwähnte Me.Requery.
Zitat von: Beaker s.a. am Januar 11, 2025, 12:05:11Genau, am Einfachsten direkt nach der Eingabe
Private txtZugabeMenge_AfterUpdate()
Me.Dirty = False
End Sub
Danke, dass funktioniert auch.
Zitat von: Bitsqueezer am Januar 11, 2025, 14:57:45Ganz nebenbei verhindert man so auch, eine datensatzweise Validierung durchführen zu können mit Form_BeforeUpdate.
Ist das jetzt gut oder schlecht? Denn aktuell habe ich ein neues Problem. Ich habe nämlich eine Datensatzvalidierung im
Form_BeforeUpdate
Ereignis, weil ich zwei Felder auf korrekte Eingabe prüfen will, bevor der gewünschte Vorgang, den ich per VBA programmiert habe beginnt. Die Fehlermeldung heißt: "Laufzeitfehler '2185': Sie können auf die Eigenschaften oder Methoden eines Steuerelements nur verweisen, wenn das Steuerelement den Fokus hat." Wenn ich sämtlichen Code im
Form_BeforeUpdate
Ereignis auskommentiere funktioniert es.
Hallo,
also ich persönlich sage, es ist schlecht, wenn man jede Änderung sofort speichert. Weil man Undo verhindert und weil Form_BeforeUpdate u.a. dazu da ist, alle Felder im Zusammenhang testen zu können.
Deine Fehlermeldung bedeutet vermutlich, daß Du auf die Eigenschaft "Text" verweist, die nur gültig ist, solange ein Control den Fokus hat. Das ist der Inhalt der Eingabe während der Änderung, ansonsten ist es "Value".
Aber dazu müßtest Du schon mal den Code aus Form_BeforeUpdate zeigen.
Klar, wenn man Code deaktiviert, dann gibt es ja auch nichts, was "nicht funktionieren" könnte.
Gruß
Christian
Hallo Bitsqueezer,
Zitat von: Bitsqueezer am Januar 11, 2025, 16:48:36und weil Form_BeforeUpdate u.a. dazu da ist, alle Felder im Zusammenhang testen zu können.
Deswegen habe ich ja das Form_BeforeUpdate Ereignis genutzt.
Zitat von: Bitsqueezer am Januar 11, 2025, 16:48:36Deine Fehlermeldung bedeutet vermutlich, daß Du auf die Eigenschaft "Text" verweist, die nur gültig ist, solange ein Control den Fokus hat. Das ist der Inhalt der Eingabe während der Änderung, ansonsten ist es "Value".
Wow. Du hast meinen Code noch nicht gesehen und weist, wo der Fehler liegt. Exakt an dieser Stelle gibt der Kompiler die Fehlermeldung aus. Hier mein Code, der etwas länger geworden ist:
Private Sub Form_BeforeUpdate(Cancel As Integer)
'MsgBox "Form_BeforeUpdate(Cancel As Integer)"
'_______________________________________________________________________
'EINGABEPRÜFUNG VON STEUERELEMENT txtZugabeMenge UND cboZugabeEinheit
Select Case True
Case IsNull(Me.txtZugabeMenge.Value) And IsNull(Me.cboZugabeEinheit.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.cboZugabeEinheit.Value <> ""
MsgBox "Es sind Leerzeichen im Textfeld ""Zugabemenge"". Bitte einen Wert eintragen."
Cancel = True
Case IsNull(Me.txtZugabeMenge.Value) And Not IsNull(Me.cboZugabeEinheit.Value)
MsgBox "Bitte eine Zugabemenge eingeben"
Cancel = True
Case Me.txtZugabeMenge.Value = 0 And IsNull(Me.cboZugabeEinheit.Value)
MsgBox "Bitte eine Zugabemenge > 0 und eine Zugabeinheit eingeben"
Cancel = True
Case Me.txtZugabeMenge.Value = 0 And Not IsNull(Me.cboZugabeEinheit.Value)
MsgBox "Bitte eine Zugabemenge > 0 eingeben"
Cancel = True
' Case Not IsNull(Me.txtZugabeMenge.Value) And IsNull(Me.cboZugabeEinheit.Value): MsgBox "Bitte eine Zugabeeinheit eingeben"
Cancel = True
Case Not IsNull(Me.txtZugabeMenge.Value) And Not IsNull(Me.cboZugabeEinheit.Value)
Dim ZEWert As Integer 'Variable f. Wert des Feldes cboZugabeEinheit deklariern
Dim rcsZugabeEinheit As DAO.Recordset 'Variable f. Recordset m.d. Tabelle tblRezeptGewicht deklariern
Dim rcsEinheiten As DAO.Recordset 'Variable f. Recordset m.d. Tabelle tblGewichtsEinheitenName deklariern
Dim ZEText As String 'Variable f. Text des Feldes ZugabeEinheit deklariern
Dim RezIDRef As Integer 'Variable f. Zahl die in txtRezepID drin steht deklariern
Dim db As DAO.Database '"db" als Variable für eine Datenbank aus der DAO Bibliothek deklarieren - aktuell keine Verwendung
Dim lngZutatSammlID As Long '"lngZutatSammlID" als Variable für das 1. Schlüsselfeld des zu kopierenden DS deklarieren (Acces selbst vergeben lassen weil Autowert & sonst doppelt & Fehlermeldung)
Dim lngZutatSammlIDNeu As Long '"lngZutatSammlIDNeu" als leere Variable zur Übergabe der DS-Nummer deklarieren auf den nach der Akualisierung gesprungen werden soll
Dim lngZutatSammlLaMaZIDRef As Long '"lngZutatSammlIDNeu" als Variable für das 2. Schlüssslfeld des zu kopierenden DS deklarieren (Acces selbst vergeben lassen weil Autowert & sonst doppelt)
Dim lngZutatSammlRezepIDRef As Long 'Variable als Übergabe für den Wert des Fremdschlüsselfeldes ZutatSammlRezepIDRef (damit alle Zutaten des Rezepts aufgelistet werden)
Dim lngZutatSammlRezepIDRef2 As Long 'Variable als Übergabe für den Wert des Fremdschlüsselfeldes für ZutatSammlRezepIDRef (damit alle Zutaten in den Zielrecordset kopiert werden)
Dim SQLRcsZiel As String '"SQL String für den Inhalt des Recordsets für das 2. Unterformular in "sfrmPopRezeptAlsZutatUnter2"
Dim rcsRezeptZutatenGesamt As DAO.Recordset
Dim rcsRezeptZutatenGpEinzeln As DAO.Recordset
Dim rcsRezeptZutatenGpEinzelnGefiltert As DAO.Recordset
Dim fld As Field
Dim lngZutatNr As Long
Dim rcsZutatStammName As DAO.Recordset
Dim lngZutatSammlIDUfo1 As Integer 'Variable für den Wert 1 für das Textfeld "txtRotAn" deklarieren. Damit mit bedingter Formatierung der Hintergrund aller STE rot gefärbt
'werden kann
ZEWert = Forms("frmPopRezeptAlsZutat").Controls("cboZugabeEinheit").Value 'Variablen f. ZEWert den Wert zuweisen
ZEText = Forms("frmPopRezeptAlsZutat").Controls("cboZugabeEinheit").Text 'Variablen f. ZEText den Wert zuweisen
RezIDRef = Forms("frmPopRezeptAlsZutat").Controls("txtRezepID").Value 'Variablen f. RezIDRef den Wert zuweisen
Set rcsZugabeEinheit = CurrentDb.OpenRecordset("SELECT * FROM tblRezeptGewicht WHERE rezgeEhIDRef = " & ZEWert & " AND rezgeRezepIDRef = " & RezIDRef, dbOpenDynaset)
'Recordset der Variablen rcsZugabeEinheit zuweisen: Alle Felder aus der Tabelle tblRezeptGewicht, bei denen der Wert aus dem Feld cboZugabeEinheit mit dem
'Tabellenfeld rezgeEhIDRef (Feld f. ID d. Fremdschlüsselfeldes f.d. Einheit) übereinstimmt UND
'der Fremdschlüssel zur tblRezept mit dem Wert in txtRezepID (Variable: RezIDRef) übereinstimmt
'kurz: alle DS zu der ausgewählten Einheit im cbo cboZugabeEinheit für das Rezept mit der Nummer aus dem Textfeld txtRezepID im RS auflisten
'kurz2: rcsZugabeEinheit ist der Recordset für die Sammlung der Gewichte der einzelnen Rezepteinheiten des bestimmten Rezepts
Set rcsEinheiten = CurrentDb.OpenRecordset("SELECT * FROM tblGewichtsEinheitenName WHERE GweNameID = " & ZEWert, dbOpenDynaset)
'Recordset der Variablen rcsEinheiten zuweisen: alle Felder der Tabelle tblGewichtsEinheitenName bei denen das Feld GweNameID mit dem Wert des Textfeldes cboZugabeEinheit
'(Variable: ZEWert) übereinstimmt
'______________________________________________________________________________________________________________
Der Fehler tritt an der Zeile:
ZEText = Forms("frmPopRezeptAlsZutat").Controls("cboZugabeEinheit").Text 'Variablen f. ZEText den Wert zuweisen
auf. Ich habe nicht den kompletten Kode eingefügt, weil ich sonst über die 20.000 Zeichen für einen Beitrag komme.
Hallo Konrad,
ZitatDamit meine ich, dass das Formular eine Abfrage als Datensatzquelle hat und das diese nicht aktualisiert wird, wenn ich in dem benannten Textfeld einen anderen Wert reinschreibe.
das gibt für mich keinen Sinn. Wieso sollte sich einen Abfrage aktualisieren müssen, wenn ein Textfeld geändert wird?
Da passt etwas mit der Programmlogik nicht und auch, was Christian schon beschrieben hat, sollte man berücksichtigen.
Wenn das dein Code für Form_BeforeUpdate ist, solltest du das Formular und deinen Workflow nochmal überdenken. Was soll das ganze Gedöns mit den Deklarationen und Recordsets? Da ist mehr im Argen, als nur die Frage, ob und wann gespeichert werden soll.
Btw.:
ZitatZEText = Forms("frmPopRezeptAlsZutat").Controls("cboZugabeEinheit").Text
Solche Zuweisungen sollten grundsätzlich vermieden werden, denn die Werte können per Openargs beim Öffnen des Popups übergeben werden. Mal abgesehen davon, daß die .Text Eigenschaft hier falsch verwendet wird. Bis auf wenige Ausnahmen wird in Access immer die .Value Eigenschaft verwendet.
ZitatDim lngZutatSammlIDUfo1 As Integer
Ist das ein Typo? Wenn schon Präfixe verwendet werden, sollten sie mit der Deklaration auch übereinstimmen.
Hallo,
also zu dem Code hätte ich auch noch so einige Anmerkungen, die die von Ulrich noch ergänzen.
- Text vs. Value hatten wir ja schon. Die Value-Eigenschaft ist normalerweise die Default-Eigenschaft, daher kann man sie auch einfach weglassen. In seltenen Fällen kann man schon mal etwas anderes bekommen als den Value, z.B. beim Zugriff auf die Fields eines Recordsets, je nach Art des Zugriffs erhältst Du dann das Feld selbst und nicht dessen Wert. Also wenn Du sichergehen möchtest, ist ".Value" zu schreiben, zumindest nicht falsch.
- wenn ich Dir einen Tip zu Usability geben darf: Form_BeforeUpdate sollte der "Fehlersammler" sein. Nicht der Code, der den User zur Weißglut nervt:
- Select Case kann immer nur ein True behandeln, beim ersten Case, das zutrifft, ist Ende (das ist nicht in allen Programmiersprachen so, aber in VBA)
- wenn man mehr als einen Fehler testen will, dann ist es besser, einzelne Ifs zu schreiben (die dann alle durchgegangen werden), und dann je aufgetretenem Fehler eine String-Variable zu erweitern, in der die Fehler gesammelt werden. Dann kann man am Ende eine gesammelte Fehlermeldung ausgeben.
- in Deiner aktuellen Version bekommt der User nur den ersten Fehler per Messagebox, er behebt ihn, versucht erneut zu speichern und bekommt den 2. Fehler usw., im schlimmsten Fall alle nacheinander. Das nervt gewaltig.
- Test auf leeres Feld: Ich empfehle Dir, statt "IsNull" besser Nz zu verwenden. Mit Nz kannst Du einen Austauschwert für NULL angeben, denn ein optisch leeres Feld muß nicht zwingend NULL sein, es kann auch ein Leerstring sein. Mit Nz ist "" immer der Standardaustauschwert, somit kannst Du testen "If Nz(Feld) = "" Then" und hast immer beide Varianten abgesichert. Der Rest ist von den Einstellungen des Feldes im Tabellendesign abhängig (ob Leereingaben zulässig sind).
- Integer: Sei vorsichtig mit der Verwendung von Integern. In anderen Programmiersprachen ist das ein 32Bit-Wert, in VBA ist es nur 16Bit. Das fällt bei Speicherung von ID-Werten erst mal nicht auf, bis Du über die +-Grenze von 32767 hinauskommst, was für IDs nicht allzuviel ist. Es gibt nahezu nie eine Einsatznotwendigkeit für Integer und in Zeiten von Gigabyte-RAM ist auch kein Speichersparen mehr notwendig. Daher am besten immer Long verwenden, dann bist Du im Allgemeinen immer auf der sicheren Seite (und ID-Felder von Tabellen können keine höheren Werte produzieren, außer im seltenen Fall, daß es ein Bigint ist, was in Access auch noch nicht so lange gibt).
- da das nicht der gesamte Code ist, kann man nur ahnen, was da noch alles kommen mag. Bei so vielen "Dim"s wird man aber schon skeptisch. Wenn Du schon selbst gesehen hast, daß da welche gar nicht gebraucht werden, kommentiere die Zeile am besten aus, dann kannst Du sie später auch gleich ganz rausnehmen
- Zugriff auf Felder: Die maximal umständlichste und dazu noch vom Compiler nicht prüfbare Syntax mit Zugriff per Namen in Strings. Da "frmPopRezeptAlsZutat" vermutlich das Formular ist, in dem Du den Code stehen hast, kannst Du auch ganz einfach "Me.cboZugabeEinheit" schreiben. Und das kann der Compiler dann auch prüfen (ob es das Control auch gibt).
- Zuweisung an eine String-Variable: Bitte beachte, daß außer "Variant" kein Variablentyp mit NULL umgehen kann.
- Wieso ist die Fehlermeldung zu: "IsNull(Me.txtZugabeMenge.Value) And IsNull(Me.cboZugabeEinheit.Value)", daß es Leerzeichen in den Feldern gibt? NULL steht für "Nichts".
- Wozu die Recordsets verwendet werden, schreibst Du nicht. Generell gilt:
- Auf "SELECT *" verzichten: Immer die genaue Feldliste angeben, die Du wirklich haben willst. Gründe dafür findest Du mit Google etc. leicht massenhaft. Zu oft aufgeschrieben, um es zum 1000.Mal zu wiederholen.
- Möglichst auf zusammengesetzte SQL-Strings verzichten. Dazu mal unter "SQL Injection" suchen. Das trifft zwar im Fall von Access nicht zu, da man in Access keine SQL-Batches schreiben kann, aber besser gleich richtig angewöhnen.
Da möchte ich lieber nicht wissen, wie der übrige Code wohl aussieht... ;)
Natürlich hat sicher jedes Modul, wie es jeder macht, "Option Explicit" als erste Zeile und Du nutzt ja auch sicher immer den Kompiler..... oder?
Gruß
Christian
Hallo knobbi38,
Zitat von: knobbi38 am Januar 11, 2025, 20:21:56Hallo Konrad,
ZitatDamit meine ich, dass das Formular eine Abfrage als Datensatzquelle hat und das diese nicht aktualisiert wird, wenn ich in dem benannten Textfeld einen anderen Wert reinschreibe.
das gibt für mich keinen Sinn. Wieso sollte sich einen Abfrage aktualisieren müssen, wenn ein Textfeld geändert wird?
Weil aufgrund der Eingabe der Daten im Textfeld, die gerade eingegeben wurden, die Abfrage geändert werden soll und damit Berechnungen in der Abfrage neu berechnet werden sollen. Die berechneten Werte will ich weiterverwenden. Wenn die Anpassung der Abfrage erst nach dem Schließen des Formulars erfolgt, ist die Berechnung auf Grundlage der Werte passiert, die vorher im Formular standen, als das Formular geöffnet wurde. Ich habe nun also berechnete Daten, auf Grundlage der Daten, die ich vorher und nicht auf Grundlage, die ich gerade eingegeben habe. Mein Ziel ist es ja, berechneten Daten aufgrundlage der gerade eben eingegeben Werte zu erhalten.
Zitat von: knobbi38 am Januar 11, 2025, 20:21:56ZitatCode [Auswählen] Erweitern
Dim lngZutatSammlIDUfo1 As Integer
Ist das ein Typo? Wenn schon Präfixe verwendet werden, sollten sie mit der Deklaration auch übereinstimmen.
Was ist ein Typo? Danke für den Hinweis mit dem Präfix.
Dim IntZutatSammlIDUfo1 As Integer
wäre wahrscheinlich besser gewesen.
Zitat von: knobbi38 am Januar 11, 2025, 20:21:56Btw.:
ZitatCode [Auswählen] Erweitern
ZEText = Forms("frmPopRezeptAlsZutat").Controls("cboZugabeEinheit").Text
Solche Zuweisungen sollten grundsätzlich vermieden werden, denn die Werte können per Openargs beim Öffnen des Popups übergeben werden. Mal abgesehen davon, daß die .Text Eigenschaft hier falsch verwendet wird. Bis auf wenige Ausnahmen wird in Access immer die .Value Eigenschaft verwendet.
Es geht darum, den Wert für eine MSG-Meldung weiter zu verwenden. Wenn ich die .Value-Eigenschaft verwende, ist der Wert bereits gespeichert. Bei der Text-Eigenschaft nicht. Ich will dem Nutzer ja vor der Speicherung die Meldung ausgeben, dass er gegebenenfalls etwas anderes eingeben soll.
Hallo Christian,
Zitat von: Bitsqueezer am Januar 11, 2025, 22:03:08Text vs. Value hatten wir ja schon. Die Value-Eigenschaft ist normalerweise die Default-Eigenschaft, daher kann man sie auch einfach weglassen. In seltenen Fällen kann man schon mal etwas anderes bekommen als den Value, z.B. beim Zugriff auf die Fields eines Recordsets, je nach Art des Zugriffs erhältst Du dann das Feld selbst und nicht dessen Wert. Also wenn Du sichergehen möchtest, ist ".Value" zu schreiben, zumindest nicht falsch.
Danke ja, Value reicht hier vollkommen aus. Ich habe es nochmal im Überwachungsfenster geprüft.
Zitat von: Bitsqueezer am Januar 11, 2025, 22:03:08wenn ich Dir einen Tip zu Usability geben darf: Form_BeforeUpdate sollte der "Fehlersammler" sein. Nicht der Code, der den User zur Weißglut nervt:
- Select Case kann immer nur ein True behandeln, beim ersten Case, das zutrifft, ist Ende (das ist nicht in allen Programmiersprachen so, aber in VBA)
- wenn man mehr als einen Fehler testen will, dann ist es besser, einzelne Ifs zu schreiben (die dann alle durchgegangen werden), und dann je aufgetretenem Fehler eine String-Variable zu erweitern, in der die Fehler gesammelt werden. Dann kann man am Ende eine gesammelte Fehlermeldung ausgeben.
- in Deiner aktuellen Version bekommt der User nur den ersten Fehler per Messagebox, er behebt ihn, versucht erneut zu speichern und bekommt den 2. Fehler usw., im schlimmsten Fall alle nacheinander. Das nervt gewaltig.
[/list]
Ich glaube, du bist jetzt der Dritte, der das hier im Forum anspricht. Ich werde es die Tage abändern. Das scheint wirklich ein wesentlicher Punkt zu sein.
Zitat von: Bitsqueezer am Januar 11, 2025, 22:03:08Test auf leeres Feld: Ich empfehle Dir, statt "IsNull" besser Nz zu verwenden. Mit Nz kannst Du einen Austauschwert für NULL angeben, denn ein optisch leeres Feld muß nicht zwingend NULL sein, es kann auch ein Leerstring sein. Mit Nz ist "" immer der Standardaustauschwert, somit kannst Du testen "If Nz(Feld) = "" Then" und hast immer beide Varianten abgesichert. Der Rest ist von den Einstellungen des Feldes im Tabellendesign abhängig (ob Leereingaben zulässig sind).
Sehr praktisch, vielen Dank. Da habe ich den Fall für einen Leerstring abgedeckt.
Zitat von: Bitsqueezer am Januar 11, 2025, 22:03:08Integer: Sei vorsichtig mit der Verwendung von Integern. In anderen Programmiersprachen ist das ein 32Bit-Wert, in VBA ist es nur 16Bit. Das fällt bei Speicherung von ID-Werten erst mal nicht auf, bis Du über die +-Grenze von 32767 hinauskommst, was für IDs nicht allzuviel ist. Es gibt nahezu nie eine Einsatznotwendigkeit für Integer und in Zeiten von Gigabyte-RAM ist auch kein Speichersparen mehr notwendig. Daher am besten immer Long verwenden, dann bist Du im Allgemeinen immer auf der sicheren Seite (und ID-Felder von Tabellen können keine höheren Werte produzieren, außer im seltenen Fall, daß es ein Bigint ist, was in Access auch noch nicht so lange gibt).
Ok, bei Integer ist das Ende eher erreicht. Hier ging es um eine Nachschlagetabelle für Rezepteinheiten. Da gibt es ja nicht so viele, dass ich über die Grenze von 32.767 herauskomme. Es sei denn, es soll eine Datenbank für alle Sprachen dieser Welt mit all deren Rezepteinheiten. Aber ja, Long ist sicherer. Dann werde ich das ändern.
Zitat von: Bitsqueezer am Januar 11, 2025, 22:03:08da das nicht der gesamte Code ist, kann man nur ahnen, was da noch alles kommen mag. Bei so vielen "Dim"s wird man aber schon skeptisch. Wenn Du schon selbst gesehen hast, daß da welche gar nicht gebraucht werden, kommentiere die Zeile am besten aus, dann kannst Du sie später auch gleich ganz rausnehmen
Absolut. Danke für den Tip. Ich finde, dass es manchmal Sinn macht eine Zeile noch auskommentiert im Code zu belassen. Wenn dann alles funktioniert, kann ich es löschen. Das schafft eine bessere Übersicht.
Zitat von: Bitsqueezer am Januar 11, 2025, 22:03:08Zugriff auf Felder: Die maximal umständlichste und dazu noch vom Compiler nicht prüfbare Syntax mit Zugriff per Namen in Strings. Da "frmPopRezeptAlsZutat" vermutlich das Formular ist, in dem Du den Code stehen hast, kannst Du auch ganz einfach "Me.cboZugabeEinheit" schreiben. Und das kann der Compiler dann auch prüfen (ob es das Control auch gibt).
Test auf leeres Feld: Ich empfehle Dir, statt "IsNull" besser Nz zu verwenden. Mit Nz kannst Du einen Austauschwert für NULL angeben, denn ein optisch leeres Feld muß nicht zwingend NULL sein, es kann auch ein Leerstring sein. Mit Nz ist "" immer der Standardaustauschwert, somit kannst Du testen "If Nz(Feld) = "" Then" und hast immer beide Varianten abgesichert. Der Rest ist von den Einstellungen des Feldes im Tabellendesign abhängig (ob Leereingaben zulässig sind). Das wurde hier im Forum schon des Öfteren angesprochen. Das habe ich bei aktuellem Code schon angepasst. Hier noch nicht. Allerdings ist der Vorteil von dieser Variant, dass Intellisens arbeiten kann. Gut, wenn ich den Formularnamen mit Form_ am Anfang reinschreibe, dann wirkt auch hier Intellisens und der Compiler kann es prüfen.
Zitat von: Bitsqueezer am Januar 11, 2025, 22:03:08Zuweisung an eine String-Variable: Bitte beachte, daß außer "Variant" kein Variablentyp mit NULL umgehen kann.
Danke. Das wusste ich noch nicht, aber hier brauche ich den Datentyp STRING tatsächlich.
Zitat von: Bitsqueezer am Januar 11, 2025, 22:03:08Wieso ist die Fehlermeldung zu: "IsNull(Me.txtZugabeMenge.Value) And IsNull(Me.cboZugabeEinheit.Value)", daß es Leerzeichen in den Feldern gibt? NULL steht für "Nichts".
Da ist meine Fehlermeldung tatsächlich falsch oder missverständlich. Das ändere ich noch ab. Danke für den Tip.
Zitat von: Bitsqueezer am Januar 11, 2025, 22:03:08Auf "SELECT *" verzichten: Immer die genaue Feldliste angeben, die Du wirklich haben willst. Gründe dafür findest Du mit Google etc. leicht massenhaft. Zu oft aufgeschrieben, um es zum 1000.Mal zu wiederholen.
Ich habe gerade mal nachgeschaut. Das führt zu einer besseren Leistung, weil nicht alle Daten abgerufen werden müssen. Das macht Sinn.
Zitat von: Bitsqueezer am Januar 11, 2025, 22:03:08Möglichst auf zusammengesetzte SQL-Strings verzichten. Dazu mal unter "SQL Injection" suchen. Das trifft zwar im Fall von Access nicht zu, da man in Access keine SQL-Batches schreiben kann, aber besser gleich richtig angewöhnen.
Das habe ich auch schon gehört. Aber wiedersprichst du dir da nicht im 1. Punkt? Da habe ich deinen Beitrag so verstanden, dass die Fehlermeldung in einen SQL-String übergeben werden soll. Wäre das dann nicht ein zusammengesetzter SQL-String bzw. wäre in diesem Fall, wenn es ein zusammengesetzter SQL-String ist, an dieser Stelle also sinnvoll?
Zitat von: Bitsqueezer am Januar 11, 2025, 22:03:08Natürlich hat sicher jedes Modul, wie es jeder macht, "Option Explicit" als erste Zeile und Du nutzt ja auch sicher immer den Kompiler..... oder?
Tatsächlich nutze ich den Compiler und in jedem Modul seht in der ersten Zeile "Option Explicit". Ich arbeite auch mit dem Überwachungsfenster. Sonst wüsste ich gar nicht, wie ich die Fehler alle finden soll.
Hallo,
ein Typo ist ein Tippfehler.
ZitatDas wurde hier im Forum schon des Öfteren angesprochen. Das habe ich bei aktuellem Code schon angepasst. Hier noch nicht. Allerdings ist der Vorteil von dieser Variant, dass Intellisens arbeiten kann. Gut, wenn ich den Formularnamen mit Form_ am Anfang reinschreibe, dann wirkt auch hier Intellisens und der Compiler kann es prüfen.
Wie kommst Du denn darauf?
Mit dieser Zugriffsvariante:
Forms("frmPopRezeptAlsZutat").Controls("cboZugabeEinheit")
kann Dir IntelliSense nicht weiterhelfen, es wird Dir nur die Standard-Eigenschaften und -Methoden eines Formulares listen, aber nicht z.B. Deine Controls darin. Das gleiche mit der Controls-Auflistung, die Du hier verwendest. Die Namen als Strings kannst der Compiler (und auch IntelliSense) nicht verarbeiten. Du könntest auch "Forms("MickyMaus")" schreiben und würdest keinen Fehler vom Compiler erhalten.
Die Syntax mit "Form_" ist mit Vorsicht zu genießen. Ja, geht, Du erhältst dann auch IntelliSense-Unterstützung mit den Controls, aber wenn man so auf ein Formular zugreift und es ist nicht geöffnet, wird es automatisch geöffnet. Das kann zu Problemen führen.
Wenn Du auf das "eigene" Formular zugreifst, dann immer "Me." verwenden.
Wenn Du auf ein anderes Formular zugreifst, und es compilersicher haben möchtest, dann kannst Du eine Klasseninstanzvariable verwenden. Zum Beispiel:
Dim frmMeinFormular As Form_MeinFormular
If CurrentProject.AllForms("MeinFormular").IsLoaded Then
Set frmMeinFormular = Forms("MeinFormular")
End If
Danach kannst Du "frmMeinFormular" wie "Me" verwenden, nur eben für das entsprechende Formular. Es schadet nichts, davor die Variable auf "Is Nothing" zu testen, falls das andere Formular eben nicht geladen war und somit hier nichts zugewiesen werden konnte.
ZitatDanke. Das wusste ich noch nicht, aber hier brauche ich den Datentyp STRING tatsächlich.
Was ja kein Problem ist, Du solltest es nur entsprechend abfangen. Heißt, das ganze mit Nz mit einem Leerstring-Austauschwert zu versehen, dann gibt es keine Fehlermeldung, wenn mal NULL dabei herauskommt.
ZitatIch habe gerade mal nachgeschaut. Das führt zu einer besseren Leistung, weil nicht alle Daten abgerufen werden müssen. Das macht Sinn.
Das ist auf jeden Fall schon mal ein wichtiger Grund.
ZitatDas habe ich auch schon gehört. Aber wiedersprichst du dir da nicht im 1. Punkt? Da habe ich deinen Beitrag so verstanden, dass die Fehlermeldung in einen SQL-String übergeben werden soll. Wäre das dann nicht ein zusammengesetzter SQL-String bzw. wäre in diesem Fall, wenn es ein zusammengesetzter SQL-String ist, an dieser Stelle also sinnvoll?
Nein, Du mußt nochmal genau nachlesen.
Im ersten Fall ging es darum, statt einzelner Messageboxen und einzelner Fehlerprüfung alle Fehler zu testen und dann einen Fehlerstring zusammenzusetzen aus allen gefundenen Fehlern und die gesamte Fehlernachricht mit allen gefundenen Fehlern dem Benutzer nur einmal anzuzeigen, so daß er alles korrigieren und dann erfolgreich speichern kann.
Im Fall eines SQL-Strings geht es darum, ein SQL-Kommando als String zusammenzubasteln, und das als Kommando auszuführen. Eine simple Methode, wenn z.B. im WHERE-Teil nach einem String getestet wird, dessen Wert im SQL-String mit "" oder '' umschlossen wird, ist, das als Eingabewert im Feld zu schreiben. Also im ersten Fall ein " und im zweiten Fall ein '.
Da Du die Eingabewerte direkt in das Kommando einbaust, schließt diese Eingabe den String. Das wird bei SQL Injection z.B. gern verwendet, um dann ein Semikolon als Befehlsende einzubauen und einen eigenen SQL-Befehl in das Eingabefeld zu schreiben, z.B. DELETE * FROM tblMeineTabelle". Wie gesagt, mangels Batchfähigkeit geht das in Access-SQL nicht, aber wenn Du das etwa mit ADO auf einem SQL Server ausführst, sehr wohl.
ZitatTatsächlich nutze ich den Compiler und in jedem Modul seht in der ersten Zeile "Option Explicit". Ich arbeite auch mit dem Überwachungsfenster. Sonst wüsste ich gar nicht, wie ich die Fehler alle finden soll.
Das ist auf jeden Fall eine gute Grundlage. Und damit Du daran nicht denken mußt, "Variablendeklaration erforderlich" in den VBA-Editor-Optionen anhaken, dann wird es automatisch bei jedem neuen Modul eingefügt.
Ich empfehle auch, MZTools zu erwerben, das ist nicht teuer und muß nicht ständig neu lizenziert werden. Hier sind etliche Tools drin, die Dir das Leben erleichtern, u.a. auch VBA-Checker, die Deinen Code auf Regeln überprüfen können, wobei Du auch eigene hinzufügen kannst.
Gruß
Christian
Hallo Christian,
Zitat von: Bitsqueezer am Januar 12, 2025, 13:31:31Mit dieser Zugriffsvariante:
Code [Auswählen] Erweitern
Forms("frmPopRezeptAlsZutat").Controls("cboZugabeEinheit")
kann Dir IntelliSense nicht weiterhelfen, es wird Dir nur die Standard-Eigenschaften und -Methoden eines Formulares listen, aber nicht z.B. Deine Controls darin.
Das ist was ich mit, Hilfe von Intelisense, meinte. Die Formularnamen werden nicht angezeigt. Die muss ich eingeben oder reinkopieren. Das ist eine Fehlerquelle. Absolut.
Zitat von: Bitsqueezer am Januar 12, 2025, 13:31:31Wenn Du auf das "eigene" Formular zugreifst, dann immer "Me." verwenden.
Wenn Du auf ein anderes Formular zugreifst, und es compilersicher haben möchtest, dann kannst Du eine Klasseninstanzvariable verwenden. Zum Beispiel:
Code [Auswählen] Erweitern
Dim frmMeinFormular As Form_MeinFormular
If CurrentProject.AllForms("MeinFormular").IsLoaded Then
Set frmMeinFormular = Forms("MeinFormular")
End If
Danach kannst Du "frmMeinFormular" wie "Me" verwenden, nur eben für das entsprechende Formular. Es schadet nichts, davor die Variable auf "Is Nothing" zu testen, falls das andere Formular eben nicht geladen war und somit hier nichts zugewiesen werden konnte.
Cool, wieder was gelernt. Vielen Dank.
Zitat von: Bitsqueezer am Januar 12, 2025, 13:31:31Das wird bei SQL Injection z.B. gern verwendet, um dann ein Semikolon als Befehlsende einzubauen und einen eigenen SQL-Befehl in das Eingabefeld zu schreiben, z.B. DELETE * FROM tblMeineTabelle". Wie gesagt, mangels Batchfähigkeit geht das in Access-SQL nicht, aber wenn Du das etwa mit ADO auf einem SQL Server ausführst, sehr wohl.
Alles klar. Hier kann also der SQL-String und damit seine Ausführung direkt verändert werden. Damit können Daten gelöscht werden etc.. Das kann also ganz schön gefährlich werden.
Zitat von: Bitsqueezer am Januar 12, 2025, 13:31:31"Variablendeklaration erforderlich" in den VBA-Editor-Optionen anhaken
Check. Das habe ich immer angehakt. Danke nochmal.
Zitat von: Bitsqueezer am Januar 12, 2025, 13:31:31Ich empfehle auch, MZTools zu erwerben, das ist nicht teuer und muß nicht ständig neu lizenziert werden
Danke für den Tip. Das schaue ich mir mal an.