Ereignisse kann man nicht nur innerhalb des jeweiligen Klassenmoduls eines Objekts wie in Formularen oder Berichten abfangen. Sie können auch Code in Klassenmodulen, Formular- und Berichtsmodulen unterbringen, der die Ereignisse anderer Klassenmodule abfängt und sich dort einklinkt. Dieser Beitrag zeigt, wie nützlich dies sein kann und wie es überhaupt funktioniert.
Im Beitrag Ereignisprozeduren (Shortlink 684) haben wir einige Grundlagen zur Programmierung der Ereignisprozeduren von Formularen und Berichten sowie darin enthaltener Steuerelementen beschrieben. Was wir dort nur angerissen haben, ist die Tatsache, dass Sie sich auch von außerhalb des betroffenen Objekts und zur Laufzeit in die Ereigniskette einschleichen können.
Was um Himmels willen soll man denn damit anfangen Bevor wir Beispiele liefern, zunächst einmal die grundsätzliche Vorgehensweise. Normalerweise schreiben Sie Ereignisprozeduren in das Klassenmodul des jeweiligen Formulars oder Berichts und stellen die Ereigniseigenschaften auf den Wert [Ereignisprozedur] oder [Event Procedure] ein, damit das Formular oder der Bericht weiß, dass für ein Ereignis eine entsprechende Prozedur vorhanden ist.
Solche Ereignisse kann man aber auch außerhalb des eigentlichen Klassenmoduls nutzen, indem man eine Objektvariable mit einer Referenz auf das Formular oder den Bericht deklariert und instanziert – und das unter Zuhilfenahme eines speziellen Schlüsselworts namens WithEvents.
Der Beispielaufbau besteht aus zwei ungebundenen Formularen namens frmEreignisAbfangen und frmFormularMitEreignis. Ersteres enthält lediglich eine Schaltfläche namens cmdSetReference und sieht wie in Bild 1 aus.
Bild 1: Dieses Formular wird gleich die Ereignisse eines anderen Formulars abfangen.
Das Formular frmFormularMitEreignis ist ein komplett leeres Formular, das lediglich Ereignisprozeduren für die Ereignisse Beim Öffnen und Beim Schließen enthält:
Private Sub Form_Close() MsgBox "Formular wird geschlossen." End Sub Private Sub Form_Open(Cancel As Integer) MsgBox "Formular wird geöffnet." End Sub
Der Schaltfläche cmdSetReference weisen Sie nun die folgende Ereignisprozedur zu – nicht ohne eine Objektvariable namens objForm mit dem Schlüsselwort WithEvents im Kopf des Klassenmoduls des Formulars zu referenzieren:
Dim WithEvents objForm As Form Private Sub cmdSetReference_Click() DoCmd.OpenForm "frmFormularMitEreignis" Set objForm = Forms!frmFormularMitEreignis End Sub
Bei einem Mausklick auf die Schaltfläche geschieht nichts Unerwartetes: Die Routine öffnet das Formular frmFormularMitEreignis, welches wiederum mit der oben definierten Meldung anzeigt, dass es geöffnet wurde. Gleiches geschieht, wenn Sie es wieder schließen.
Ereignis einschleusen
Dank der Deklaration von objForm mit dem Schlüsselwort WithEvents können wir im Klassenmodul des Formulars frmEreignisAbfangen Ereignisprozeduren für das mit objForm referenzierte Formular festlegen.
Das geht am einfachsten, indem Sie aus dem linken Kombinationsfeld des Codefensters mit dem Modul Form_frmEreignisAbfangen den Eintrag objForm und aus dem rechten Fenster zum Beispiel den Eintrag Open auswählen. Probieren wir dies am Beispiel des Close-Ereignisses des Objekts objForm aus:
Private Sub objForm_Close() MsgBox "Implantiert: Formular wurde geschlossen." End Sub
Wenn Sie die Schaltfläche nun betätigen und das Formular frmFormularMitEreignis anschließend wieder schließen, erscheinen zwei Meldungsfenster – das erste mit dem Text Implantiert: Formular wurde geschlossen und schließlich die Meldung aus dem Formular frmFormularMitEreignis selbst.
Das Fazit lautet: Sie können Ereignisse für ein Formular auch von außen festlegen, wenn Sie nur eine Objektvariable mit WithEvents deklarieren und damit auf das Zielformular verweisen. Hierbei gibt es noch einige weitere Möglichkeiten und auch Punkte, die Sie beachten müssen – fürs Erste soll dies jedoch ausreichen, sodass wir uns nun wichtigeren Dingen zuwenden: Wofür brauchen wir das Ganze überhaupt
Beispiele für die vorgestellte Technik
In den folgenden Abschnitten zeigen wir Ihnen an zwei Beispielen, wie Sie durch das Implantieren einer Ereignisprozedur von einem Formular in ein anderes Vorteile erzielen und den Entwurf Ihrer Anwendung verbessern können.
Beispiel Detailformular
Das erste Beispiel, bei dem das Implantieren einer Ereignisprozedur eine Verbesserung hervorruft, ist die typische Vorgehensweise beim Öffnen eines Detailformulars von einer Übersichtsliste aus zum Anlegen eines neuen Datensatzes (Objekte in der Beispieldatenbank: frmArtikeluebersicht_I, sfmArtikeluebersicht_I, frmArtikeldetails_I). Bild 2 zeigt das Übersichtsformular mit einer Anzeigen-Schaltfläche, die den Detaildialog aus Bild 3 öffnen soll. Normalerweise ist dafür etwa folgender Code verantwortlich, den Sie für die Ereigniseigenschaft Beim Klicken hinterlegen:
Bild 2: Mit der Anzeigen-Schaltfläche dieses Formulars öffnen Sie …
Bild 3: … dieses Detailformular.
Private Sub cmdNeu_Click() DoCmd.OpenForm "frmArtikeldetails_I", _ DataMode:=acFormAdd, WindowMode:=acDialog Me!sfmArtikeluebersicht.Form.Requery End Sub
Diese Routine öffnet das Detailformular als modalen Dialog, das heißt, dass der aufrufende Code erst weiterläuft, wenn das Formular wieder geschlossen ist. Genau dann führt die Routine noch eine Anweisung aus, die den Inhalt des Unterformulars mit der Übersicht der Datensätze aktualisiert, damit dieses den über das Detailformular hinzugefügten Datensatz direkt anzeigt.
Nachteil Detailformular, Version I
Der Nachteil hierbei ist in manchen Fällen, dass der Benutzer nicht auf die Daten im aufrufenden Formular zugreifen kann, solange das Detailformular geöffnet ist (manchmal ist dies auch gewollt – in diesen Fällen reicht die obige Technik natürlich aus).
Verbesserung Detailformular
Wenn Sie auf das Übersichtsformular zugreifen möchten, während das Detailformular geöffnet ist, rufen Sie dieses mit folgender Anweisung auf – also wie oben, nur ohne den Parameter WindowMode auf acDialog einzustellen:
DoCmd.OpenForm "frmArtikeldetails_I", DataMode:=acFormAdd
Das Detailformular verschwindet nun allerdings hinter dem Übersichtsformular, was Sie durch Einstellen der Eigenschaft Popup des Detailformulars auf den Wert Ja ändern. Sie können dann auf das Übersichtsformular zugreifen, während das Detailformular geöffnet ist (siehe Bild 4).
Bild 4: Arbeiten im Übersichtsformular, während das Detailformular geöffnet ist
Wenn Sie allerdings wollen, dass der neue Datensatz gleich nach dem Schließen des Detailformulars im Übersichtsformular angezeigt wird, können Sie mit einem Requery in der aufrufenden Routine direkt hinter dem DoCmd.OpenForm nichts erreichen: Da das Detailformular nicht als modaler Dialog geöffnet wird, hält Access auch die Ausführung des Codes nicht an und ruft die Requery-Anweisung unmittelbar nach dem Öffnen des Detailformulars auf, was hier nicht hilfreich ist.
Wie aber bekommen wir nun mit, wann der Benutzer das Detailformular schließt und wir das Hauptformular aktualisieren müssen Hier setzen wir mit einem Ereignisprozedur-Implantat an. Deklarieren Sie im Kopf des Klassenmoduls des Formulars frmArtikelUebersicht_I eine Objektvariable, die eine Referenz auf das geöffnete Formular enthält:
Dim WithEvents objFrmArtikelDetails_I As Form
Diese Variable füllen Sie in der Ereignisprozedur, die das Detailformular öffnet:
Set objFrmArtikelDetails_I = Forms!frmArtikeldetails_I
Nun brauchen Sie nur noch ein Ereignis für das Detailformular im Klassenmodul des Übersichtsformulars anzulegen, das beim Schließen des Detailformulars ausgelöst wird und das Unterformular mit der Datenübersicht aktualisiert:
Private Sub objFrmArtikelDetails_I_Close() Me!sfmArtikeluebersicht.Requery End Sub
Wenn Sie dies nun ausprobieren, schlägt der Plan allerdings fehl: Der Grund ist, dass wir zwar eine Ereignisprozedur für das Ereignis Beim Schließen des Detailformulars festgelegt haben, dieses aber nicht in die entsprechende Ereigniseigenschaft eingetragen wurde.
Weiter oben in der Beschreibung der grundlegenden Techniken hat dies noch funktioniert, weil das Ereignis bereits im Formular vorhanden und dementsprechend auch die Ereigniseigenschaft gefüllt war. An dieser Stelle gibt es zwei Möglichkeiten:
- Sie tragen manuell den Wert [Ereignisprozedur] (besser englisch [Event Procedure]) für die Eigenschaft Beim Schließen des Detailformulars ein oder
- Sie lassen dies das aufrufende Formular per Code erledigen.
Für Letzteres ist nur eine Zeile nötig, die Sie in die Beim Klicken-Prozedur des Übersichtsformulars gleich hinter der Anweisung zum Füllen der Objektvariablen objFrmArtikelDetails_I einfügen:
objFrmArtikelDetails_I.OnClose = "[Event Procedure]"
Somit wird nun beim Schließen des Detailformulars frmArtikelDetails die im Klassenmodul des Übersichtsformulars frmArtikelUebersicht angelegte Ereignisprozedur ausgelöst, die das zeitnahe Aktualisieren des Unterformulars ermöglicht, ohne dass das Detailformular als modaler Dialog geöffnet werden muss.
Ereignisimplantat für Haupt- und Unterformulare
Wenn Sie in einem Formular ein Unterformular verwenden, sollten Sie ebenfalls auf die Abhängigkeiten der beiden Elemente achten. Sinnvoll wäre es, wenn die Steuerung beziehungsweise die Logik komplett im Hauptformular läge.
Als Beispiel schauen wir uns die Artikelübersicht vom Anfang dieses Beitrags an. Sie enthält einige Schaltflächen, die normalerweise nur verwendet werden können, wenn ein Datensatz im Unterformular markiert ist. Um die Wirkung der folgenden Technik ein wenig zu unterstreichen, soll beispielsweise die Löschen-Schaltfläche nur aktiviert sein, wenn der im Unterformular angezeigte Artikel einen Lagerbestand von 0 aufweist.
Normalerweise würden Sie nun im Unterformular eine Ereignisprozedur für die Ereigniseigenschaft Beim Anzeigen anlegen, welche den aktuellen Datensatz prüft und die Schaltfläche cmdLoeschen im Hauptformular in Abhängigkeit vom ausgewählten Datensatz aktiviert oder deaktiviert.
Diese Abhängigkeit geht in die falsche Richtung: Das Unterformular sollte normalerweise völlig unabhängig vom Hauptformular sein, sich also ohne Probleme auch in andere Hauptformulare einfügen lassen – beispielsweise eines, das nur für solche Mitarbeiter gedacht ist, die nur Artikeldaten lesen, diese aber nicht bearbeiten und schon gar nicht löschen dürfen.
Das Formular frmArtikeluebersicht_II sieht wie in Bild 5 aus und enthält als Unterformular das Formular sfmArtikeluebersicht_II. Für die Objektreferenz auf das Unterformular brauchen wir wieder eine entsprechende Variable, die wir im Kopf des Klassenmoduls von frmArtikeluebersicht_II platzieren:
Bild 5: Dieses Formular implantiert dem Unterformular ein Beim Anzeigen-Ereignis.
Dim WithEvents objSfmArtikeluebersicht_II As Form
Die Löschen-Schaltfläche soll diese Funktion auslösen:
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