Datensatzänderungen auf einen Blick

Wenn der Benutzer änderungen an den in einem Formular angezeigten Daten vornimmt, sieht er nur noch die geänderten Daten. Was aber, wenn er beispielsweise einen Wert in ein falsches Feld eingetragen hat – zum Beispiel indem er Vorname und Nachname vertauscht Dann möchte er die änderung vermutlich rückgängig machen. Aber wie, wenn er den vorherigen Wert nicht mehr in Erinnerung hat Also erleichtern wir ihm die Arbeit, indem wir die alten Werte neben den entsprechenden Textfeldern anzeigen.

Die hier vorgestellte Technik bezieht sich natürlich auf Detailformulare – in der Datenblatt- oder Endlosansicht lassen sich Steuerelemente zur Anzeige der vorherigen Daten nur schlecht anzeigen. Wir beginnen also mit einem neuen, leeren Formular namens frmDatensatz-aenderungenAufEinenBlick, dessen Eigenschaft Datenherkunft wir mit dem Namen der Tabelle tblKunden füllen.

Diese finden Sie bereits in der Beispieldatenbank.

Anschließend können Sie alle Felder dieser Tabelle aus der Feldliste in den Detailbereich der Entwurfsansicht des Formulars ziehen. Das Ergebnis sieht etwa wie in Bild 1 aus.

Entwurf des Ausgangsformulars

Bild 1: Entwurf des Ausgangsformulars

Damit das Formular die alten Werte der jeweiligen Felder anzeigen kann, fügen wir für jedes Feld, das der Benutzer bearbeiten kann, ein weiteres Textfeld samt Bezeichnungsfeld hinzu. Die Bezeichnungsfelder erhalten jeweils den Text Alt: als Beschriftung. Die Textfelder benennen wir genauso wie die eigentlichen Textfelder – mit dem Unterschied, dass wir den Namen noch das Suffix _Alt anhängen. Das Textfeld zur Anzeige des alten Wertes für das Feld txtVorname soll also txtVorname_Alt heißen (s. Bild 2).

Anlegen der Textfelder für die alten Werte

Bild 2: Anlegen der Textfelder für die alten Werte

Hier haben wir nun bereits den Wert Aktiviert der Textfelder mit den vorherigen Werten auf Nein eingestellt, damit der Benutzer gleich erkennen kann, dass er hier keine Werte eingeben kann.

Es fehlt noch eine Einstellung, denn die Textfelder sollen ja auch erst angezeigt werden, wenn der Benutzer einen anderen als den vorhandenen Wert eingegeben hat.

Also stellen wir die Eigenschaft Sichtbar für die Textfelder auf der rechten Seite auf Nein ein (Sie können alle Textfelder markieren, indem Sie einen Rahmen aufziehen und dann die Eigenschaft für alle Textfelder gleichzeitig einstellen).

Das Formular sieht nun in der Formularansicht vorerst wie in Bild 3 aus. Beim ändern eines der Einträge tut sich natürlich noch nichts, denn wir haben ja auch noch keinen entsprechenden Code hinterlegt.

Formular in der Formularansicht

Bild 3: Formular in der Formularansicht

Code zum Einblenden und Füllen der Alt-Felder

Nun haben wir uns das so vorgestellt, dass wir einfach das Ereignis Bei Geändert für das jeweilige Steuerelement implementieren und dafür die folgende Ereignisprozedur hinterlegen:

Private Sub txtFirma_Dirty(Cancel As Integer)
     With Me!txtFirma_Alt
         .Visible = True
         .Value = Me!txtFirma.OldValue
     End With
End Sub

Für das Feld txtFirma soll also eine Prozedur ausgelöst werden, welche die Eigenschaft Visible des Textfeldes txtFirma_Alt auf True einstellt und den Wert des Textfeldes txtFirma_Alt auf den alten Wert (OldValue) des Textfeldes txtFirma.

Dies klappt allerdings nicht wie gewünscht: Sobald Sie etwa ein Zeichen an den Inhalt des Textfeldes txtFirma anfügen wollen (also ohne diesen zu markieren und zu überschreiben), werden zwar die gewünschten Aktionen für das Textfeld txtFirma_Alt durchgeführt, aber der Inhalt des Textfeldes txtFirma wird komplett durch das soeben eingegebene Zeichen überschrieben (s. Bild 4).

Verhalten bei Eingabe in ein Textfeld

Bild 4: Verhalten bei Eingabe in ein Textfeld

Schauen wir uns an, was zu diesem unerwarteten Verhalten führt. Ein kurzer Test, bei dem wir erst die erste, dann die zweite Anweisung innerhalb der Prozedur ausblenden, zeigt, dass die Zuweisung des alten Wertes an die Value-Eigenschaft von txtFirma_Alt dafür sorgt, dass der alte Wert von txtFirma komplett überschrieben wird.

Hier geschieht Folgendes: Wenn Sie das erste Zeichen in das Textfeld txtFirma eingeben, wird bereits die Prozedur txtFirma_Dirty ausgelöst. Das Textfeld txtFirma_Alt wird eingeblendet und erhält als Wert den alten Wert des auslösenden Textfeldes. Bei einer solchen Zuweisung wird immer auch der Fokus auf das aufnehmende Feld verschoben!

Der Fokus gelangt dann zwar wieder zum Feld txtFirma zurück, aber beim Eintritt in das Feld wird die Markierung so eingestellt, wie dies standardmäßig beim Eintritt in ein Feld vorgesehen ist.

Cursorverhalten bei Eintritt in Feld

Dieses Verhalten wird normalerweise Access-weit eingestellt, und zwar in den Optionen von Access. Hier finden Sie im Bereich Clienteinstellungen unter Bearbeiten die Eigenschaft Cursorverhalten bei Eintritt in Feld (s. Bild 5).

Option zum Einstellen des Verhaltens beim Eintritt in ein Feld

Bild 5: Option zum Einstellen des Verhaltens beim Eintritt in ein Feld

Der Standardwert ist Ganzes Feld markieren, was hier auch geschehen ist und das beobachtete Verhalten verursacht (sollten Sie eine andere Einstellung vorfinden, haben Sie auch ein entsprechendes anderes Verhalten beobachten können).

Wie können wir nun den Inhalt des Feldes transferieren, ohne dass nach dem Zurücksetzen des Fokus auf das auslösende Feld wieder der komplette Inhalt markiert und ersetzt wird Hilft es eventuell, wenn wir die Einstellung für diese Aktion per Code ändern Wohl kaum, denn wir wissen ja nicht, an welcher Stelle der Benutzer die änderung durchgeführt hat.

Die einzige Möglichkeit, die wir haben, ist, uns die aktuelle Cursorposition zu merken und die Länge der Markierung und die Position vor der Rückkehr des Fokus zum auslösenden Feld wiederherzustellen.

Dies erledigen wir, indem wir zunächst zwei Variablen zur Prozedur hinzufügen, mit denen wir die Position der Einfügemarke sowie die Breite der Markierung speichern. Dann füllen wir diese beiden Variablen mit den entsprechenden Werten für das Textfeld txtFirma. Anschließend folgen die bereits vorhandenen Anweisungen der Prozedur. Nach dem Verschieben des Fokus auf das Textfeld txtFirma_Alt und vor dem Erhalt des Fokus durch das Textfeld txtFirma stellen wir die Markierungsposition und -breite wieder her:

Private Sub txtFirma_Dirty(Cancel As Integer)
     Dim lngSelStart As Long
     Dim lngSelLength As Long
     lngSelStart = Me!txtFirma.SelStart
     lngSelLength = Me!txtFirma.SelLength
     With Me!txtFirma_Alt
         .Visible = True
         .Value = Me!txtFirma.OldValue
     End With
     Me!txtFirma.SelStart = lngSelStart
     Me!txtFirma.SelLength = lngSelLength
End Sub

Nun erhalten wir das gewünschte Verhalten, wie Bild 6 zeigt. Wir haben hier den Buchstaben n aus dem Firmennamen gelöscht, der anschließend genau wie erwartet angezeigt wird.

Gewünschtes Verhalten

Bild 6: Gewünschtes Verhalten

übrige Felder mit Funktionalität ausstatten

Nun wollen wir noch schnell die übrigen Felder mit der hier implementierten Funktion ausstatten. Dazu legen Sie einfach die leeren Prozeduren für das Ereignis Bei Geändert an und kopieren den Inhalt der weiter oben bereits vorgestellten Prozedur für das Feld txtFirma ein.

Außerdem passen Sie dort noch die Feldnamen an, indem Sie etwa für das Textfeld txtVorname jeweils txtFirma durch txtVorname und txtFirma_Alt durch txtVorname_Alt ersetzen:

Private Sub txtVorname_Dirty(Cancel As Integer)
     Dim lngSelStart As Long
     Dim lngSelLength As Long
     lngSelStart = Me!txtVorname.SelStart
     lngSelLength = Me!txtVorname.SelLength
     With Me!txtVorname_Alt
         .Visible = True
         .Value = Me!txtVorname.OldValue
     End With
     Me!txtVorname.SelStart = lngSelStart
     Me!txtVorname.SelLength = lngSelLength
End Sub

Dies müssen Sie nun nur noch für alle übrigen Felder durchführen und erhalten so einen sehr gut wartbaren Code. äh … nein, doch nicht. Genau genommen haben wir nun eine ganze Reihe von Prozeduren erstellt, die alle das gleiche tun – mit dem Unterschied, dass jeweils zwei andere Textfelder referenziert werden.

Code optimieren

Eine erste Möglichkeit, den Code zumindest zuverlässiger vervielfältigen zu können, ist die folgende Variante, die wir für das Ereignis Bei änderung des Textfeldes txtNachname implementiert haben:

Private Sub txtNachname_Dirty(Cancel As Integer)
     Dim lngSelStart As Long
     Dim lngSelLength As Long
     Dim strTextfeld As String
     strTextfeld = "txtNachname"
     lngSelStart = Me(strTextfeld).SelStart
     lngSelLength = Me(strTextfeld).SelLength
     With Me(strTextfeld & "_Alt")
         .Visible = True
         .Value = Me(strTextfeld).OldValue
     End With
     Me(strTextfeld).SelStart = lngSelStart
     Me(strTextfeld).SelLength = lngSelLength
End Sub

Hier haben wir den Namen des betroffenen Textfeldes, nämlich txtNachname, in einer Variablen namens strTextfeld gespeichert. Danach haben wir alle Bezüge auf dieses Steuerelement in der Prozedur, die etwa Me!txtNachname lauten, in Me(strTextfeld) geändert. Für die Stellen, an denen es eine Referenz auf Me!txtNachname_Alt gibt, verwenden wir Me(strTextfeld & “_Alt”).

Hier profitieren wir also davon, dass wir die Textfelder zum Anzeigen der alten Werte immer konsequent mit dem Suffix _Alt ausgestattet haben.

Wir können den Inhalt der Prozedur nun also in die entsprechenden Prozeduren der übrigen Steuerelemente übertragen und brauchen nur noch die Zeile strTextfeld = “txtNachname” an den Namen des jeweils aufrufenden Textfeldes anzupassen. Die Gefahr, beim Copy and Paste und beim anschließenden manuellen Anpassen Fehler in den Code einzubauen, sinkt so erheblich.

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

den kompletten Artikel im PDF-Format mit Beispieldatenbank

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar