Lies diesen Artikel und viele weitere mit einem kostenlosen, einwöchigen Testzugang.
Bei unserer Arbeit mit Access passiert es immer wieder, dass wir schnell prüfen wollen, was eine durch ein bestimmtes Ereignis ausgelöste Prozedur überhaupt erledigt. Dann muss man in den Entwurf wechseln, einen Haltepunkt setzen, wieder den Formularansicht aktivieren und dann das Ereignis auslösen. Wir wäre es mit einem Add-In, das die Ereignisse des aktuell markierten Steuerelements direkt anzeigen könnte Ein solches COM-Add-In wollen wir in diesem Beitrag entwickeln und vorstellen. Das ist ein perfekter Anwendungszweck für die neue Entwicklungsumgebung twinBASIC, die wir in Ausgabe 3/2021 im Detail vorgestellt haben.
Debuggen schwer gemacht
Kennen Sie das auch Sie geben testweise während der Entwicklung einer Anwendung Daten ein oder betätigen eine Schaltfläche, und es geschieht einfach nicht das, was Sie gerade erwarten. Also geht es ans Debuggen: wechseln in die Entwurfsansicht des Formulars, dort das betreffende Steuerelement anklicken, damit seine Eigenschaften im Eigenschaftenblatt erscheinen, die passende Ereignisprozedur auswählen und auf die Schaltfläche mit den drei Punkten klicken (siehe Bild 1).
Bild 1: Üblicher Weg zur Anzeige der Ereignisprozedur eines Steuerelements
Der VBA-Editor erscheint und zeigt die durch das Ereignis ausgelöste Prozedur an. Hier setzen wir dann einen Haltepunkt, um den Prozedurablauf gleich im Detail ansehen zu können (siehe Bild 2).
Bild 2: Setzen eines Haltepunktes in der ersten Zeile einer Ereignisprozedur
Damit sind die Arbeiten im VBA-Editor vorerst erledigt und wir können zum Access-Fenster zurückkehren. Dort wechseln wir im aktuellen Formular wieder in die Formularansicht und führen den Vorgang erneut aus.
Gegebenenfalls ist es hierzu sogar noch notwendig, das Formular zu schließen und es anschließend über das Ribbon oder von einem anderen Formular aus erneut zu öffnen, damit die benötigte Datenkonstellation wieder vorhanden ist.
Wenn Sie das kennen, wissen Sie: Das ist anstrengend und wirkt irgendwie aufwendiger, als es sein muss. Das haben wir uns auch gedacht und ein COM-Add-In entwickelt, das beim Entwickeln einer Anwendung Folgendes erlaubt: Sie markieren einfach das zu untersuchende Steuerelement und wählen per Ribbon-Befehl aus allen Ereignissen der aktuellen Situation das zu untersuchende aus, welches dann automatisch im VBA-Editor angezeigt wird.
Debuggen, die leichte Version
Nun schauen wir uns erst einmal an, was wir am Ende des Beitrags für eine Lösung erhalten. Wenn Sie nach der Installation des COM-Add-Ins ein Steuerelement markieren, können Sie einfach das Ribbon-Menü Ereignisprozeduren anzeigen in der Gruppe Steuerelemente des Tabs amvAccessTools anzeigen. Hier finden Sie ganz oben die Ereignisprozeduren für das aktuelle Hauptformular, in diesem Fall Form_Load. Darunter zeigt die Liste für dieses Beispiel die drei Ereignisprozeduren für das Steuerelement ctlTreeView (siehe Bild 3).
Bild 3: Anzeige der Ereignisprozedur per Ribbon-Befehl
Wählen Sie einen dieser Einträge aus, erscheint der VBA-Editor und die ausgewählte Prozedur wird markiert angezeigt (siehe Bild 4). Wenn Sie ein Steuerelement in einem Unterformular selektieren, werden auch noch die Ereignisprozeduren des Unterformulars in der Liste angezeigt. Für weitere Verschachtelungsebenen ist die Lösung in der hier vorgestellten Form jedoch nicht ausgelegt.
Bild 4: Anzeige und Markierung der Ereignisprozedur
Programmieren des COM-Add-Ins
Das COM-Add-In programmieren wir mit dem neuen Tool twinBASIC, das wir im Beitrag twinBASIC – VB/VBA mit moderner Umgebung (www.access-im-unternehmen.de/1303) im Detail vorgestellt haben. Hier erfahren Sie auch, wie Sie twinBASIC installieren. Wie Sie ein COM-Add-In grundsätzlich programmieren, beschreibt der Beitrag twinBASIC – COM-Add-Ins für Access (www.access-im-unternehmen.de/1306).
Wir starten mit dem aktuellen Beispielprojekt für ein COM-Add-In für Access, das Sie im Download zu diesem Beitrag in der Zip-Datei twinBASIC_myCOMAddin.zip finden.
Projekt öffnen
Nachdem Sie Visual Studio Code und twinBASIC wie im oben genannten Beitrag installiert haben, öffnen Sie das Projekt durch einen Doppelklick auf die Datei myCOMAddin.code-workspace.
Projektelemente umbenennen
Als Erstes nehmen wir einige Änderungen bezüglich der Benennung von Projekt, Klasse et cetera vor. Dazu öffnen Sie durch einen Mausklick auf den Eintrag Settings im Projekt-Explorer die Einstellungen des Projekts.
Hier sehen Sie ganz oben die beiden Einträge Project: Name und Project: Description. Diese passen Sie wie in Bild 5 an. Wichtig: Merken Sie sich den Namen, den Sie für das Projekt vergeben, hier amvEreignisprozedurAnzeigen, – diesen benötigen wir gleich noch. Wichtig: Betätigen Sie auf jeden Fall die Tastenkombination Strg + S, um die Änderungen zu speichern!
Bild 5: Anpassen der Benennung von Elementen
Klassenname ändern
Dann schauen wir uns die Klasse des Projekts an, was wir durch einen Klick auf den Eintrag HelloWorld.twin erreichen. Diesen Eintrag ändern wir, indem wir seinen Kontextmenü-Befehl Umbenennen aufrufen und dann etwa die Bezeichnung EreignisprozedurenAnzeigen.twin eingeben.
Auch im Code der Klasse stellen wir den Namen wie folgt um:
Class EreignisprozedurAnzeigen
Implements IDTExtensibility2
...
Verweise auf Bibliotheken anlegen
Schließlich benötigen wir für die nachfolgende Programmierung noch Verweise auf ein paar weitere Bibliotheken. Diese legen wir wieder im Bereich Settings an. Hier fügen wir im Bereich COM Type Library / ActiveX References die beiden folgenden Verweise hinzu:
- Microsoft Access 16.0 Object Library
- Microsoft Visual Basic for Applications Extensibility 5.3
Der Bereich sollte danach wie in Bild 6 aussehen.
Bild 6: Hinzufügen von Verweisen
COM-Add-In für Access vorbereiten
Die Klasse EreignisprozedurAnzeigen enthält nun bereits einige grundlegende Elemente. Zum Beispiel legt die Zeile Implements IDTExtensibility2 fest, dass die Klasse die angegebene Schnittstelle implementiert. Das bedeutet, dass wir die für die Schnittstelle vorgegebenen Ereignisprozeduren anlegen müssen.
Die wichtigste Ereignisprozedur ist in unserem Fall OnConnection. Wenn Sie Access öffnen, startet es die in der Registry angegebenen COM-Add-Ins (mehr zur Registrierung weiter unten). Dabei wird direkt OnConnection aufgerufen und der Parameter Application liefert einen Verweis auf die aufrufende Anwendung, in diesem Fall Access. Den Inhalt dieses Parameters schreiben wir direkt in die zuvor deklarierte Variable objApplication, die mit dem Datentyp Access.Application deklariert ist.
Außerdem implementiert die Klasse eine weitere Schnittstelle namens IRibbonExtensibility. Diese wird benötigt, um vom COM-Add-In aus Ribbon-Anpassungen vorzunehmen. Hier müssen wir zusätzlich noch die Eigenschaft WithDispatchForwarding voranstellen:
[WithDispatchForwarding] Implements IRibbonExtensibility
Schließlich benötigen wir noch eine Objektvariable, mit der wir die Ribbon-Erweiterung referenzieren können:
Public objRibbon As IRibbonUI
Das Grundgerüst der Klasse haben wir in Listing 1 abgebildet.
Class EreignisprozedurAnzeigen Implements IDTExtensibility2 [WithDispatchForwarding] Implements IRibbonExtensibility Private objApplication As Access.Application Sub OnConnection(ByVal Application As Object, ByVal ConnectMode As ext_ConnectMode, _ ByVal AddInInst As Object, ByRef custom As Variant()) Implements IDTExtensibility2.OnConnection Set objApplication = Application End Sub Sub OnDisconnection(ByVal RemoveMode As ext_DisconnectMode, ByRef custom As Variant()) _ Implements IDTExtensibility2.OnDisconnection End Sub Sub OnAddInsUpdate(ByRef custom As Variant()) Implements IDTExtensibility2.OnAddInsUpdate End Sub Sub OnStartupComplete(ByRef custom As Variant()) Implements IDTExtensibility2.OnStartupComplete End Sub Sub OnBeginShutdown(ByRef custom As Variant()) Implements IDTExtensibility2.OnBeginShutdown End Sub ... End Class
Listing 1: Grundstruktur des COM-Add-Ins
Aufruf über das Ribbon
Die Funktion des COM-Add-Ins soll, wie eingangs erwähnt, über einen Befehl beziehungsweise ein Menü im Ribbon bereitgestellt werden. Damit dieses erscheint, müssen wir die Ribbon-Erweiterung, die das COM-Add-In bereitstellt, über die ebenfalls beim Start des Add-Ins aufgerufene Funktion GetCustomUI zusammenstellen und als Rückgabewert dieser Funktion definieren.
In dieser stellen wir den kompletten XML-Ausdruck für die Ribbon-Erweiterung zusammen (siehe Listing 2).
Private Function GetCustomUI(ByVal RibbonID As String) As String _ Implements IRibbonExtensibility.GetCustomUI Dim strXML As String strXML &= "<customUI xmlns=""http://schemas.microsoft.com/office/2006/01/customui"" onLoad=""OnLoad"">" & vbCrLf strXML &= " <ribbon startFromScratch=""false"">" & vbCrLf strXML &= " <tabs>" & vbCrLf strXML &= " <tab id=""tabTest"" label=""amvAccessTools"">" & vbCrLf strXML &= " <group id=""grpSteuerelemente"" label=""Steuerelemente"">" & vbCrLf strXML &= " <dynamicMenu id=""dmnEvents"" label=""Ereignisprozeduren anzeigen""" _ & " getContent=""GetContent"" imageMso=""CreateModule"" size=""large""/>" & vbCrLf strXML &= " </group>" & vbCrLf strXML &= " </tab>" & vbCrLf strXML &= " </tabs>" & vbCrLf strXML &= " </ribbon>" & vbCrLf strXML &= "</customUI>" & vbCrLf Return strXML End Function
Listing 2: Diese Funktion setzt das Ribbon zusammen.
Dies liefert eine XML-Definition wie die Folgende:
<customUI xmlns="http://schemas.microsoft.com/ office/2006/01/customui" onLoad="OnLoad"> <ribbon startFromScratch="false"> <tabs> <tab id="tabTest" label="amvAccessTools"> <group id="grpSteuerelemente" label="Steuerelemente"> <dynamicMenu id="dmnEvents" label="Ereignisprozeduren anzeigen" getContent="GetContent" imageMso="CreateModule" size="large"/> </group> </tab> </tabs> </ribbon> </customUI>
Das Kernstück ist das dynamicMenu-Element, das mit der getContent-Callback-Funktion beim Aufklappen dynamisch die Ereignisprozeduren für die aktuell markierten Elemente zusammenstellen soll.
Die dazu notwendige Funktion schauen wir uns weiter unten an.
Ribbon per Variable referenzieren
Wichtig ist auch noch, dass wir für das Attribut onLoad des Elements customUI eine Callback-Funktion angeben, die beim ersten Laden des Ribbons ausgelöst wird.
Diese schreibt den mit dem Parameter ribbon gelieferten Verweis auf das Ribbon in die weiter oben deklarierte Variable objRibbon:
Sub OnLoad(ribbon As IRibbonUI) Set objRibbon = ribbon End Sub
Warum benötigen wir dies Weil beim Öffnen des dynamicMenu-Elements die Callbackfunktion zum Zusammenstellen des Menüs nur einmal aufgerufen wird – außer, wir machen diese „ungültig“. Und dazu müssen wir eine Methode der Objektvariablen für das IRibbonUI-Objekt aufrufen.
dynamicMenu beim Aufklappen des Menüs füllen
Nun kommt der spannende Teil. Wie füllen wir das dynamicMenu-Element beim Aufklappen des Menüs Die erste Anmerkung ist für Programmierer, die bereits einmal dynamicMenu-Elemente unter Access verwendet haben.
Hier unter twinBASIC nutzen Sie eine etwas andere Syntax als unter VBA. Unter VBA lautet die erste Zeile der Callbackfunktion:
Sub GetContent(control As IRibbonControl, ByRef XMLString)
Unter twinBASIC verwenden wir die Deklaration wie unter VB6, die etwas anders aussieht:
Function GetContent(control As IRibbonControl) As String
Danach deklariert die Funktion die notwendigen Variablen, von denen viele aus der Bibliothek Microsoft Visual Basic for Applications Extensibility 5.3 stammen. Das bedeutet: Wir werden direkt auf den Code im VBA-Editor zugreifen, um die Ereignisprozeduren der Elemente zu ermitteln.
VBA-Projekt per Hilfsfunktion holen
Damit steigen wir nun in die Funktion GetContent aus Listing 3 ein (im Download finden Sie eine bereits weiterentwickelte Version). Hier nutzen wir als Erstes eine Hilfsfunktion namens GetCurrentVBProject, um das aktuelle VBA-Projekt zu referenzieren.
Function GetContent(control As IRibbonControl) As String Dim objVBProject As VBProject, objVBComponent As VBComponent, objCodeModule As CodeModule Dim frm As Form, sfm As Form, ctl As Control Dim strVBComponent As String, strContent As String Dim strEventsForm As String, strEventsSubform As String, strEventsControl As String Set objVBProject = GetCurrentVBProject strContent = "<menu xmlns=""http://schemas.microsoft.com/office/2006/01/customui"">" & vbCrLf On Error Resume Next Set frm = objApplication.Screen.ActiveForm On Error Goto 0 If Not frm Is Nothing Then On Error Resume Next Set ctl = objApplication.Screen.ActiveControl On Error Goto 0 If Not ctl Is Nothing Then strVBComponent = frm.Name If frm.HasModule Then Set objVBComponent = objVBProject.VBComponents("Form_" & strVBComponent) Set objCodeModule = objVBComponent.CodeModule strEventsForm = GetEventsForm(strVBComponent, objCodeModule) strContent = strContent & strEventsForm End If If Not ctl.Parent Is frm Then Set sfm = ctl.Parent If sfm.HasModule Then strVBComponent = ctl.Parent.Name Set objVBComponent = objVBProject.VBComponents("Form_" & strVBComponent) Set objCodeModule = objVBComponent.CodeModule strEventsSubForm = GetEventsForm(strVBComponent, objCodeModule) strContent = strContent & "<menuSeparator id=""sep2""/>" strContent = strContent & strEventsSubform End If End If Else MsgBox "Kein Steuerelement aktiviert.", vbExclamation + vbOKOnly, "Fehlende Selektion" End If If ctl.Parent.HasModule Then strContent = strContent & "<menuSeparator id=""sep3""/>" & vbCrLf strEventsControl = GetEventsControl(strVBComponent, objCodeModule, ctl) strContent = strContent & strEventsControl End If Else MsgBox "Kein Formular aktiviert.", vbExclamation + vbokonly, "Fehlende Selektion" End If strContent = strContent & " </menu>" objRibbon.Invalidate Return strContent End Function
Listing 3: Diese Funktion setzt das dynamicMenu zusammen.
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
Testzugang
eine Woche kostenlosen Zugriff auf diesen und mehr als 1.000 weitere Artikel
diesen und alle anderen Artikel mit dem Jahresabo