COM-Add-In: Ereignisprozedur zur Laufzeit anzeigen

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).

Üblicher Weg zur Anzeige der Ereignisprozedur eines Steuerelements

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).

Setzen eines Haltepunktes in der ersten Zeile einer Ereignisprozedur

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).

Anzeige der Ereignisprozedur per Ribbon-Befehl

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.

Anzeige und Markierung der Ereignisprozedur

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!

Anpassen der Benennung von Elementen

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.

Hinzufügen von Verweisen

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

Schreibe einen Kommentar