Datenmakros dienen dazu, Aktionen automatisch beim Ändern der Daten einer Tabelle auszulösen – nämlich beim Anlegen, Bearbeiten oder Löschen eines Datensatzes. Diese legt man in der Regel über die Benutzeroberfläche an. Leider kann man damit immer nur sehen, welche Datenmakros für die aktuelle im Entwurf oder in der Datenblattansicht angezeigte Tabelle zur Verfügung stehen. Dieser Beitrag zeigt, wie Sie sich einen besseren Überblick über die vorhandenen Datenmakros verschaffen, diese anzeigen und bearbeiten und sogar neue Datenmakros anlegen können – und das auch noch parametrisiert für gleichartige Datenmakros und mehrere Tabellen gleichzeitig.
Genau genommen sind wir durch die Lösung des Beitrags Zugriffsrechte mit Datenmakros (www.access-im-unternehmen.de/1193) auf die Idee zu diesem Beitrag gekommen. Dort haben wir die Tabellen der Beispieldatenbank mit einigen Datenmakros ausgestattet, die dafür sorgen, dass die Daten der Tabelle nur durch Benutzer mit entsprechenden Zugriffsrechten geändert werden dürfen.
Es wäre etwas mühselig, diese Datenmakros bei großen Datenbankanwendungen für jede Tabelle einzelnen hinzuzufügen. Daher haben wir dort schon gezeigt, wie Sie per VBA-Funktion ein Datenmakro zu der im Parameter der Funktion genannten Tabelle hinzufügen können. Aber wir wollen etwas grundlegender starten.
Datenmakros erkunden
Die erste Frage ist: Wie können wir überhaupt auf die bereits in den Tabellen der Anwendung vorhandenen Datenmakros zugreifen Gibt es eine Art VBA-Auflistung Oder auf welche Technik können wir zugreifen, um die vorhandenen Datenmakros zu analysieren
Datenmakros per Benutzeroberfläche
Schauen wir uns erst einmal an, wie wir über die Benutzeroberfläche auf die Datenmakros zugreifen. Der erste Weg ist über die Entwurfsansicht einer Tabelle. Dann finden Sie im Ribbon-Tab Entwurf unter Feld-, Datensatz- und Tabellenereignisse den Befehl Datenmakros erstellen. Wenn man diese Schaltfläche anklickt, öffnet sich eine Liste aller möglichen Daten-makros. In Bild 1 sind beispielsweise noch keine Datenmakros angelegt.
Bild 1: Datenmakros über die Entwurfsansicht verwalten
Wenn wir hier das Makro Nach Einfügung anlegen, wird das Icon mit einem roten Rand versehen (siehe Bild 2).
Bild 2: Kennzeichnung eines vorhandenen Datenmakros
Datenmakros per Datenblatt
In der Datenblattansicht ist es noch übersichtlicher (siehe Bild 3). Hier finden Sie im Ribbon-Tab Tabelle einige Schaltflächen, mit denen Sie Datenmakros anlegen oder bearbeiten können. Wenn das Daten-makro bereits vorhanden ist, hinterlegt Access die Schaltfläche mit einem Hintergrund wie hier beim Datenmakro Nach Einfügung.
Bild 3: Datenmakros über die Datenblattansicht verwalten
Wenn Sie auf eine der Schaltflächen klicken, öffnet Access die Entwurfsansicht für das Erstellen und Bearbeiten von Datenmakros. Diese steht aber in diesem Beitrag nicht im Mittelpunkt – daher schauen wir uns diese nun nicht im Detail an.
Per VBA auf die Datenmakros zugreifen
Nun möchten wir herausfinden, wie wir per VBA auf die Datenmakros einer Tabelle beziehungsweise der kompletten Datenbank zugreifen können. Welches Werkzeug im VBA-Editor hilft uns weiter, etwas über die Befehle in Zusammenhang mit Makros herauszufinden Der Objektkatalog.
Diesen öffnen wir und suchen nach dem Schlüsselwort Macro. Hier finden wir zunächst einige acCmd…-Konstanten, die letztlich zum Ausführen der Ribbonbefehle per VBA dienen. Außerdem entdecken wir allerdings auch ein paar weitere Elemente, die interessant sein könnten: besonders die Auflistung AllMacros (siehe Bild 4).
Bild 4: Befehle, welche die Zeichenkette Macro enthalten
Wir schauen uns einmal in einer kleinen Prozedur an, welches Ergebnis diese Auflistung liefert:
Public Sub AlleMakros() Dim mac As Object For Each mac In CurrentProject.AllMacros Debug.Print TypeName(mac), mac.Name Next mac End Sub
Dies liefert allerdings kein Ergebnis, wenn Sie nur Datenmakros angelegt haben. Die Auflistung dient allein dem Zugriff auf die herkömmlichen Makros, die auch im Navigationsbereich unter Makros angezeigt werden.
Es gibt also in der Tat keine Möglichkeit, per VBA direkt auf die Datenmakros zuzugreifen. Also gehen wir einen kleinen Umweg.
Umweg über SaveAsText
Wir wissen, dass Datenmakros in Zusammenhang mit Tabellen gespeichert werden. Weiterhin wissen wir, dass es eine Möglichkeit gibt, Access-Objekte über die Anweisung SaveAsText auf der Festplatte zu speichern und etwa über einen Texteditor auf diese Dateien zuzugreifen. Also schauen wir uns diesen Befehl einmal genauer an. Dies gelingt etwa über die IntelliSense-Funktion im Direktbereich des VBA-Editors. Dort finden wir dann ganz unten in der Liste eine interessante Konstante, nämlich acTableDataMacro (siehe Bild 5).
Bild 5: Die neue Konstante acTableDataMakro
Auch für diese Konstante geben wir als zweiten Parameter den Namen des zu speichernden Objekts an sowie als dritten Parameter den Dateinamen, unter dem das Objekt gespeichert werden soll. Nach kurzem Experimentieren wird klar, dass wir für den zweiten Parameter den Tabellennamen angeben, dessen Datenmakros wir im Dateisystem speichern wollen.
Also legen wir für die Tabelle tblArtikel einmal je ein Makro für die verschiedenen Typen an (das Ribbon sieht dann für diese Makros wie in Bild 6 aus).
Bild 6: Die Einträge für die Datenmakros im Ribbon
Zusätzlich fügen wir auch ein benanntes Makro zu der Tabelle hinzu, für das wir allerdings im Gegensatz zu den vorgegebenen Makros einen Namen angeben müssen. Das ist logisch, denn es kann mehr als ein benanntes Makro je Tabelle geben.
Danach rufen wir den folgenden Befehl auf, um den Code der Datenmakros auf der Festplatte zu speichern:
SaveAsText acTableDataMacro, "tblArtikel", Currentproject.Path & " blArtikel_Makros.txt"
Das Ergebnis sieht dann in der neu angelegten Datei tblArtikel_Makros.txt wie in Listing 1 aus. Hier sehen wir, dass es sich um eine XML-Datei handelt. Diese verwendet als Hauptobjekt das Element DataMacros. Darunter finden Sie einige Elemente namens DataMacro, die mit einem Attribut namens Event ausgestattet sind. Dieses gibt an, durch welches Ereignis das Datenmakro ausgelöst wird:
<xml version="1.0" encoding="UTF-16" standalone="no"> <DataMacros xmlns="http://schemas.microsoft.com/office/accessservices/2009/11/application"> <DataMacro Event="AfterInsert"> <Statements> <Comment>Kommentar Nach Einfügung</Comment> </Statements> </DataMacro> <DataMacro Event="AfterUpdate"> <Statements> <Comment>Kommentar Nach Aktualisierung</Comment> </Statements> </DataMacro> <DataMacro Event="AfterDelete"> <Statements> <Comment>Kommentar Nach Löschung</Comment> </Statements> </DataMacro> <DataMacro Event="BeforeChange"> <Statements> <Comment>Kommentar Vor Änderung</Comment> </Statements> </DataMacro> <DataMacro Event="BeforeDelete"> <Statements> <Comment>Kommentar Vor Löschung</Comment> </Statements> </DataMacro> <DataMacro Name="dmkBeispiel"> <Statements> <Comment>Kommentar Benanntes Makro</Comment> </Statements> </DataMacro> </DataMacros>
Listing 1: Die in eine Textdatei exportieren Datenmakros
- AfterInsert: Nach Einfügung
- AfterUpdate: Nach Aktualisierung
- AfterDelete: Nach Löschung
- BeforeChange: Vor Änderung
- BeforeDelete: Vor Löschung
Das ist aber nicht das einzige mögliche Attribut. Genau genommen wird dieses nur für die Datenmakros eingesetzt, die durch ein Ereignis ausgelöst werden. Für die anderen Makros von Tabellen, also die benannten Makros, gibt es das Attribut Name.
Unterhalb der DataMacro-Elemente finden wir ein Statements-Element, das dann die eigentlichen Befehle enthält. In unserem Beispiel haben wir für alle Makros einfach nur den Makrobefehl Kommentar eingefügt.
Hier finden dann auch die übrigen Befehle und Strukturen Platz, wobei bei den Strukturen noch untergeordnete Elemente integriert werden. Gleiches gilt für die Datenmakros.
Datenmakro analysieren
Wie aber nun können wir ermitteln, für welche Tabelle welche Datenmakros existieren und diese etwa im Direktfenster ausgeben Wir müssen tatsächlich über den Umweg des Dateiexports auf die Festplatte gehen. Dort würden wir die so gespeicherte Datei dann mit der Open-Anweisung öffnen und mit den Methoden der Bibliothek Microsoft XML, v3.0 analysieren.
Diese Bibliothek fügen Sie zunächst über den Verweise-Dialog hinzu (siehe Bild 7). Achtung: Mit der Version v6.0 funktioniert der nachfolgend angegebene VBA-Code nicht.
Bild 7: Hinzufügen eines Verweises auf die XML-Bibliothek
Alle Tabellen durchlaufen
Danach bauen wir zunächst ein Grundgerüst, mit dem wir alle Tabellen einer Datenbank durchlaufen. Dieses sieht wie folgt aus:
Public Sub TabellenAnalysieren() Dim db As dao.Database Dim tbl As dao.TableDef Dim objXML As MSXML2.DOMDocument60 Set db = CurrentDb For Each tbl In db.TableDefs If Not (Left(tbl.Name, 4) = "MSys") Then If EnthaeltMakros(tbl.Name, objXML) = True Then Debug.Print "Makros vorhanden in ''" & tbl.Name & "''." Else Debug.Print "Keine Makros in ''" & tbl.Name & "''." End If End If Next tbl End Sub
Damit durchlaufen wir alle Tabellen der Anwendung, deren Name nicht mit MSys… beginnt – also alle Tabellen außer den Systemtabellen. Innerhalb der If…Then-Bedingung können wir dann den Aufruf einer weiteren Prozedur unterbringen, welche die Makros in eine Datei exportiert und in ein XML-Dokument einliest – in diesem Fall eine Funktion namens EnthaeltMakros.
Diese soll den Wert True liefern, wenn eine Tabelle Datenmakros enthält und False, wenn dies nicht der Fall ist.
Makros nach XML
Die Funktion EnthaeltMakros erwartet den Namen der zu untersuchenden Tabelle als Parameter sowie eine Variable des Typs MSXML2.DOMDocument. Aus diesen bildet sie zunächst einen Dateinamen, der aus dem Pfad zur aktuellen Datenbank, einem Backslash, dem Namen der Tabelle und der Dateiendung .xml besteht:
Public Function EnthaeltMakros(strTabelle As String, objXML As MSXML2.DOMDocument60) As Boolean Dim strDateiname As String strDateiname = CurrentProject.Path & "" & strTabelle & ".xml"
Dann versuchen wir, bei deaktivierter Fehlerbehandlung die SaveAsText-Anweisung mit dem Namen der Tabelle für die zu exportierenden Datenmakros und dem Dateinamen als Parameter aufzurufen. Wenn die Tabelle keine Datenmakros enthält, wird der Fehler mit der Nummer 2950 ausgelöst (Reservierter Fehler). In diesem Fall soll die Funktion enden, ohne den Funktionswert auf True einzustellen:
On Error Resume Next SaveAsText acTableDataMacro, strTabelle, strDateiname If Err.Number = 2950 Then Exit Function End If On Error GoTo 0
Konnten die Datenmakros hingegen erfolgreich exportiert werden, erstellt die Funktion ein neues Objekt des Typs DOMDocument60, das wir im nächsten Schritt mit dem XML-Dokument aus der soeben mit SaveAsText angelegten Datei füllen und danach den Inhalt des XML-Dokuments testweise im Direktbereich ausgeben:
Set objXML = New MSXML2.DOMDocument60 objXML.Load strDateiname Debug.Print objXML.XML EnthaeltMakros = True End Function
In diesem Fall gibt die Funktion den Wert True zurück – und auch der Parameter objXML liefert das gewünschte XML-Dokument.
XML-Dokument analysieren
In der aufrufenden Prozedur TabellenAnalysieren ersetzen wir nun die Debug.Print-Anweisung im If-Teil der If…Then-Bedingung durch den Aufruf der Prozedur XMLAnalysieren. Diesem stellen wir die Ausgabe des Tabellennamens voran. Dann übergeben wir das mit objXML referenzierte XML-Dokument als Parameter, sodass die Prozedur nun wie folgt aussieht:
Public Sub TabellenAnalysieren() Dim db As dao.Database Dim tbl As dao.TableDef Dim objXML As MSXML2.DOMDocument Set db = CurrentDb For Each tbl In db.TableDefs If Not (Left(tbl.Name, 4) = "MSys") Then If EnthaeltMakros(tbl.Name, objXML) = True Then Debug.Print "Tabelle: " & tbl.Name Debug.Print String(Len("Tabelle: " & tbl.Name), "=") XMLAnalysieren objXML Debug.Print End If End If Next tbl End Sub
Die dadurch aufgerufene Prozedur XMLAnalysieren finden Sie in Listing 2.
Public Sub XMLAnalysieren(objXML As MSXML2.DOMDocument) Dim objDataMacro As MSXML2.IXMLDOMElement Dim objAttribute As MSXML2.IXMLDOMAttribute For Each objDataMacro In objXML.selectNodes("DataMacros/DataMacro") Set objAttribute = objDataMacro.selectSingleNode("@Event") If Not objAttribute Is Nothing Then Debug.Print " Event: " & objAttribute.nodeTypedValue Else Set objAttribute = objDataMacro.selectSingleNode("@Name") If Not objAttribute Is Nothing Then Debug.Print " Name: " & objAttribute.nodeTypedValue End If End If Next objDataMacro End Sub
Listing 2: Diese Prozedur analysiert den Code der enthaltenen Makrodefinitionen anhand der Attribute.
Die Prozedur erwartet das XML-Dokument als Parameter. Sie deklariert zwei Variablen: Eine für ein Element des Typs Element des XML-Dokuments und eines für ein Attribut-Element. Die erste verwendet sie als Laufvariable für eine For Each-Schleife über alle mit einer per selectNodes abgesetzten XPath-Abfrage. Die Abfrage lautet DataMacros/DataMacro und liefert alle DataMacro-Elemente unterhalb des DataMacros-Elements. Das sind für unser obiges Beispiel sechs Elemente – die fünf Daten-makros, die durch die verschiedenen Ereignisse ausgelöst werden, und ein benanntes Makro.
Innerhalb der Schleife versucht die Prozedur, mit einer weiteren XPath-Abfrage, diesmal für das bereits in objDataMacro gespeicherte XML-Element des Typs DataMacro, das Attribut Event zu referenzieren. Um Attribute zu referenzieren, stellt man unter XPath dem Attributnamen das @-Zeichen voran.
Wenn objAttribute danach nicht leer ist, wurde das Attribut gefunden und die Prozedur gibt den Wert des Attributs über die Eigenschaft NodeTyped-Value aus. Wurde kein Attribut namens Event gefunden, versucht die Prozedur im Else-Teil, auf die gleiche Art ein Attribut namens Name zu finden. Ist dieses vorhanden, gibt die Prozedur auch dessen Wert samt Attributnamen aus.
Auf diese Weise durchläuft die Prozedur alle Elemente des Typs DataMacro. Wenn wir noch für eine weitere Tabelle namens tblBestelldetails ein Datenmakro hinzufügen, sieht die Ausgabe im Direktfenster wie folgt aus:
Tabelle: tblArtikel =================== Event: AfterInsert Event: AfterUpdate Event: AfterDelete Event: BeforeChange Event: BeforeDelete Name: dmkBeispiel Tabelle: tblBestelldetails ========================== Event: AfterInsert
Datenmakros in Tabelle speichern
Wir wollen nun nicht weiter ins Detail gehen, was die in den Datenmakros enthaltenen Makroaktionen angeht, sondern eine Möglichkeit schaffen, die Tabellen und die enthaltenen Makros übersichtlich in der Datenbank anzuzeigen, damit der Benutzer diese auf die Schnelle öffnen und bearbeiten kann. Dazu benötigen wir zwei Tabellen zum Speichern der Tabellennamen und der enthaltenen Datenmakros. Auf diese greifen wir dann später von einer Kombination aus Formular und Bericht zu.
Dazu legen wir zwei Tabellen an. Die erste heißt tblTabellen und speichert die Namen der Tabellen, die Datenmakros enthalten. Sie enthält ein Primärschlüsselfeld namens TabelleID und ein Textfeld namens Tabelle, welches den Namen der Tabelle speichert. Für dieses Feld haben wir einen eindeutigen Index angelegt (siehe Bild 8).
Bild 8: Tabelle zum Speichern der Tabellennamen
Die zweite Tabelle heißt tblDatenmakros. Das Primärschlüsselfeld nennen wir logischerweise DatenmakroID. Aber wie speichern wir die Informationen aus den beiden Attributen Event und Name Wir legen in einem Nachschlagefeld namens DatenmakrotypID den Typ des Datenmakros fest, der einen der folgenden sechs Werte annehmen können soll:
- AfterInsert
- AfterUpdate
- AfterDelete
- BeforeChange
- BeforeDelete
- NamedDataMacro
Diese Werte speichern wir in der Lookuptabelle tblDatenmakrotypen, die im Entwurf wie in Bild 9 aussieht.
Bild 9: Tabelle zum Speichern der Datenmakrotypen
Jetzt fehlt noch ein Feld, das den Namen des Datenmakros speichert. Dieses nennen wir Makroname. Es soll für Datenmakros, die durch Ereignisse ausgelöst werden, den gleichen Wert erhalten, der auch über das Nachschlagefeld ausgewählt wurde, also Vor Änderung, Vor Löschung und so weiter. Für die benannten Makros hinterlegen wir den angegebenen Namen. Schließlich geben wir in einem weiteren Nachschlagefeld namens TabelleID noch an, zu welcher Tabelle das Datenmakro gehört. Der Entwurf der Tabelle tblDatenmakros sieht dann wie in Bild 10 aus.
Bild 10: Tabelle zum Speichern der Datenmakro-Eigenschaften
Die Beziehungen der Tabellen haben wir in Bild 11 abgebildet.
Bild 11: Beziehung der beteiligten Tabellen
Tabellen mit den Datenmakros füllen
Nun passen wir die Prozeduren TabellenAnalysieren und XML-Analysieren noch so an, dass die ermittelten Daten nicht im Direktbereich des VBA-Editors landen, sondern in den soeben erstellten Tabellen. Die Frage hierbei ist noch, ob wir nur die Tabellen in die Tabelle tblTabellen schreiben sollen, die bereits über Datenmakros verfügen, oder ob wir dort alle Tabellen eintragen sollen. Das wäre sinnvoll, wenn wir in unserer noch zu erstellenden Benutzeroberfläche nicht nur per Klick den Entwurf eines Makros öffnen, sondern auch noch neue Datenmakros anlegen wollen. Wir belassen es zunächst bei den Tabellen, die bereits Datenmakros enthalten.
Dazu haben wir die Prozedur TabellenAnalysieren wie in Listing 3 angepasst. Sie löscht zunächst alle Einträge der Tabelle tblTabellen.
Public Sub TabellenAnalysieren() Dim db As DAO.Database Dim tbl As DAO.TableDef Dim objXML As MSXML2.DOMDocument Dim lngTabelleID As Long Set db = CurrentDb db.Execute "DELETE FROM tblTabellen", dbFailOnError For Each tbl In db.TableDefs If Not (Left(tbl.Name, 4) = "MSys") Then If EnthaeltMakros(tbl.Name, objXML) = True Then db.Execute "INSERT INTO tblTabellen(Tabelle) VALUES(''" & tbl.Name & "'')", dbFailOnError lngTabelleID = db.OpenRecordset("SELECT @@IDENTITY", dbOpenDynaset).Fields(0) XMLAnalysieren objXML, lngTabelleID, db End If End If Next tbl End Sub
Listing 3: Die neue Version der Prozedur TabellenAnalysieren schreibt direkt die Tabellen in tblTabellen.
Damit dies ausreicht, um auch die mit diesen Einträgen verknüpften Datensätze der Tabelle tblDatenmakros zu löschen, haben wir für die Beziehung zwischen diesen beiden Tabellen referenzielle Integrität mit Löschweitergabe definiert (siehe Bild 12).
Bild 12: Referenzielle Integrität mit Löschweitergabe
Die Prozedur durchläuft nach wie vor alle benutzerdefinierten Tabellen der Datenbank und prüft mit der Funktion EnthaeltMakros, ob die Tabelle Datenmakros enthält. In diesem Fall schreibt sie die Tabelle als neuen Datensatz in die Tabelle tblTabellen und ermittelt den Primärschlüsselwert des neuen Datensatzes. Damit ausgestattet, ruft sie die Prozedur XMLAnalysieren auf.
Diese Prozedur erwartet neben dem Verweis auf das XML-Dokument nun noch zwei weitere Parameter: den Primärschlüsselwert des durch die Prozedur TabellenAnalysieren hinzugefügten Datensatzes und den Verweis auf das Database-Objekt der aktuellen Datenbank (siehe Listing 4).
Public Sub XMLAnalysieren(objXML As MSXML2.DOMDocument, lngTabelleID As Long, db As DAO.Database) Dim objDataMacro As MSXML2.IXMLDOMElement Dim objAttribute As MSXML2.IXMLDOMAttribute Dim lngDatenmakrotypID As Long For Each objDataMacro In objXML.selectNodes("DataMacros/DataMacro") Set objAttribute = objDataMacro.selectSingleNode("@Event") If Not objAttribute Is Nothing Then lngDatenmakrotypID = DLookup("DatenmakrotypID", "tblDatenmakrotypen", "Datenmakrotyp = ''" _ & objAttribute.nodeTypedValue & "''") db.Execute "INSERT INTO tblDatenmakros(DatenmakrotypID, Makroname, TabelleID) VALUES(" _ & lngDatenmakrotypID & ", ''" & objAttribute.nodeTypedValue & "'', " & lngTabelleID & ")", dbFailOnError Else Set objAttribute = objDataMacro.selectSingleNode("@Name") If Not objAttribute Is Nothing Then lngDatenmakrotypID = DLookup("DatenmakrotypID", "tblDatenmakrotypen", _ "Datenmakrotyp = ''NamedDataMacro''") db.Execute "INSERT INTO tblDatenmakros(DatenmakrotypID, Makroname, TabelleID) VALUES(" _ & lngDatenmakrotypID & ", ''" & objAttribute.nodeTypedValue & "'', " & lngTabelleID & ")", dbFailOnError End If End If Next objDataMacro End Sub
Listing 4: Die neue Version von XMLAnalysieren hat neue Parameter und trägt die gefundenen Daten gleich in die Tabelle tblDatenmakros ein.
Sie durchläuft wieder alle DataMacro-Elemente des XML-Dokuments und prüft, ob diese das Attribut Event enthalten. Ist das der Fall, ermittelt sie aus der Tabelle tblDatenmakrotypen den Primärschlüsselwert zu dem Eintrag, der das passende Event enthält. Diesen schreibt sie dann in der folgenden INSERT INTO-Anweisung zusammen mit dem Wert des Attributs für das Feld Makroname und dem Primärschlüsselwert der Tabelle tblTabellen aus lngTabelleID in die Tabelle tblDatenmakros.
Im Else-Zweig untersucht die Prozedur dann wieder, ob das Element ein Attribut namens Name enthält. In diesem Fall fügt sie einen entsprechenden Eintrag für ein benanntes Daten-makro zur Tabelle tblDatenmakros hinzu. Auf diese Weise durchläuft sie alle Datenmakros der Tabelle.
Das Ergebnis sieht dann in der Tabelle tblDatenmakros etwa wie in Bild 13 aus.
Bild 13: Ergebnis unserer Beispieldatenbank
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