Im Beitrag „Steuerelemente ausrichten per VBA“ (www.access-im-unternehmen.de/1429) haben wir eine Prozedur entwickelt, die markierte Steuerelemente samt Bezeichnungsfeldern in der Entwurfsansicht ausrichtet. Damit wir sie nutzen können, muss der Code in der jeweiligen Datenbank vorliegen. Bequemer ist es, wenn sie in jeder Datenbank über das Ribbon und das Kontextmenü der Steuerelemente bereitsteht. Wie wir dazu ein COM-Add-In mit twinBASIC bauen, zeigt der vorliegende Beitrag.
Von der VBA-Prozedur zum COM-Add-In
Access lädt ein COM-Add-In beim Start. Seine Funktionen stehen damit unabhängig von der geöffneten Datenbank zur Verfügung. Wir müssen keinen Code in einzelne Datenbanken kopieren. Die Prozedur aus dem VBA-Beitrag bleibt im Kern erhalten. Es ändert sich, wie wir ihre Parameter festlegen und wie wir sie aufrufen.
Die Parameter lngDistanceLabelControl, lngVerticalDistance, lngAlignment und bolColons der Prozedur AlignControls erscheinen als Elemente im Ribbon. Zwei Textfelder legen den horizontalen Abstand zwischen Bezeichnungsfeld und Steuerelement sowie den vertikalen Abstand fest, jeweils in Twips. Eine Dropdownliste bestimmt die Textausrichtung der Bezeichnungsfelder, und ein Kontrollkästchen schaltet den Doppelpunkt (:) hinter den Beschriftungen ein. Zwei Schaltflächen starten das Ausrichten und das Anhängen der Doppelpunkte (siehe Bild 1).

Bild 1: Der neue Bereich im Ribbon
Dieselben beiden Befehle fügen wir dem Kontextmenü der Steuerelemente hinzu (siehe Bild 2).

Bild 2: Die beiden Befehle im Kontextmenü
Das Register im Ribbon soll nur erscheinen, wenn wir ein Formular oder einen Bericht in der Entwurfsansicht bearbeiten. Dafür verwenden wir kontextbezogene Register.
Das Add-In besteht aus drei Teilen. Die Klasse ControlAligner enthält die Anbindung an Access, das Ribbon und die Kontextmenüs. Das Modul Alignment enthält die Ausrichtungslogik. Das Modul DllRegistration trägt das Add-In bei der Registrierung in die Registry ein.
Das twinBASIC-Projekt anlegen
Wie wir twinBASIC einrichten und ein COM-Add-In für Access aufbauen, haben wir in den Beiträgen twinBASIC – VB/VBA mit moderner Umgebung (www.access-im-unternehmen.de/1303) und twinBASIC – COM-Add-Ins für Access (www.access-im-unternehmen.de/1306) beschrieben. Hier legen wir ein neues Projekt vom Typ ActiveX-DLL mit dem Namen amvControlAligner an und fügen die drei Quelldateien hinzu. Das Projekt benötigt vier Verweise.
- OLE Automation: liefert die Standardtypen (stdole2.tlb).
- Microsoft Add-In Designer: liefert die Schnittstelle IDTExtensibility2 (MSADDNDR.DLL).
- Microsoft Office 16.0 Object Library: liefert die Schnittstellen IRibbonExtensibility und IRibbonControl (MSO.DLL).
- Microsoft Access 16.0 Object Library: liefert die Typen von Access (MSACC.OLB).
Außerdem schalten wir Option Explicit für das Projekt ein. Die DLL muss dieselbe Bitbreite wie die installierte Access-Version haben. In unserem Beispiel ist das die 32-Bit-Version von Access.
Nach dem Erstellen müssen wir die DLL registrieren. Das Modul DllRegistration schreibt dazu unter HKCU\SOFTWARE\Microsoft\Office\Access\Addins einen Schlüssel mit dem Namen amvControlAligner.ControlAligner und den Werten FriendlyName, Description und LoadBehavior. Der Wert 3 für LoadBehavior bewirkt, dass Access das Add-In beim Start lädt. Das Vorgehen dazu zeigt der Beitrag COM-Add-Ins und -DLLs installieren oder registrieren (www.access-im-unternehmen.de/1484).
Die Klasse des Add-Ins
Die Klasse ControlAligner implementiert zwei Schnittstellen. IDTExtensibility2 meldet dem Add-In, dass Access es lädt oder entlädt. IRibbonExtensibility liefert Access beim Laden die Ribbondefinition:
[ ClassId ("940037E2-0FB7-89-B188-3212448EA4EC") ]
Class ControlAligner
Implements IDTExtensibility2
[ WithDispatchForwarding ]
Implements IRibbonExtensibility
Private objAccess As Access.Application
Private objRibbon As IRibbonUI
Private lngDistanceLabelControl As Long
Private lngVerticalDistance As Long
Private lngAlignment As Long
Private bolColons As Boolean
Private WithEvents cbbFormAlign As CommandBarButton
Private WithEvents cbbFormColons As CommandBarButton
Private WithEvents cbbReportAlign As CommandBarButton
Private WithEvents cbbReportColons As CommandBarButton
...
End Class
Das Attribut ClassId legt die Klassen-ID fest, unter der Access das Add-In findet. Jedes Add-In benötigt eine eigene ID. Das Attribut WithDispatchForwarding vor IRibbonExtensibility sorgt dafür, dass Access die im Ribbon-XML angegebenen Callbackprozeduren anhand ihres Namens in der Klasse aufrufen kann.
Die Variable objAccess nimmt die Access-Anwendung auf und objRibbon das Ribbon-Objekt, das wir zum Aktualisieren benötigen. Die vier Variablen für die Einstellungen entsprechen den Parametern der Ausrichtung. Die vier mit WithEvents deklarierten Variablen vom Typ CommandBarButton nehmen später die Schaltflächen der Kontextmenüs auf und liefern deren Ereignis Click.
Beim Laden ruft Access die Prozedur OnConnection auf (siehe Listing 1). Sie speichert die Access-Anwendung in objAccess, lädt die gespeicherten Einstellungen und legt die Schaltflächen in den Kontextmenüs an.
Sub OnConnection(ByVal Application As Object, ByVal ConnectMode As ext_ConnectMode, ByVal AddInInst As Object, _ ByRef custom As Variant()) Implements IDTExtensibility2.OnConnection Set objAccess = Application LoadSettings SetCommandBars End Sub
Listing 1: Die Prozedur OnConnection verbindet das Add-In mit Access
Beim Entladen ruft Access die Prozedur OnDisconnection auf. Diese entfernt die Schaltflächen wieder und gibt die Objektvariablen frei. Die übrigen Prozeduren der Schnittstelle (OnAddInsUpdate, OnStartupComplete und OnBeginShutdown) benötigen wir nicht und lassen sie leer. Da sie Teile der Schnittstelle IDTExtensibility2 sind, dürfen wir sie nicht einfach weglassen.
Die Einstellungen speichern
Die Einstellungen sollen beim nächsten Start von Access wieder zur Verfügung stehen. Das Add-In speichert sie mit der Funktion SaveSetting in der Registry und liest sie in der Prozedur LoadSettings mit der Funktion GetSetting wieder ein. Fehlt ein Eintrag, verwendet LoadSettings die Standardwerte. Das sind 100 Twips für beide Abstände, die Textausrichtung Standard und keine Doppelpunkte.
Die Prozedur SaveSettings schreibt die vier Werte und aktualisiert danach das Ribbon (siehe Listing 2).
Private Sub SaveSettings() SaveSetting SETTINGS_APP, SETTINGS_SECTION, "AbstandBezeichnung", CStr(lngDistanceLabelControl) SaveSetting SETTINGS_APP, SETTINGS_SECTION, "AbstandVertikal", CStr(lngVerticalDistance) SaveSetting SETTINGS_APP, SETTINGS_SECTION, "Ausrichtung", CStr(lngAlignment) SaveSetting SETTINGS_APP, SETTINGS_SECTION, "Doppelpunkte", IIf(bolColons, "1", "0") 'Das Ribbon enthält die Steuerelemente je einmal für 'Formulare und Berichte und muss beide neu lesen. If Not objRibbon Is Nothing Then objRibbon.Invalidate End If End Sub
Listing 2: Die Prozedur SaveSettings speichert die Einstellungen
Der Aufruf der Methode Invalidate ist erforderlich, weil das Ribbon die Elemente zweimal enthält, einmal im Register für Formulare und einmal im Register für Berichte. Ändern wir einen Wert in einem der beiden Register, müssen beide ihre Anzeige neu einlesen.
Die Eingaben in die beiden Textfelder prüft die Funktion ParseDistance. Sie wandelt den Text mit der Funktion Val in eine ganze Zahl zwischen 0 und 99999 um.
Ist die Eingabe keine Zahl oder liegt sie außerhalb dieses Bereichs, liefert die Funktion den Standardwert 100. Die Obergrenze ergibt sich aus der Länge der Eingabe, die wir im Ribbon mit dem Attribut maxLength auf fünf Zeichen begrenzen:
Private Function ParseDistance(ByVal strText As String) As Long If IsNumeric(strText) Then If Val(strText) >= 0 And Val(strText) <= 99999 Then ParseDistance = CLng(Val(strText)) Exit Function End If End If ParseDistance = 100 End Function
Das Ribbon mit kontextbezogenen Registern
Access ruft beim Laden die Funktion GetCustomUI der Schnittstelle IRibbonExtensibility auf und erwartet als Ergebnis den XML-Code der Ribbondefinition. Wir erzeugen ihn in dieser Funktion (siehe Listing 3).
Private Function GetCustomUI(ByVal RibbonID As String) _ As String Implements IRibbonExtensibility.GetCustomUI Dim strXML As String strXML &= "<customUI xmlns=""http://schemas.microsoft.com/office/2009/07/customui""" & _ " onLoad=""OnRibbonLoad"">" & vbCrLf strXML &= "<ribbon startFromScratch=""false"">" & vbCrLf strXML &= "<contextualTabs>" & vbCrLf strXML &= "<tabSet idMso=""TabSetFormTools"">" & vbCrLf strXML &= GetTabXml("Form") strXML &= "</tabSet>" & vbCrLf strXML &= "<tabSet idMso=""TabSetReportTools"">" & vbCrLf strXML &= GetTabXml("Report") strXML &= "</tabSet>" & vbCrLf strXML &= "</contextualTabs>" & vbCrLf strXML &= "</ribbon>" & vbCrLf strXML &= "</customUI>" & vbCrLf Return strXML End Function
Listing 3: Die Funktion GetCustomUI liefert die Ribbondefinition
Das Attribut onLoad des Elements customUI nennt die Callbackprozedur OnRibbonLoad, über die wir das Ribbon-Objekt erhalten. Das Register soll nur im Entwurf von Formularen und Berichten erscheinen.
Deshalb enthält das Element contextualTabs zwei Elemente tabSet. Über die Werte TabSetFormTools und TabSetReportTools des Attributs idMso wählen wir die kontextbezogenen Registergruppen für Formulare und Berichte aus. Access blendet das Register ein, wenn das Objekt in der Entwurfsansicht geöffnet ist. In der Formularansicht erscheint es nicht.
Access prüft die Werte für idMso beim Laden des Ribbons.
Verwenden wir einen unbekannten Wert, meldet Access einen Fehler in der benutzerdefinierten Benutzeroberfläche.
Beide Registergruppen erhalten dasselbe Register. Weil jedes Element im Ribbon eine eindeutige ID benötigt, hängt die Funktion GetTabXml an alle IDs ein Suffix an, nämlich Form für Formulare und Report für Berichte (siehe Listing 4).
Private Function GetTabXml(ByVal strSuffix As String) As String Dim strXML As String strXML &= " <tab id=""tabAmvAlign" & strSuffix & """ label=""Steuerelemente ausrichten"">" & vbCrLf strXML &= " <group id=""grpAmvSettings" & strSuffix & """ label=""Einstellungen"">" & vbCrLf strXML &= " <editBox id=""edtDistanceLabel" & strSuffix & """ label=""Abstand Bezeichnung/Feld"" " & _ "sizeString=""99999"" maxLength=""5""" & " screentip=""Horizontaler Abstand in Twips"" " & _ "getText=""GetDistanceLabelText"" onChange=""OnDistanceLabelChange""/>" & vbCrLf strXML &= " <editBox id=""edtDistanceVertical" & strSuffix & """ label=""Vertikaler Abstand"" " & _ "sizeString=""99999"" maxLength=""5""" & " screentip=""Vertikaler Abstand zwischen den Zeilen in " & _ "Twips"" getText=""GetDistanceVerticalText"" onChange=""OnDistanceVerticalChange""/>" & vbCrLf strXML &= " <dropDown id=""ddAlignment" & strSuffix & """ label=""Beschriftung"" " & _ "screentip=""Textausrichtung der Bezeichnungsfelder""" & _ " getSelectedItemIndex=""GetAlignmentIndex"" onAction=""OnAlignmentChange"">" & vbCrLf strXML &= " <item id=""itmAlign0" & strSuffix & """ label=""Standard""/>" & vbCrLf strXML &= " <item id=""itmAlign1" & strSuffix & """ label=""Links""/>" & vbCrLf strXML &= " <item id=""itmAlign2" & strSuffix & """ label=""Zentriert""/>" & vbCrLf strXML &= " <item id=""itmAlign3" & strSuffix & """ label=""Rechts""/>" & vbCrLf strXML &= " <item id=""itmAlign4" & strSuffix & """ label=""Verteilen""/>" & vbCrLf strXML &= " </dropDown>" & vbCrLf strXML &= " <checkBox id=""chkColons" & strSuffix & """ label=""Mit Doppelpunkten"" " & _ "screentip=""Beim Ausrichten Doppelpunkte hinter die Beschriftungen setzen""" & _ " getPressed=""GetColonsPressed"" onAction=""OnColonsChange""/>" & vbCrLf strXML &= " </group>" & vbCrLf strXML &= " <group id=""grpAmvActions" & strSuffix & """ label=""Befehle"">" & vbCrLf strXML &= " <button id=""btnAlign" & strSuffix & """ label=""Steuerelemente ausrichten"" " & _ "size=""large"" imageMso=""AlignLeft""" & " screentip=""Markierte Steuerelemente ausrichten"" " & _ "onAction=""OnAlignClicked""/>" & vbCrLf strXML &= " <button id=""btnColons" & strSuffix & """ label=""Doppelpunkte anhängen"" " & _ "size=""large"" imageMso=""AlignRight""" & " screentip=""Markierten Bezeichnungsfeldern einen " & _ "Doppelpunkt anhängen"" onAction=""OnColonsClicked""/>" & vbCrLf strXML &= " </group>" & vbCrLf strXML &= " </tab>" & vbCrLf Return strXML End Function
Listing 4: Die Funktion GetTabXml erzeugt das Register
Die Gruppe Einstellungen enthält zwei Elemente editBox für die Abstände, ein Element dropDown mit den fünf Einträgen Standard, Links, Zentriert, Rechts und Verteilen für die Textausrichtung der Bezeichnungsfelder sowie ein Element checkBox für die Doppelpunkte. Die Reihenfolge der Einträge in der Dropdownliste entspricht den Werten der Eigenschaft TextAlign, also 0 für Standard bis 4 für Verteilen. Die Gruppe Befehle enthält die Schaltflächen Steuerelemente ausrichten und Doppelpunkte anhängen.
Im Formularentwurf zeigt Access es hinter dem Register Format an (siehe Bild 3).

Bild 3: Das Register Steuerelemente ausrichten im Entwurf eines Formulars
Die Schaltflächen zeigen eingebaute Symbole von Office, die wir über das Attribut imageMso angeben. Eigene Bilder binden wir nach dem Muster aus dem Beitrag Benutzerdefinierte Bilder in twinBASIC (www.access-im-unternehmen.de/1325) ein.
Unser exklusives Angebot für Dich!
(Gilt für den Abschluss eines Jahres-Abonnements im ersten Jahr, danach 189,-/Jahr)
Hier geht’s weiter →Die ersten 4 Wochen kostenlos testen – voller Zugriff auf alle Artikel, vollständigen Code und Beispieldatenbanken. Kein Risiko: Wenn es nicht passt, kündigst Du einfach innerhalb der ersten vier Wochen.
Hast Du eine konkrete Frage zu Deiner eigenen Access-Anwendung?
Vielleicht stellt Deine Anwendung Dich vor eine Herausforderung, zu der Du bisher keine Lösung findest. Schlechte Performance, kein ausreichender Zugriffsschutz, Du bist unsicher über Dein Datenmodell oder Dein Code liefert unerklärliche Fehler?
In unserem kostenlosen Access-Audit schaut sich André Minhorst persönlich gemeinsam mit Dir Deine Lösung per Zoom an – und zeigt Dir, wo Datenmodell, VBA-Code, Ergonomie und Sicherheit Optimierungspotenzial bieten.
Jetzt kostenloses Access-Audit anfordern →