Mit Access 2007 hat Microsoft das Ribbon eingeführt und Access seiner Werkzeuge beraubt, Menüleisten und über die Benutzeroberfläche zu erstellen. Nun gut: Menüleisten gibt es nicht mehr, aber können in professionellen Anwendung doch sehr hilfreich und vor allem ergonomisch sein. Dieser Artikel zeigt den alternativen Weg zur Erstellung von den über VBA. Beispieldatenbank Die Beispiele dieses Artikels finden Sie in der Datenbank 1310_Kontextmenues.mdb. In Access 2003 und jünger konnten Sie nicht nur, sondern auch andere Menüs zusammenklicken. Wenn Sie jedoch einmal dynamisch generierte benötigten, mussten Sie auch dort zu VBA greifen beispielsweise, um verschiedene Werte einer Tabelle per Kontextmenü zur Auswahl anzubieten. Dementsprechend können Sie die in diesem Artikel vorgestellten Techniken auch in Datenbanken einsetzen, die mit Access älteren Jahrgangs als 2007 erstellt wurden. Sollte Ihnen der Begriff Kontextmenü fremd sein: Das sind diejenigen Menüs, die aufgeklappt werden, wenn Sie mit der rechten Maustaste auf ein Objekt klicken (zum Beispiel auf eine Datei oder einen Ordner im Windows Explorer). Diese können Sie auch für Ihre eigenen Anwendungen anlegen und an den entsprechenden Stellen anbieten. Einsatzbereiche Access selbst bietet ja bereits eine ganze Reihe eingebauter für die verschiedenen Objekte an für die Spaltenköpfe oder die Felder der Datenblattansicht, für Steuerelemente in Formularen, für Formulare selbst et cetera (siehe Bild 1). Warum sollten Sie also eigene hinzufügen? Oder sogar die vorhandenen ersetzen? Ganz einfach: Wenn Sie eine eigene Anwendung erstellen, dann enthält diese auch die von Ihnen definierte Benutzeroberfläche und dieser können Sie neben den üblichen Ribbons, Formularen und Steuerelementen auch hinzufügen. Sehr sinnvoll sind beispielsweise in Zusammenhang mit TreeView-Steuerelementen, da Sie so leicht Funktionen zu den angezeigten Elementen hinzufügen können etwa zum Bearbeiten des angeklickten Eintrags, zum Löschen des Beitrags oder auch zum Anlegen neuer Einträge. Vielleicht möchten Sie auch nur eingebaute Steuerelemente anpassen, indem Sie vorhandene Einträge Bild 1: Beispiel für ein eingebautes Kontextmenü ausblenden oder deaktivieren oder eigene Einträge hinzufügen. So könnten Sie zum Beispiel das Kontextmenü eines Textfeldes um den Kopieren- oder den Ausschneiden-Befehl erleichtern, wenn Sie nicht möchten, dass der Benutzer auf einfache Weise die Inhalte des Textfeldes in die Zwischenablage einfügt. Für all diese Zwecke müssen wir zunächst einen Blick auf die VBA-Objekte zum Steuern des Aussehens von werfen. per VBA Dazu müssen Sie zunächst wissen, dass die keine Access-interne Komponente sind, sondern dass die notwendigen Objekte in der Office-Bibliothek enthalten sind. Und da diese einem VBA-Pro- www.access-basics.de Seite 3
jekt standardmäßig nicht zugewiesen ist, fügen Sie den entsprechenden Verweis nun zunächst hinzu. Dazu öffnen Sie den VBA-Editor (zum Beispiel mit Strg + G) und betätigen den Menübefehl Extras Verweise. Fügen Sie dort den Eintrag Microsoft Office x.0 Object Library hinzu (siehe Bild 2). Anschließend können Sie im Code- Fenster auf die Objekte dieser Bibliothek zugreifen. Zu Testzwecken wollen wir zunächst eine Liste aller aktuell in der Datenbankdatei enthaltenen im Direktfenster ausgeben. Dabei lernen Sie dann gleich einige Objekte und Eigenschaften der unter VBA kennen. Der dazu notwendige Code steckt in der Prozedur AlleMenues und sieht wie folgt aus: Die Prozedur deklariert eine Variable des Typs CommandBar, was einer Menüleiste, einer Symbolleiste oder einem Kontextmenü entspricht. Mithilfe dieser Variablen durchläuft die Prozedur in einer For Each-Schleife alle Elemente der CommandBars- Auflistung des Application-Objekts, was der aktuellen Instanz von Access entspricht. Sie gibt dann über die Eigenschaft Name den Namen des aktuellen CommandBar-Objekts aus. Wenn Sie diese Prozedur eingeben und starten, rauschen eine ganze Reihe Einträge durch den Direktbereich. Wieviele waren das? Das finden Sie durch Absetzen der folgenden Anweisung im Direktfenster heraus:? Commandbars.Count 209 wir die Namen der Menüs mit Typ ausgeben, erweitern Sie die Debug. Print-Zeile der obigen Prozedur wie folgt: Debug.Print cbr.name, cbr.type Eingebaut oder nicht? Eine weitere interessante Eigenschaft des CommandBar-Objekts heißt BuiltIn. Diese Eigenschaft gibt den Wert True zurück, wenn es sich um ein benutzerdefiniertes Menü handelt, also eines, dass durch den Code der Anwendung, durch Import oder durch Erstellen mit den Tools von Access 2003 und älter erstellt wurde. Wenn Sie also beispielsweise einmal alle eingebauten entfernen möchten, liefert die Eigenschaft Builtin alle notwendigen Informationen. Public Sub AlleMenues() For Each cbr In µ Application.CommandBars Debug.Print cbr.name Next cbr Unter Access 2010 etwa gibt es also über 200 Menüs! Allerdings sind dies nicht alles. Informationen über die Art des Menüs liefert die Eigenschaft Type. Wollen Arten von Menüleisten Um herauszufinden, welche der CommandBar-Objekte Kontext- Bild 2: Verweis auf die Office-Bibliothek Bild 3: Ermitteln der Bedeutung der Type-Werte von CommandBar-Objekten www.access-basics.de Seite 4
Bild 4: Auch IntelliSense liefert die Namen der Konstanten einer Enumeration. menüs repräsentieren, müssen wir zunächst ermitteln, was die Zahlenwerte 0, 1 und 2 bedeuten. Dazu bemühen wir den Objekt-Explorer (F2), siehe Bild 3. Dort filtern wir zunächst nach der Bibliothek, in der sich die entsprechenden Konstanten befinden könnten, also Office (für Microsoft Office x.0 Object Library). BarType, finden Sie schnell die möglichen Werte: 0: msobartypenormal (Symbolleiste) 1: msobartypemenubar (Menüleiste) Bild 5: Kontextmenü der Entwurfsansicht eines Formulars Fügen wir also eine If...Then-Bedingung hinzu, welche nur noch die Namen der ausgibt: If cbr.type = msobartypepopup Then Debug.Print cbr.name, cbr.type End If Dann suchen wir nach dem Namen der Eigenschaft, hier Type. Darüber gelangen wir an die Type-Eigenschaft des CommandBar-Objekts. Wählen Sie diese aus, erhalten Sie unten weitere Informationen nämlich den Namen der Enumeration, welche die Konstanten für die Eigenschaft Type enthält (Sie hätten auch direkt den Weg über das CommandBar-Objekt gehen und dann nach der Eigenschaft Type suchen können). Klicken Sie dort auf die Enumeration mso- Public Sub KontextmenuesMitSteuerelementen() Set cbr = CommandBars("Form Design Form") Debug.Print cbr.name, cbr.type For Each cbc In cbr.controls Debug.Print " " & cbc.caption Next cbc Listing 1: Ausgabe der Steuerelemente eines 2: msobartypepopup (Kontextmenü) Es gibt allerdings noch einen anderen Weg, um an die Konstanten zu gelangen. Dazu geben Sie einfach eine Anweisung in den VBA-Editor ein, mit der Sie der Eigenschaft Type einen Wert zuweisen wollen. Dank IntelliSense liefert der VBA-Editor an dieser Stelle die gewünschten Informationen (siehe Bild 4). Steuerelemente ausgeben Als Nächstes interessieren uns natürlich die Einträge der einzelnen. Um diese zu referenzieren und auszugeben, legen wir wiederum eine entsprechende Objektvariable fest, die diesmal den Namen cbc hat und den Datentyp CommandBarControl aufweist. Also geben wir einmal alle Steuerelemente eines bestimmten eingebauten aus, zum Beispiel Form Design Form. Dabei handelt es sich um das Kontextmenü, das erscheint, wenn Sie in den leeren Bereich im Formularentwurf klicken (siehe Bild 5). Der Code, um die Namen der Steuerelemente auszugeben, sieht wie in Listing 1 aus. Die Prozedur stellt die Variable cbr auf das genannte Kontextmenü ein und durchläuft dann www.access-basics.de Seite 5
Bild 6: Ausgabe aller Controls eines Command- Bars über die Variable cbc alle Elemente der Controls-Auflistung des CommandBar-Objekts. Das Ergebnis sieht dann wie in Bild 6 aus. Die Prozedur hat alle Elemente des CommandBar-Objekts in das Direktfenster geschrieben. Sie werden möglicherweise feststellen, dass ein Element nur im Direktbereich ausgegeben wird, aber nicht im Kontextmenü erscheint nämlich der Eintrag &Berichtseigenschaften. Das ist mit den Anweisungen aus Listing 2 schnell erledigt. Die erste Set-Anweisung referenziert das zu erweiternde Kontextmenü. Die zweite setzt einen Verweis auf das mit der Add-Methode hinzugefügte Element des Typs msocontrolbutton. Damit das hinzugefügte Element beim nächsten Öffnen von Access wieder verschwunden ist, erhält der letzte Parameter namens Temporary den Wert True. Dem neuen Eintrag verpassen wir mit der Caption-Eigenschaft noch die Bezeichnung Test. Bild 7: Kontextmenü mit einem benutzerdefinierten Eintrag Wie Bild 7 zeigt, erscheint der neue Eintrag im angegebenen Kontextmenü, das sich übrigens auch durch einen Rechtsklick auf den Schnittpunkt der beiden Lineale des Formularentwurfs anzeigen lässt. Kontextmenü anlegen Nun wollen wir uns der Erstellung komplett benutzerdefinierter zuwenden. Dazu müssen Sie zunächst ein neues CommandBar- Objekt erstellen und dieses mit einer Objektvariablen etwa namens cbr referenzieren (siehe Listing 3). Dazu Eine Erklärung hierfür gibt es nicht außer, dass Microsoft da wohl einen Eintrag vergessen hat. Gelegentlich kommt es auch vor, dass ein Eintrag in einer bestimmten Konstellation nicht im Kontextmenü angezeigt wird. Woher aber weiß ich, dass das Kontextmenü Form Design Form tatsächlich im vermuteten Kontext angezeigt wird? Vielleicht wird dieses ja für einen ganz anderen Zweck benötigt, was auch den zusätzlichen Eintrag erklären würde? Um dies zu prüfen, gibt es einen einfachen Weg: Wir fügen dem Kontextmenü namens Form Design Form einfach einen Eintrag hinzu, beispielsweise mit dem Namen Test. Public Sub KontextmenueErweitern() Set cbr = CommandBars("Form Design Form").Caption = "Test" Listing 2: Erweitern eines bestehenden Public Sub KontextmenueAnlegen() On Error Resume Next CommandBars("cbrTest").Delete On Error GoTo 0 Set cbr = CommandBars.Add("cbrTest", msobarpopup, False, True).Caption = "Test".OnAction = "=Test()" cbr.showpopup Listing 3: Anlegen eines neuen mit einem Beispieleintrag www.access-basics.de Seite 6
nutzen Sie die Add-Methode der CommandBars-Auflistung, wobei wir gleich alle vier Parameter dieses Aufrufs nutzen: Danach fügen Sie mit der Add- Methode, wie bereits gesehen, die gewünschten Kontextmenü-Einträge hinzu. Diese referenzieren Sie wiederum mit einer Objektvariablen namens cbc, damit Sie ihre Eigenschaften anschließend einstellen können. In unserem Fall sollen nur eine Beschriftung hinzugefügt werden (Caption) und ein Wert für die Eigenschaft OnAction. Name: Name des Position: Position, hier msobar- Popup MenuBar: Nur True, wenn eine Menüleiste erstellt werden soll, hier also False Temporary: Gibt an, ob das Menü beim Schließen der Anwendung wieder gelöscht werden soll. Beim Hinzufügen eines Command- Bar-Objekts ist außerdem zu beachten, dass es gegebenenfalls bereits ein Menü gleichen Namens gibt. Um diesem Fall vorzubeugen, löschen wir gleich zu Beginn mit der Delete- Methode ein eventuell vorhandenes Element der CommandBars-Auflistung gleichen Namens. Da dies wiederum einen Fehler auslöst, wenn das Menü doch noch nicht vorhanden sein sollte, setzen wir für die Ausführung dieser Anweisung die Fehlerbehandlung außer Kraft (On Error Resume Next/On Error Goto 0). Letztere legt fest, welche VBA- Funktion beim Auslösen des Kontextmenü- Eintrags aufgerufen werden soll in diesem Fall heißt diese Funktion Test(). Schließlich zeigt die ShowPopup-Methode des CommandBar- Objekts das Kontextmenü an. Wenn Sie die Prozedur KontextmenueAnlegen vom VBA-Fenster aus starten, erscheint das Kontextmenü auch dort (siehe Bild 8). Bild 8: Anzeige eines per VBA erzeugten Kontextmenü-Aktion Fehlt noch die Funktion, die durch das Auswählen des Kontextmenü-Ein trags ausgelöst wird. Diese legen Sie wie folgt in einem Standardmodul an: Public Function Test() MsgBox "Test" End Function Public Sub KontextmenueMitGruppen() On Error Resume Next CommandBars("cbrTest").Delete On Error GoTo 0 Set cbr = CommandBars.Add("cbrTest", msobarpopup, False, True).Caption = "Anzeigen".OnAction = "=Gruppe('Anzeigen')".Caption = "Drucken".OnAction = "=Gruppe('Drucken')".Caption = "Löschen".OnAction = "=Gruppe('Löschen')".BeginGroup = True cbr.showpopup Listing 4: Mehrere Einträge anlegen, von denen der letzte eine neue Gruppe beginnt und die verschiedene Parameter an eine Funktion übergeben www.access-basics.de Seite 7
Mehrere Einträge anlegen Wenn Sie mehr als einen Eintrag zum Kontextmenü hinzufügen möchten, werden Sie die Einträge möglicherweise gruppieren wollen. Dies geschieht durch einen vertikalen Strich zwischen zwei Einträgen. Eine solche Gruppierung erreichen Sie, wenn Sie für das erste Element einer Gruppe die Eigenschaft BeginGroup auf den Wert True einstellen. Parameter übergeben Der aufzurufenden Funktion, die Sie mit der Eigenschaft OnAction eines Steuerelements festlegen, können Sie auch Parameter übergeben. Wie dies gelingt, zeigt das Beispiel aus Listing 5. Die dort angelegten drei Einträge rufen alle die gleiche Funktion auf, übergeben aber ihre Beschriftung als Parameter. Außerdem wird der letzte Eintrag als erstes Element einer neuen Gruppe angelegt (Begin- Group = True). Die Funktion Gruppe erwartet die Beschriftung des Befehls als Parameter und gibt diesen als Teil einer Meldung aus (siehe ). Wenn Sie also einmal mehrere Einträge zu einem Kontextmenü hinzufügen, welche die gleiche Funktion mit verschiedenen Parametern aufrufen sollen, können Sie diese Technik verwenden. So sparen Sie sich das Anlegen jeweils einer eigenen Funktion für jeden Kontextmenü-Eintrag. Public Function Gruppe(strAufrufenderEintrag As String) MsgBox "Aufruf durch '" & straufrufendereintrag & "'" End Function Listing 5: Funktion mit Parameter Aktivieren und Deaktivieren Mit der Enabled-Eigenschaft können Sie einzelne Einträge beliebiger aktivieren oder deaktivieren. Bereits beim Anlegen können Sie damit festlegen, ob der Eintrag aktiviert werden soll (Standardeinstellung) oder ob der Benutzer ihn nicht betätigen können soll (Enabled = False). Zugriff auf vorhandene Wenn Sie ein bereits angelegtes Kontextmenü (oder ein eingebautes) anzeigen möchten, müssen Sie nur seinen Namen kennen und die folgende Anweisung ausführen: Commandbars("cbrTest").ShowPopup Sie können auch einzelne Einträge aktivieren oder deaktivieren. Auch das gelingt mit einer einzigen Anweisung. Sie müssen nur den Namen des CommandBar-Objekts und den des Control-Objekts für den Eintrag kennen: CommandBars("cbrTest"). Controls("Löschen").Enabled = False Wenn Sie das Kontextmenü direkt danach anzeigen, sieht dieses etwa wie in Bild 9 aus. Ein- und Ausblenden Sie können einen Kontextmenü- Eintrag auch gleich komplett ausblenden, sodass der Benutzer diesen überhaupt gar nicht erst sieht. Dies erledigen Sie mit der Visible- Eigenschaft. Die folgende Anweisung macht einen Eintrag unsichtbar: Bild 9: Kontextmenü mit deaktiviertem Eintrag CommandBars("cbrTest"). Controls("Löschen").Visible = False Einträge kopieren Wenn Sie vorhandene Einträge in nutzen möchten, können Sie diese mit der Copy-Methode kopieren. Dazu deklarieren Sie wie in Listing 6 zwei CommandBar-Objekte. Das erste (cmdneu) ist das Zielmenü, das zweite (cmdquelle) liefert die gewünschten Einträge. Das neue Menü wird nach dem Löschen eines gegebenenfalls gleichnamigen Menüs neu erstellt. Das Quellmenü wird mit cbrquelle referenziert. Dann ruft die Prozedur für die zu kopierenden Einträge die Copy-Methode auf und gibt das Zielmenü als Parameter an. Das Ergebnis sehen Sie in Bild 10. Der Vorteil bei der Verwendung eingebauter Kontextmenü-Einträge ist, dass diese automatisch deaktiviert werden, wenn sie nicht zur Verfügung stehen. Im vorherigen Beispiel wird so der Eintrag Einfügen abgeblendet, wenn die Zwischenablage leer ist. Kontextmenü bei Rechtsklick Nun haben wir ein Kontextmenü angezeigt, indem wir die entsprechende Prozedur direkt über den VBA-Editor www.access-basics.de Seite 8
aufgerufen haben. Kümmern wir uns nun also darum, das Kontextmenü an Ort und Stelle auszuführen beispielsweise durch einen Rechtsklick auf den Detailbereich eines ansonsten leeren Formulars. Das Formular heißt frmkontextmenues und soll beispielsweise einen Eintrag namens Formular schließen im Kontextmenü anzeigen. Als Erstes müssen wir das verantwortliche Ereignis identifizieren. Wann erscheint also ein Kontextmenü beim Herunterdrücken oder beim Loslassen der rechten Maustaste? Dies geschieht erst beim Loslassen der Maustaste. Also legen wir eine Prozedur an, die durch das Ereignis Bei Maustaste auf des Detailbereichs ausgelöst wird (siehe Bild 11). Public Sub KontextmenueMitEingebautenEintraegen() Dim cbrneu As Office.CommandBar Dim cbrquelle As Office.CommandBar On Error Resume Next CommandBars("cbrTest").Delete On Error GoTo 0 Set cbrneu = CommandBars.Add("cbrTest", msobarpopup, False, True) Set cbrquelle = CommandBars("Form Design Control") cbrquelle.controls(7).copy cbrneu cbrquelle.controls(8).copy cbrneu cbrquelle.controls(9).copy cbrneu cbrneu.showpopup Listing 6: Eingebaute Einträge zu Kontextmenü hinzufügen Die Prozedur sieht wie in Listing 7 aus. Das Anklicken des Kontextmenü-Eintrags Formular schließen soll die folgende Funktion auslösen, die Sie sowohl im Klassenmodul des betroffenen Formulars als auch in einem Standardmodul unterbringen können: Bild 10: Ein aus vorhandenen Einträgen zusammengestelltes Kontextmenü Public Function FormularSchliessen() DoCmd.Close acform, Me.Name End Function Wenn diese Funktion nur im Kontext des Formulars aufgerufen werden kann, ist es sinnvoller, diese im Klassenmodul des Formulars anzulegen. Die Ereignisprozedur Bei Maustaste ab prüft, welche Maustaste der das Ereignis ausgelöst hat. Handelt es sich um die rechte, liefert der Para- Bild 11: Anlegen einer Prozedur, die durch einen rechten Mausklick ausgelöst wird www.access-basics.de Seite 9
meter Button den Wert der Konstanten acrightbutton. In diesem Fall zeigt das Formular wie in Bild 12 das Kontextmenü an. Wenn Sie den einzigen Eintrag des anklicken, schließt die Funktion FormularSchliessen zwar das Formular, es erscheint jedoch kurz vorher noch das eigentlich durch den rechten Mausklick ausgelöste Kontextmenü. Eingebautes Kontextmenü ersetzen Also müssen wir noch einen Weg finden, die Anzeige des eingebauten zu unterbinden. Dies erreichen Sie, indem Sie die Eigenschaft Kontextmenü des Formulars auf den Wert Nein einstellen. Unter VBA stellen Sie dazu die Eigenschaft ShortcutMenu des Formulars auf den Wert False ein. Dies hat allerdings weitreichende Folgen: Zwar erscheint nach dem benutzerdefinierten Kontextmenü nicht mehr das eingebaute Kontextmenü, aber dafür zeigt das Formular überhaupt keine eingebauten mehr an. Wenn Sie also etwa das eingebaute Kontextmenü für den Einsatz in Textfeldern oder anderen Steuerelementen anzeigen lassen wollen, müssen Sie die Eigenschaft ShortcutMenu zu Private Sub Detailbereich_MouseUp(Button As Integer, Shift As Integer, _ X As Single, Y As Single) If Button = acrightbutton Then On Error Resume Next CommandBars("cbrDetailbereich").Delete On Error GoTo 0 Set cbr = CommandBars.Add("cbrDetailbereich", msobarpopup, False, True).Caption = "Formular schließen".onaction = "=FormularSchliessen()" 2 cbr.showpopup End If Listing 7: Kontextmenü beim Rechtsklick auf den Detailbereich anzeigen gegebener Zeit wieder auf den Wert True einstellen. Dies können Sie theoretisch in einer Prozedur erledigen, die durch das Ereignis Beim Hingehen oder Bei Fokuserhalt ausgelöst wird. Was aber, wenn es sich um das einzige Steuerelement handelt, das ohnehin direkt beim Öffnen aktiviert wird? Dann geht unser Plan nicht auf. Also verwenden wir direkt eine Ereignisprozedur, die durch das Anklicken des jeweiligen Steuerelements ausgelöst wird, etwa Bei Maustaste ab: Private Sub txt_mousedown(...) Me.ShortcutMenu = True Bild 12: Kontextmenü beim rechten Mausklick auf den Detailbereich Zusammenfassung und Ausblick Dieser Artikel hat die Grundlagen der Programmierung benutzerdefinierter erläutert. In weiteren Artikeln erfahren Sie, wie Sie in der Praxis einsetzen und wie Sie diese mit Icons ausstatten. www.access-basics.de Seite 10