Home > Artikel > Ausgabe 10/2013 > Kontextmenüs

Kontextmenüs

  PDF ansehen

  Download PDF und Beispieldatenbank

Mit Access 2007 hat Microsoft das Ribbon eingeführt und Access seiner Werkzeuge beraubt, Menüleisten und Kontextmenüs über die Benutzeroberfläche zu erstellen. Nun gut: Menüleisten gibt es nicht mehr, aber Kontextmenüs können in professionellen Anwendung doch sehr hilfreich und vor allem ergonomisch sein. Dieser Artikel zeigt den alternativen Weg zur Erstellung von Kontextmenüs – den über VBA.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 1310_Kontextmenues.mdb.

Kontextmenüs

In Access 2003 und jünger konnten Sie nicht nur Kontextmenüs, sondern auch andere Menüs zusammenklicken. Wenn Sie jedoch einmal dynamisch generierte Kontextmenüs 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 Kontextmenüs 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 Kontextmenüs 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 Kontextmenüs hinzufügen? Oder sogar die vorhandenen Kontextmenüs 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 Kontextmenüs hinzufügen.

Beispiel für ein eingebautes Kontextmenü

Bild 1: Beispiel für ein eingebautes Kontextmenü

Sehr sinnvoll sind Kontextmenüs 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 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 Kontextmenüs werfen.

Kontextmenüs per VBA

Dazu müssen Sie zunächst wissen, dass die Kontextmenüs keine Access-interne Komponente sind, sondern dass die notwendigen Objekte in der Office-Bibliothek enthalten sind. Und da diese einem VBA-Projekt 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).

Verweis auf die Office-Bibliothek

Bild 2: Verweis auf die Office-Bibliothek

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 Kontextmenüs im Direktfenster ausgeben. Dabei lernen Sie dann gleich einige Objekte und Eigenschaften der Kontextmenüs unter VBA kennen. Der dazu notwendige Code steckt in der Prozedur AlleMenues und sieht wie folgt aus:

Public Sub AlleMenues()

     Dim cbr As Office.CommandBar

     For Each cbr In Application.CommandBars

         Debug.Print cbr.Name

     Next cbr

End Sub

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

Unter Access 2010 etwa gibt es also über 200 Menüs! Allerdings sind dies nicht alles Kontextmenüs. Informationen über die Art des Menüs liefert die Eigenschaft Type. Wollen 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 Kontextmenüs entfernen möchten, liefert die Eigenschaft Builtin alle notwendigen Informationen.

Arten von Menüleisten

Um herauszufinden, welche der CommandBar-Objekte Kontextmenü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).

Ermitteln der Bedeutung der Type-Werte von CommandBar-Objekten

Bild 3: Ermitteln der Bedeutung der Type-Werte von CommandBar-Objekten

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 msoBarType, finden Sie schnell die möglichen Werte:

  • 0: msoBarTypeNormal (Symbolleiste)
  • 1: msoBarTypeMenuBar (Menüleiste)
  • 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).

Auch IntelliSense liefert die Namen der Konstanten einer Enumeration.

Bild 4: Auch IntelliSense liefert die Namen der Konstanten einer Enumeration.

Fügen wir also eine If...Then-Bedingung hinzu, welche nur noch die Namen der Kontextmenüs ausgibt:

If cbr.Type = msoBarTypePopup Then

     Debug.Print cbr.Name, cbr.Type

End If

Steuerelemente ausgeben

Als Nächstes interessieren uns natürlich die Einträge der einzelnen Kontextmenüs. 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 Kontextmenüs 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).

Kontextmenü der Entwurfsansicht eines Formulars

Bild 5: Kontextmenü der Entwurfsansicht eines Formulars

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

Ausgabe aller Controls eines CommandBars

Bild 6: Ausgabe aller Controls eines CommandBars

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.

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.

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ü mit einem benutzerdefinierten Eintrag

Bild 7: Kontextmenü mit einem benutzerdefinierten Eintrag

Kontextmenü anlegen

Nun wollen wir uns der Erstellung komplett benutzerdefinierter Kontextmenüs 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 nutzen Sie die Add-Methode der CommandBars-Auflistung, wobei wir gleich alle vier Parameter dieses Aufrufs nutzen:

  • Name: Name des Kontextmenüs
  • Position: Position, hier msoBarPopup
  • 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 CommandBar-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).

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.

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

Anzeige eines per VBA erzeugten Kontextmenüs

Bild 8: Anzeige eines per VBA erzeugten Kontextmenüs

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

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 (siehe Listing 4).

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 (BeginGroup = True). Die Funktion Gruppe erwartet die Beschriftung des Befehls als Parameter und gibt diesen als Teil einer Meldung aus.

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.

Aktivieren und Deaktivieren

Mit der Enabled-Eigenschaft können Sie einzelne Einträge beliebiger Kontextmenüs 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 Kontextmenüs

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.

Kontextmenü mit deaktiviertem Eintrag

Bild 9: Kontextmenü mit deaktiviertem Eintrag

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:

CommandBars("cbrTest").Controls("Löschen").Visible = False

Einträge kopieren

Wenn Sie vorhandene Einträge in Kontextmenüs 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.

Ein aus vorhandenen Einträgen zusammengestelltes Kontextmenü

Bild 10: Ein aus vorhandenen Einträgen zusammengestelltes Kontextmenü

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

Anlegen einer Prozedur, die durch einen rechten Mausklick ausgelöst wird

Bild 11: Anlegen einer Prozedur, die durch einen rechten Mausklick ausgelöst wird

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:

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 Parameter Button den Wert der Konstanten acRightButton. In diesem Fall zeigt das Formular wie in Bild 12 das Kontextmenü an.

Kontextmenü beim rechten Mausklick auf den Detailbereich

Bild 12: Kontextmenü beim rechten Mausklick auf den Detailbereich

Wenn Sie den einzigen Eintrag des Kontextmenüs 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 Kontextmenüs 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 Kontextmenüs 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 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

End Sub

Zusammenfassung und Ausblick

Dieser Artikel hat die Grundlagen der Programmierung benutzerdefinierter Kontextmenüs erläutert. In weiteren Artikeln erfahren Sie, wie Sie Kontextmenüs in der Praxis einsetzen und wie Sie diese mit Icons ausstatten.