Zur Hauptseite ... Zum Onlinearchiv ... Zum Abonnement ... Zum Newsletter ... Zu den Tools ... Zum Impressum ... Zum Login ...

Achtung: Dies ist nicht der vollständige Artikel, sondern nur ein paar Seiten davon. Wenn Sie hier nicht erfahren, was Sie wissen möchten, finden Sie am Ende Informationen darüber, wie Sie den ganzen Artikel lesen können.

Kompletten Artikel lesen?

Einfach für den Newsletter anmelden, dann lesen Sie schon in einer Minute den kompletten Artikel und erhalten die Beispieldatenbanken.

E-Mail:

Gedrucktes Heft

Diesen Beitrag finden Sie in Ausgabe 6/2017.

Unser Angebot für Sie!

Lesen Sie diesen Beitrag und 500 andere sofort im Onlinearchiv, und erhalten Sie alle zwei Monate brandheißes Access-Know-how auf 72 gedruckten Seiten! Plus attraktive Präsente, zum Beispiel das bald erscheinende Buch 'Access 2010 - Das Grundlagenbuch für Entwickler'!

Diesen Beitrag twittern

Undo in mehreren Unterformularen

In den Beiträgen »Undo in Haupt- und Unterformular« und »Undo in Haupt- und Unterformular mit Klasse« haben wir gezeigt, wie Sie die Undo-Funktion etwa durch einen Abbrechen-Schaltfläche nicht nur auf das Hauptformular, sondern auch auf die Änderungen im Unterformular erstrecken. Nun hat ein Leser gefragt, ob man dies auch für mehrere Unterformulare erledigen kann. Klar kann man – die angepasste Lösung stellt der vorliegende Beitrag vor.

Dabei setzen wir auf der Lösung aus dem Beitrag Undo in Haupt- und Unterformular mit Klasse (www.access-im-unternehmen.de/912) auf. Diese enthält im Vergleich zu dem vorherigen Beitrag eine Klasse mit der vollständigen Funktionalität für das Implementieren des Undo in Haupt- und Unterformular, die man nur noch im Ereignis Beim Laden des Hauptformulars initialisieren und mit einigen Eigenschaften versehen muss. Der dazu anzulegende Code war sehr überschaubar gemessen an der Funktion, die er hinzufügte.

Die Einschränkung dieser Klasse ist, dass man in dieser lediglich das Hauptformular und das Unterformular angeben konnte, auf die sich die Undo-Funktion auswirken sollte. Wenn man ein zweites Unterformular hinzufügen wollte, war das nicht möglich. Also schauen wir uns im vorliegenden Beitrag an, wie wir dieses Feature nachrüsten.

Aber wie viele Formular dürfen es denn sein? Reichen zwei aus – oder benötigt man drei oder vier Unterformulare? Und macht man sich denn mit so vielen Unterformularen nicht die Übersicht im Formular kaputt? Der Leser, der mit seiner Anfrage an uns herangetreten ist, lieferte gleich einen Screenshot seines Formulars mit, auf dem ersichtlich wurde, dass er ein Register-Steuerelement verwendet, um die verschiedenen Unterformulare im Formular unterzubringen. So gelingt das natürlich bei Erhaltung guter Übersicht.

Damit war klar: Wir sollten uns nicht unbedingt einer Einschränkung hingeben und eine Lösung programmieren, die beliebig viele Unterformulare berücksichtigt.

Erster Ansatz: Zwei Unterformulare

Dennoch haben wir, um uns ein wenig in den VBA-Code der anzupassenden Lösung einzuarbeiten, zunächst die bestehende Lösung auf den Einsatz mit zwei Unterformularen erweitert. Dazu haben wir lediglich im Code überall dort, wo bisher das Unterformular erwähnt wurde, den Code für ein zweites Unterformular untergebracht. Das klappte recht leicht – die Hauptarbeit war Copy and Paste und ein wenig Achtsamkeit an den richtigen Stellen.

Zweiter Ansatz: So viele Unterformulare wie nötig

Klar: Die Anzahl der Unterformular je Formulare ist begrenzt. Dennoch wollen Sie den Code der Klasse ja nicht jedes Mal erweitern, wenn ein neues Unterformular zum Formular hinzukommt. Wir wollen dann maximal ein paar Zeilen Code im Beim Laden-Ereignis hinzufügen, die das neu hinzugekommene Formular und dessen Datenherkunft sowie das Fremdschlüsselfeld der zugrunde liegenden Tabelle an die Klasse übergeben.

Dafür ist dann zum Umwandeln des Codes ein wenig mehr Aufwand erforderlich. Aber es lohnt sich!

Aus eins mach zwei

Wenn Sie bereits andere Lösungen von uns gesehen haben, die Funktionen in Klassen auslagern und bei denen sich die Funktion auf ein oder mehrere Steuer­elemente bezieht, dann wissen Sie bereits: Es wird eine Collection geben, welche die Informationen für die in beliebiger Anzahl auftretenden Unterformulare aufnehmen soll. Dazu legen wir für jedes Unterformular eine neue Klasse an, die dann in der Collection gespeichert wird. Das heißt weiter: Wir benötigen nicht mehr nur eine Hauptklasse, sondern noch eine weitere Klasse für die untergeordneten Steuer­elemente.

Bild 1 zeigt schon einmal, wie das Endergebnis aussehen soll. Zu diesem Zweck haben wir drei Tabellen namens tblHaupt, tblUnter1 und tblUnter2 erstellt, von denen die letzten beiden per Fremdschlüssel die erste Tabelle referenzieren.

Formular mit zwei Unterformularen

Bild 1: Formular mit zwei Unterformularen

Das Hauptformular ist an die Tabelle tblHaupt gebunden, die beiden Unterformulare an die Tabellen tblUnter1 und tblUnter2.

Undo-Funktion implementieren

Damit die Daten in diesen drei Formularen auch nach dem Ändern von Daten im Hauptformular und in den beiden Unterformularen noch wieder hergestellt werden können, ist nur wenig Code notwendig, wenn die beiden Klassen clsUndoMultiMain und clsUndoMultiSub einmal zur Datenbank hinzugefügt wurden.

Diese sieht dann nämlich wie in Listing 1 aus. Hier deklarieren wir im allgemeinen Teil der Code behind-Klasse des Formulars die Variable objUndoMultiMain vom Typ clsUndoMultiMain. Diese erstellen wir dann in der Beim Laden-Ereignisprozedur, wo wir zuerst die Datenherkunft des Hauptformulars für RecordsourceForm angeben, dann das Primärschlüsselfeld der Datenherkunft des Hauptformulars und schließlich einen Verweis auf das Hauptformular selbst.

Dim objUndomultiMain As clsUndoMultiMain
Private Sub Form_Load()
     Set objUndomultiMain = New clsUndoMultiMain
     With objUndomultiMain
         .RecordsourceForm = "tblHaupt"
         .PKForm = "HauptID"
         Set .Form = Me
         .AddSubform Me!sfmUnter1.Form, "tblUnter1", "HauptID"
         .AddSubform Me!sfmUnter2.Form, "tblUnter2", "HauptID"
         Set .OKButton = Me!cmdOK
         Set .CancelButton = Me!cmdAbbrechen
     End With
End Sub

Listing 1: Code zum Einbinden der Funktion zum Undo in Haupt- und Unterformular

Dann folgen die neuen Elemente: Die Methode AddSubform kann nämlich so oft aufgerufen werden, wie es nötig ist, und damit ein oder mehrere Unterformulare in die Undo-Funktion einschließen.

Dazu übergeben Sie dieser Methode die folgenden drei Parameter:

  • frm: Verweis auf das Unterformular (nicht auf das Unterformular-Steuerelement, sondern auf das darin enthaltene Element – zu referenzieren mit der Form-Eigenschaft)
  • strRecordsource: Bezeichnung der Datenherkunft (Tabelle oder Abfrage)
  • strFKSubform: Fremdschlüsselfeld der Datenherkunft des Unterformulars, über welches die Beziehung zum Datensatz im Hauptformular hergestellt wird

Grundlegende Technik

Die grundlegende Idee ist es, die gesamte Bearbeitung des aktuellen Datensatzes des Hauptformulars und der mit diesem Datensatz verknüpften Datensätze im Unterformular in eine Transaktion einzuarbeiten. Dazu gibt es einen wichtigen Unterschied zur herkömmlichen Arbeit mit an Formulare gebundene Datenherkünften: Diese dürfen nämlich nicht wie sonst einfach etwa über die Eigenschaft Recordsource beziehungsweise Datenherkunft an die Formulare und Unterformulare gebunden werden. Stattdessen erstellen wir diese als zunächst unabhängiges Recordset und weisen dieses dann der Recordset-Eigenschaft des Hauptformulars und der Unterformulare zu.

Die Recordsets erstellen wir im Kontext eines Database-Objekts, das direkt dem Workspace-Objekt der aktuellen Sitzung untergeordnet ist. Auf diese Weise können wir in den Recordsets Änderungen vornehmen und diese dann über die Transaction-Methoden BeginTrans, CommitTrans und Rollback des Workspace-Objekts nach den Wünschen des Benutzers entweder zusammen speichern oder verwerfen.

Zusammenhang zwischen den beiden Klassen

Die Hauptklasse clsUndoMultiMain nimmt die wichtigsten Elemente wie das Database- und das Workspace-Objekt auf und enthält die Ereignisprozeduren, die für das Hauptformular angelegt werden. Die Klasse clsUndoMultiSub, die für jedes Unterformular einmal als Objekt angelegt wird, erhält den Verweis auf das jeweilige Unterformular und implementiert die Ereignisprozeduren für das Unterformular.

Wichtig: Hierbei ist zu beachten, dass das Unterformular ein VBA-Klassenmodul enthält! Sie können das am einfachsten erreichen, indem Sie die Eigenschaft Enthält Modul auf Ja einstellen. Anderenfalls werden die Ereignisprozeduren, die in der Klasse clsUndoMultiSub angelegt sind, nicht für das betroffene Unterformular ausgelöst. Beim Hauptformular sollte dies standardmäßig der Fall sein, da wir dort ja auch die Ereignisprozedur Form_Load unterbringen, in der wie die Undo-Klassen initialisieren und einstellen.

Die ersten beiden Einstellungen, die der Benutzer vornehmen muss, sind der Name des Primärschlüssels der Datenherkunft des Formulars sowie der Name der Datenherkunft. Diese landen in den beiden folgenden Variablen:

Private m_PKForm As String
Private m_RecordsourceForm As String

Damit diese Variablen über die Eigenschaft PKForm gefüllt und ausgelesen werden kann, legen wir die beiden folgenden Property Let/Get-Prozeduren an:

Public Property Let PKForm(str As String)
     m_PKForm = str
End Property
Public Property Get PKForm() As String
     PKForm = m_PKForm
End Property

Für m_RecordsourceForm benötigen wir nur eine öffentliche Eigenschaft zum Zuweisen, ausgelesen wird diese Eigenschaft von außen nicht:

Public Property Let RecordsourceForm(str As String)
     m_RecordsourceForm = str
End Property

Die Klasse clsUndoMultiMain enthält außerdem die folgenden zwei Elemente, für welche Ereignisprozeduren implementiert werden:

Private WithEvents m_OKButton As CommandButton
Private WithEvents m_CancelButton As CommandButton

Über die folgenden beiden Property Set-Methoden können die Schaltflächen des Formulars den Variablen zugewiesen werden.

Dabei stellen wir auch gleich ein, dass das Ereignis Beim Klicken in dieser Klasse implementiert werden soll:

Public Property Set OKButton(cmd As CommandButton)
     Set m_OKButton = cmd
     With m_OKButton
         .OnClick = "[Event Procedure]"
     End With
End Property
Public Property Set CancelButton(cmd As CommandButton)
     Set m_CancelButton = cmd
     With m_CancelButton
         .OnClick = "[Event Procedure]"
     End With
End Property

Angabe des Hauptformulars

Der Verweis auf das Hauptformular soll in der Variablen m_Form gespeichert werden, für das wir ebenfalls Ereignisprozeduren implementieren wollen können:

Private WithEvents m_Form As Form

Die beiden folgenden Variablen nehmen die Verweise auf das Database- und das Workspace-Objekt auf:

Private m_db As DAO.Database
Private m_wrk As DAO.Workspace

Die Variable m_Form wird über die folgende Property Set-Methode gefüllt, die auch gleich einstellt, für welche Ereignisse im aktuellen Modul Ereignisprozeduren hinterlegt werden sollen. Außerdem werden hier die beiden Variablen m_db und m_wrk gefüllt.

Schließlich füllt sie noch das Recordset des Hauptformulars mit der in m_RecordsourceForm angegebenen Datenherkunft (zuerst als Recordset rst als Element des aktuelle Database-Objekts und dann durch Zuweisen dieser Variablen an die Recordset-Eigenschaft des Formulars):

Public Property Set Form(frm As Form)
    Dim rst As DAO.Recordset
     Set m_Form = frm
     With m_Form
         .AfterUpdate = "[Event Procedure]"
         .BeforeDelConfirm = "[Event Procedure]"
         .OnCurrent = "[Event Procedure]"
         .OnDelete = "[Event Procedure]"
         .OnDirty = "[Event Procedure]"
         .OnError = "[Event Procedure]"
         .OnOpen = "[Event Procedure]"
         .OnUnload = "[Event Procedure]"
         .OnUndo = "[Event Procedure]"
     End With
     Set m_db = DBEngine(0)(0)
     Set m_wrk = DBEngine.Workspaces(0)
     Set rst = m_db.OpenRecordset(m_RecordsourceForm, _
          dbOpenDynaset)
     Set m_Form.Recordset = rst
End Property

Wir müssen auch von der Klasse clsUndoMultiSub auf das in clsUndoMultiMain enthaltene Formular zugreifen können, also legen wir auch eine Property Get-Prozedur an:

Public Property Get Form() As Form
     Set Form = m_Form
End Property

Auch die Variable m_wrk mit dem Workspace-Objekt wollen wir von den Unterklassen aus nutzen. Dazu legen wir die folgende Property Get-Methode an:

Public Property Get wrk() As DAO.Workspace
     Set wrk = m_wrk
End Property

Es gibt noch drei Eigenschaften, mit denen wir den aktuellen Zustand der Daten im Formular speichern müssen. Diese sollen in der Hauptklasse clsUndoMultiMain gespeichert werden, aber auch von den Unterklassen zugreifbar sein. Daher legen wir auch diese als private Variablen an:

Private m_DirtyForm As Boolean
Private m_SavedForm As Boolean
Private m_DeletedForm As Boolean

m_DirtyForm müssen wir lesen und schreiben können:

Public Property Get DirtyForm() As Boolean
     DirtyForm = m_DirtyForm
End Property
Public Property Let DirtyForm(bol As Boolean)
     m_DirtyForm = bol
End Property

Gleiches gilt für m_SavedForm:

Public Property Get SavedForm() As Boolean
     SavedForm = m_SavedForm
End Property
Public Property Let SavedForm(bol As Boolean)
     m_SavedForm = bol
End Property

m_DeletedForm verwenden wir nur innerhalb der Klasse clsUndoMultiMain, also brauchen wir keine Property-Eigenschaften. Schließlich fehlt noch eine Collection-Variable, in der wir die durch die weiter unten beschriebene AddSubform-Methode hinzugefügten Instanzen der Klasse clsUndoMultiSub speichern:

Private col As Collection

Diese instanzieren wir in dem folgenden Ereignis, das gleich beim Erstellen der Klasse clsUndoMultiMain ausgelöst wird:

Private Sub Class_Initialize()

Sie haben das Ende des frei verfügbaren Teils des Artikels erreicht. Lesen Sie weiter, um zu erfahren, wie Sie den vollständigen Artikel lesen und auf viele hundert weitere Artikel zugreifen können.

Sind Sie Abonnent?Jetzt einloggen ...
 

Kompletten Artikel lesen?

Einfach für den Newsletter anmelden, dann lesen Sie schon in einer Minute den kompletten Artikel und erhalten die Beispieldatenbanken.

E-Mail:

© 2003-2015 André Minhorst Alle Rechte vorbehalten.