window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-TCJTE9L38H');

Ribbon anpassen, Teil 2

Lies diesen Artikel und viele weitere mit einem kostenlosen, einwöchigen Testzugang.

Das Ribbon, in der deutschen Version von Office auch Menüband genannt, ist die Menüleiste für Office-Anwendungen und somit auch für Access. Sie können die Befehle im Ribbon jedoch nicht nur während der Entwicklung von Access-Anwendungen nutzen, sondern dieses für eigene Anwendungen so anpassen, dass der Benutzer die Funktionen dieser Anwendung darüber aufrufen kann. Dabei können Sie die eingebauten Elemente sogar weitgehend ausblenden, sodass man Ihrer Anwendung nur noch an wenigen Stellen ansieht, dass es sich überhaupt um eine Access-Anwendung handelt. Diese Artikelreihe liefert alle Informationen, die Sie für die Arbeit mit dem Ribbon und für seine Anpassung und Programmierung benötigen. Dabei lernen Sie auch alle Steuerelemente und ihre Eigenschaften kennen.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 2106_RibbonAnpassen.accdb.

Das dynamicMenu-Element

Dieses Element nimmt eine besondere Rolle unter den Ribbonelementen ein, denn Sie können nicht nur einzelne Attribute mit get…-Attributen manipulieren, sondern Sie können den gesamten Inhalt des Menüs per VBA im XML-Format zusammensetzen und an ein spezielles Callbackattribut zurückgeben.

Das Beispiel finden Sie in der Tabelle USysRibbons unter dem Namen Beispiel dynamicMenu. Der Code befindet sich im Modul mdlRibbonsDynamicMenu.

Das Attribut zum Füllen des Elements heißt getContent und wir betten dieses wie folgt in das dynamicMenu-Element ein:

<group id="grp0" label="Beispiel dynamicMenu">
   <dynamicMenu id="dyn" label="Dynamic Menu:"                               getContent="getContent" />
</group>

Eine sehr einfache Variante der dadurch aufgerufenen Callbackprozedur getContent sieht wie in Listing 1 aus.

Sub getContent(control As IRibbonControl, ByRef content)
     Dim strXML As String
     Dim i As Integer
     strXML = "<menu xmlns=""http://schemas.microsoft.com/office/2009/07/customui"">"
     For i = 1 To 10
         strXML = strXML & "<button id=""btn" & i & """ label=""Button " & i & """/>"
     Next i
     strXML = strXML & "</menu>"
     content = strXML
End Sub

Listing 1: Beispiel für ein dynamicMenu-Element

Die Prozedur erhält einen Verweis auf das auslösende dynamicMenu-Element als Parameter und erhält einen weiteren Parameter namens content, der den Inhalt des dynamicMenu-Elements entgegennimmt.

Sie füllt sukzessive eine Variable namens strXML. Die erste Zeile enthält das menu-Element:

<menu xmlns="http://schemas.microsoft.com/office/2009/07/customui">

Sie durchläuft eine Schleife von 1 bis 10, in der Sie Einträge wie die folgenden zu der ersten Zeile hinzufügt:

<button id="btn1" label="Button 1"/>
<button id="btn2" label="Button 2"/>
<button id="btn3" label="Button 3"/>
...
<button id="btn10" label="Button 10"/>

Schließlich hängt die Prozedur noch die abschließende Zeile mit dem schließenden menu-Element an:

</menu>

Schließlich gibt die Prozedur das in strXML zusammengestellte Ergebnis mit dem Parameter content zurück. Das Ergebnis dieses Beispiels finden Sie in Bild 1.

dynamicMenu-Element

Bild 1: dynamicMenu-Element

dynamicMenu-Element aus der Datenbank füllen

Genau wie über eine Schleife können Sie das dynamicMenu-Element auch mit den Daten einer Tabelle aus der Datenbank füllen. Um den Spaß zu erhöhen, machen wir das gleich mal mit zwei Ebenen – Kategorien und Artikeln. Das Ergebnis soll wie in Bild 2 aussehen und ein Klick auf einen der Artikel soll auch noch ein Formular öffnen, das diesen Artikel anzeigt.

dynamicMenu-Element mit Kategorien und Artikeln

Bild 2: dynamicMenu-Element mit Kategorien und Artikeln

Das Beispiel finden Sie in der Tabelle USysRibbons unter dem Namen Beispiel dynamicMenu aus Datenbank. Der Code befindet sich im Modul mdlRibbonsDynamicMenu.

Die Prozedur, die durch das Callbackattribut getContent ausgelöst wird, finden Sie in Listing 2. Die Prozedur referenziert mit der Variablen db das aktuelle Database-Objekt und mit rstKategorien ein Recordset auf Basis der Tabelle tblKategorien. Sie erstellt dann zunächst das umschließende menu-Element und steigt dann in eine Do While-Schleife über alle Kategorien ein. Darin fügt sie zunächst für jede Kategorie ein neues menu-Element hinzu und legt seine Attribute fest. id erhält den Buchstaben k und die KategorieID, label erhält den Kategorienamen und image den Wert folder.

Sub dynKategorienUndArtikel_getContent(control As IRibbonControl, ByRef content)
     Dim db As DAO.Database
     Dim rstKategorien As DAO.Recordset
     Dim rstArtikel As DAO.Recordset
     Dim strXML As String
     Dim i As Integer
     Set db = CurrentDb
     Set rstKategorien = db.OpenRecordset("SELECT * FROM tblKategorien", dbOpenDynaset)
     strXML = "<menu xmlns=""http://schemas.microsoft.com/office/2009/07/customui"">" & vbCrLf
     Do While Not rstKategorien.EOF
         strXML = strXML & "  <menu id=""k" & rstKategorien!KategorieID & """ label=""" & rstKategorien!Kategoriename _
             & """ image=""folder"">" & vbCrLf
         Set rstArtikel = db.OpenRecordset("SELECT * FROM tblArtikel WHERE KategorieID = " & rstKategorien!KategorieID, dbOpenDynaset)
         Do While Not rstArtikel.EOF
             strXML = strXML & "    <button id=""a" & rstArtikel!ArtikelID & """ label=""" & rstArtikel!Artikelname _
                 & """ onAction=""btnArticle_onAction"" tag=""" & rstArtikel!ArtikelID & """ image=""form""/>" & vbCrLf
             rstArtikel.MoveNext
         Loop
         strXML = strXML & "  </menu>" & vbCrLf
         rstKategorien.MoveNext
     Loop
     strXML = strXML & "</menu>"
     content = strXML
End Sub

Listing 2: Beispiel zum Füllen eines dynamicMenu-Elements aus den Daten einer Datenbank

Danach erstellt die Prozedur ein Recordset, das alle Datensätze der Tabelle tblArtikel enthält, die zur aktuellen Kategorie aus der übergeordneten Schleife gehören. Diese durchläuft die Prozedur ebenfalls in einer Do While-Schleife. Hier legt sie jeweils ein button-Element für den aktuellen Artikel an. Die id erhält den Buchstaben a und die Artikel-ID, das label den Artikelnamen und das Attribut onAction den Prozedurnamen btnArticle_onAction. Und damit wir beim Anklicken des button-Elements wissen, welchen Artikel der Benutzer überhaupt angeklickt hat, hinterlegen wir für das tag-Attribut noch den Primärschlüsselwert des jeweiligen Artikels.

Sind alle Artikel einer Kategorie durchlaufen, fügt die Prozedur das schließende menu-Element hinzu und schreitet mit der nächsten Kategorie voran. Auf diese Weise werden alle Kategorien und Artikel eingefügt, bevor das schließende menu-Element folgt und die Prozedur den Inhalt von strXML mit dem Parameter content zurückgibt.

Das Ergebnis sieht in gekürzter Form wie in Listing 3 aus.

<menu xmlns="http://schemas.microsoft.com/office/2009/07/customui">
   <menu id="k1" label="Getränke" image="folder">
     <button id="a1" label="Chai" onAction="btnArticle_onAction" tag="1" image="form"/>
     <button id="a2" label="Chang" onAction="btnArticle_onAction" tag="2" image="form"/>
     ...
   </menu>
   <menu id="k2" label="Gewürze" image="folder">
     <button id="a3" label="Aniseed Syrup" onAction="btnArticle_onAction" tag="3" image="form"/>
     <button id="a4" label="Chef Anton's Cajun Seasoning" onAction="btnArticle_onAction" tag="4" image="form"/>
     ...
   </menu>
   ...
</menu>

Listing 3: Der von der Prozedur dynKategorienUndArtikel_getContent erzeugte XML-Ausdruck (gekürzt)

Anzeigen des Formulars mit dem gewählten Artikel

Für das Callbackattribut btnArticle_onAction hinterlegen wir die folgende Prozedur:

Sub btnArticle_onAction(control As IRibbonControl)
     Dim lngArtikelID As Long
     lngArtikelID = control.Tag
     DoCmd.OpenForm "frmArtikel",            WhereCondition:="ArtikelID = " & lngArtikelID
End Sub

Diese Prozedur ermittelt aus der Eigenschaft tag des Parameters control den Primärschüsselwert des Artikels, den der Benutzer angeklickt hat. Diesen fügen wir dann über die Variable lngArtikel als Vergleichswert in die WhereCondition der DoCmd.OpenForm-Methode ein.

Sie können auch Menü-Einträge zum Hinzufügen neuer Kategorien oder Artikel hinzufügen – hier ergeben sich eine Menge neuer Ideen!

dynamicMenu zur Laufzeit aktualisieren

Bisher wird das dynamicMenu-Element beim ersten Aufruf per VBA zusammengestellt. Was aber, wenn der Benutzer bei der Arbeit mit der Datenbank neue Kategorien oder neue Artikel anlegt oder die vorhandenen Daten ändert oder löscht Dann sollte auch das Menü beim nächsten Aufruf neu zusammengestellt werden.

Was wir dazu brauchen, ist wiederum ein Attribut namens onLoad für das customUI-Element und dafür eine Ereignisprozedur, welche die Ribbondefinition per IRibbonUI-Variable referenziert.

Für diese können wir dann zu gegebener Zeit die Invalidate– oder die InvalidateControl-Methode aufrufen.

Also erweitern wir customUI um das onLoad-Attribut:

<customUI ... loadImage="loadImage"   onLoad="onLoad_dynamicMenu">

Die Callbackprozedur implementieren wir wie folgt:

Sub onLoad_dynamicMenu(ribbon As IRibbonUI)
     Set objRibbon_DynamicMenu = ribbon
End Sub

Die Deklaration der benötigten Variablen lautet:

Public objRibbon_DynamicMenu As IRibbonUI

Um das Ribbon beispielsweise nach dem Ändern eines Datensatzes im Formular frmArtikel neu zu laden, fügen wir für das Ereignis Nach Aktualisierung des Formulars die folgende Ereignisprozedur hinzu:

Private Sub Form_AfterUpdate()
     objRibbon_DynamicMenu.Invalidate
End Sub

Damit wird das dynamicMenu nach Änderungen an einem Artikel im Formular frmArtikel neu gefüllt.

Das gallery-Element

Mit dem gallery-Element können Sie Elemente nicht nur untereinander, sondern unter- und nebeneinander anzeigen. Dazu teilen Sie dem gallery-Element mit den Attributen columns und rows mit, wie die enthaltenen Elemente dargestellt werden sollen.

Das Beispiel finden Sie in der Tabelle USysRibbons unter dem Namen Beispiel gallery. Der Code befindet sich im Modul mdlRibbonsGallery.

Hier ist ein Beispiel für ein sehr einfaches gallery-Element mit zwei mal zwei Elementen:

<group id="grp0" label="Beispiel gallery">
   <gallery id="gal" label="Galerie" rows="2" 
       columns="2">
     <item id="itm1" label="Element 1" />
     <item id="itm2" label="Element 2" />
     <item id="itm3" label="Element 3" />
     <item id="itm4" label="Element 4" />
   </gallery>
</group>

Dieses sieht wie in Bild 3 aus. Die Elemente werden also zuerst zeilenweise hinzugefügt und dann untereinander. Dabei geben die Werte für die beiden Attribute rows und columns lediglich an, wie groß der Platz für das gallery-Element berechnet wird. Sie legen damit keine Obergrenze für die anzeigbaren Elemente fest. Wenn Sie mehr Elemente hinzufügen, also in dem durch rows und columns festgelegten Platz angezeigt werden können, erscheint eine Bildlaufleiste und die übrigen Elemente werden unten angehängt (siehe Bild 4).

gallery-Element

Bild 3: gallery-Element

gallery-Element mit Bildlaufleiste

Bild 4: gallery-Element mit Bildlaufleiste

Das gallery-Element hat noch zwei interessante Eigenschaften:

  • itemHeight: Gibt die Höhe der Bilder im gallery-Element an.
  • itemWidth: Gibt die Breite der Bilder im gallery-Element an.

Diese treten jedoch nur in Erscheinung, wenn für die Elemente überhaupt Bilder hinterlegt sind. Dies ist etwa in folgendem Beispiel der Fall:

<customUI ... loadImage="loadImage">
...
<group id="grp0" label="Beispiel gallery">
   <gallery id="gal" label="Galerie" rows="2" 
       columns="2" itemHeight="32" itemWidth="32">
     <item id="itm1" label="Element 1" image="apple" />
     <item id="itm2" label="Element 2" image="lemon" />
     <item id="itm3" label="Element 3" image="hamburger"/>
     <item id="itm4" label="Element 4" image="banana" />
   </gallery>
</group>

Das Ergebnis sehen Sie in Bild 5.

gallery-Element mit Bildern

Bild 5: gallery-Element mit Bildern

gallery-Element immer neu laden

Genau wie das comboBox-Element weist auch das gallery-Element das Attribut invalidateContentOnDrop auf. Stellen Sie dieses auf true ein, wird der Inhalt des Elements immer wieder neu geladen.

Dazu benötigen wir aber zunächst die notwendigen Attribute, die zum dynamischen Laden der Elemente notwendig sind.

Diese hinterlegen wir in der Definition aus Listing 4. Diese Definition finden Sie in der Tabelle USysRibbons unter dem Namen Beispiel gallery dynamisch. Den Code finden Sie im Modul mdlRibbonsGallery.

<xml version="1.0">
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" loadImage="loadImage">
   <ribbon>
     <tabs>
       <tab id="tab" label="Beispiel Menüs" insertBeforeMso="TabHomeAccess">
         <group id="grp0" label="Beispiel gallery">
           <gallery id="gal" label="Galerie" itemHeight="32" itemWidth="32" getItemLabel="gal_getItemLabel" 
               getItemImage="gal_getItemImage" getItemID="gal_getItemID" getItemCount="gal_getItemCount" 
               invalidateContentOnDrop="true"/>
         </group>
       </tab>
     </tabs>
   </ribbon>
</customUI>

Listing 4: Beispiel für ein gallery-Element, das aus der Datenbank befüllt wird

Wir haben in diesem Beispiel wieder die beiden Attribute columns und rows entfernt, da diese nicht dynamisch gesetzt werden können – es gibt kein get…-Attribute für diese Eigenschaften. Wenn wir diese weglassen, teilt das Ribbon die Elemente gleichmäßig auf Zeilen und Spalten auf.

Wir wollen in diesem Beispiel einmal alle Bilder, die in der Tabelle MSysResources gespeichert sind, in einem gallery-Element auflisten. Dazu benötigen wir wieder die Attribute getItemCount, getItemLabel und getItemID. Außerdem wollen wir zu jedem Element das Bild anzeigen und nutzen dazu das Attribut getItemImage.

Die folgende Array-Variable soll die einzelnen Einträge in der Prozedur für das Attribut getItemCount erfassen:

Public strGallery() As String

Die Prozedur aus Listing 5 ermittelt die Anzahl der Elemente und stellt die wichtigsten Informationen zu diesen im Array strGallery zusammen. Dazu durchläuft diese Prozedur alle Datensätze eines Recordsets basierend auf der Tabelle MSysResources. Wir haben diese gefiltert nach Elementen, deren Feld Type den Wert img enthält.

Sub gal_getItemCount(control As IRibbonControl, ByRef count)
     Dim db As DAO.Database
     Dim rst As DAO.Recordset
     Dim i As Integer
     Set db = CurrentDb
     Set rst = db.OpenRecordset("SELECT * FROM MSysResources WHERE Type = 'img'", dbOpenDynaset)
     Do While Not rst.EOF
         ReDim Preserve strGallery(1, i) As String
         strGallery(0, i) = rst!ID
         strGallery(1, i) = rst!Name
         i = i + 1
         rst.MoveNext
     Loop
     count = i
End Sub

Listing 5: Code zum Ermitteln der Anzahl der Elemente für das gallery-Element aus der Tabelle MSysResources

Beim Durchlaufen des Recordsets in einer Do -While-Schleife erweitern wir das zweidimensionale Array jeweils um eine Zeile und fügen der ersten Spalte des neu hinzugefügten Elements die ID und der zweiten den Namen aus der Tabelle hinzu. Dabei erhöhen wir die Zählervariable i jeweils um 1. Der Wert von i entspricht anschließend der Anzahl der einzulesenden Elemente und wird mit dem Parameter count zurückgegeben.

Entsprechend dieser Anzahl ruft das Ribbon anschließend die Prozeduren für die drei Attribute getItemLabel, getItemImage und getItemID auf. Die Prozedur gal_getItemLabel gibt als Bezeichnung des Elements den Wert des Feldes Name zurück, der im Array in der zweiten Spalte enthalten ist:

Sub gal_getItemLabel(control As IRibbonControl, _
         index As Integer, ByRef label)
     label = strGallery(1, index)
End Sub

Die Prozedur gal_getItemID holt den Wert aus der ersten Spalte des Arrays und gibt diese mit dem Parameter ID zurück:

Sub gal_getItemID(control As IRibbonControl, _
         index As Integer, ByRef ID)
     ID = strGallery(0, index)
End Sub

Schließlich fehlt noch die Prozedur gal_getItemImage. Diese liefert einen Verweis auf das auslösende Steuerelement für den Parameter control und den aktuellen index, also den Wert für das aktuell zu füllende Element. Der Parameter image erwartet ein StdPicture-Element mit dem Bild, das im gallery-Element für diesen Eintrag angezeigt werden soll. Dazu ermitteln wir aus dem Array strGallery den Namen aus der zweiten Spalte, welcher dem Bildnamen entspricht. Danach können wir die Funktion PicFrom-SharedResource_Ribbon nutzen, um das Bild aus der Tabelle MSysResources zu diesem Namen einzulesen und mit dem Parameter image zurückzugeben:

Sub gal_getItemImage(control As IRibbonControl, _
         index As Integer, ByRef image)
     Dim strImage As String
     strImage = strGallery(1, index)
     Set image = _
         PicFromSharedResource_Ribbon(CStr(strImage))
End Sub

Das Ergebnis sieht schließlich wie in Bild 6 aus.

gallery-Element mit zur Laufzeit geladenen Bildern

Bild 6: gallery-Element mit zur Laufzeit geladenen Bildern

Da wir das Attribut invalidateContentOnDrop auf den Wert true eingestellt haben, können wir zur Laufzeit weitere Elemente zur Tabelle MSysResources hinzufügen und diese werden direkt beim nächsten Aufklappen vom gallery-Element angezeigt.

Das splitButton-Element

Dieses Element ist vermutlich eines der eingebauten Elemente, das Sie ständig verwenden: Bei dem Element zum Ändern der Ansicht eines Objekts handelt es sich nämlich um ein splitButton-Element (siehe Bild 7). Damit wählen Sie einen der Einträge Formularansicht, Datenblattansicht, Layoutansicht oder Entwurfsansicht aus der Aufklappliste aus, wobei einer der Einträge immer schon als sofort anklickbares Element erscheint.

Das wohl meistgenutzte eingebaute splitButton-Element

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