Datensatznavigation per Ribbon

Daten in der Datenblattansicht

Bild 1: Daten in der Datenblattansicht

Die Navigationszeile am unteren Rand von Formularen oder Unterformularen in der Datenblattansicht ist recht fummelig und fällt nicht auf den ersten Blick auf, wenn man nicht gewohnt ist, damit zu arbeiten. Es gibt eine Menge Lösungen, bei denen diese Steuerelemente in Form von Formular-Schaltflächen bereitgestellt werden. Uns ist aber noch keine Lösung über den Weg gelaufen, bei der die Navigationssteuerelemente im Ribbon abgebildet wurden. Zwar gibt es einen eigenen Bereich, der Werkzeuge für den Umgang mit der Datenblattansicht bereitstellt, aber dieser bietet keine Steuerelemente zum Navigieren in den Datensätzen. Aber kein Problem: Wir liefern das nach!

Wenn wir eine Tabelle in einem Formular in der Datenblattansicht anzeigen wie in Bild 1, sehen wir am unteren Rand des Formulars die Navigationssteuerelemente – also Schaltflächen und ein Textfeld mit den folgenden Funktionen:

Daten in der Datenblattansicht

Bild 1: Daten in der Datenblattansicht

  • Ersten Datensatz anzeigen/markieren
  • Vorherigen Datensatz anzeigen/markieren
  • Ausgabe des aktuellen Datensatzes und der gesamten Anzahl samt Eingabe des Datensatzindex, zu dem navigiert werden soll
  • Nächsten Datensatz anzeigen/markieren
  • Letzten Datensatz anzeigen/markieren
  • Neuen Datensatz anzeigen/markieren

Es wäre doch praktisch, wenn diese wichtigen Steuerelemente nicht klein und ganz unten angezeigt würden, sondern oben prominent im Ribbon – sodass der Benutzer diese nicht übersehen kann!

Also schauen wir uns in diesem Beitrag an, wie wir dies realisieren können.

Verhalten der Navigationssteuerelemente untersuchen

Da wir das Verhalten der oben genannten Steuerelemente möglichst genau abbilden wollen, müssen wir es zunächst studieren. In der Regel öffnet man ein Formular mit Daten und erhält den ersten Datensatz der Datensatzquelle. In diesem Zustand sind alle Steuerelemente mit Ausnahme der Schaltfläche zum Anzeigen des vorherigen Datensatzes aktiviert. Das ergibt Sinn, denn man kann nicht zu einem früheren Datensatz springen, wenn schon der erste Datensatz angezeigt wird. Aber kann man zum ersten Datensatz springen, wenn schon der erste angezeigt wird? Man könnte sich streiten, ob die Schaltfläche zum Ansteuern des ersten Datensatzes aktiviert sein muss, wenn der erste Datensatz angezeigt wird, aber Tatsache ist: Unter Access ist es so.

Die nächste zu untersuchende Situation ist die, wenn sich der Datensatzzeiger irgendwo zwischen dem ersten und dem letzten Datensatz befindet. Hier sind immer alle Schaltflächen aktiviert.

Das ist auch bei der Anzeige des letzten Datensatzes der Fall! Warum aber ist hier die Schaltfläche zum Anzeigen des nächsten Datensatzes aktiviert, obwohl wir ja eigentlich nicht mehr zu einem weiteren Datensatz springen können?

Ganz einfach: Unter Access können wir in dieser Situation noch zu einem neuen, leeren Datensatz springen, wenn wir diese Schaltfläche anklicken.

Sonderfall nicht aktualisierbare Datensatzquelle

Eine Situation führt übrigens dazu, dass auch die Schaltfläche zum Anzeigen eines neuen Datensatzes einmal deaktiviert ist: dann nämlich, wenn das Formular Daten anzeigt, die nicht aktualisierbar sind.

Und dann wird auch die Schaltfläche zum Anzeigen des nächsten Datensatzes deaktiviert, wenn sich der Datensatzzeiger auf dem letzten Datensatz befindet.

Dieses Verhalten können wir für ein Formular übrigens relativ einfach abbilden – wir müssen dazu nur die Eigenschaft Recordsettyp auf Snapshot einstellen oder eine nicht aktualisierbare Abfrage zusammenstellen.

Die hier definierten Regeln wollen wir beim Programmieren der Ribbonanpassung zum Steuern der Datensatzanzeige berücksichtigen.

Aussehen des Ribbons definieren

Das geplante Ribbon soll wie in Bild 2 aussehen. Es enthält also fünf Schaltflächen sowie ein EditBox-Element, das die aktuelle Position des Datensatzzeigers anzeigt und auch die Eingabe einer neuen Position erlaubt.

So soll das Ribbon zur Datensatznavigation aussehen.

Bild 2: So soll das Ribbon zur Datensatznavigation aussehen.

Was wir hier nicht beschreiben, aber dennoch benötigt wird und in der Beispieldatenbank enthalten ist:

  • das Modul mdlRibbonImages, das Funktionen enthält, mit denen wir die Bilder aus der Tabelle MSysResources auslesen und in geeigneter Form für die Anzeige im Ribbon bereitstellen
  • die Funktion LoadImages im Modul mdlRibbons, welches wir für das Attribut loadImages des customUI-Elements des Ribbons hinterlegen und die dafür sorgt, dass für jedes Element, welches das image-Attribut enthält, das entsprechende Bild aus der Tabelle MSysResources geladen wird.

Davon ab nutzen wir eine Tabelle namens USysRibbons, in der wir die Ribbondefinition im XML-Format speichern.

Diese enthält drei Felder, nämlich RibbonID (Primärschlüsselfeld mit Autowert), RibbonName (Datentyp Kurzer Text) und RibbonXML (Datentyp Langer Text). Darin speichern wir die nötigen Informationen wie in Bild 3.

Tabelle zum Speichern der Ribbondefinition

Bild 3: Tabelle zum Speichern der Ribbondefinition

Nun schauen wir uns die darin enthaltene Ribbondefinition an. Diese haben wir in Listing 1 abgebildet. Das Element customUI enthält die beiden Attribute onLoad und loadImage, welche die beim Laden des Ribbons und für die Anzeige von Bildern benötigten VBA-Prozeduren angeben. Die für onLoad hinterlegte Prozedur OnLoad_Navigation wird direkt beim Laden ausgelöst, die für loadImage hinterlegte für jedes Element, welches das Attribut image aufweist und somit ein Bild anzeigen soll.

<?xml version="1.0"?>
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="OnLoad_Navigation" loadImage="loadImage">
     <ribbon>
         <tabs>
             <tab id="tabNavigation" getLabel="getLabel">
                 <group id="grpNavigation" label="Datensatznavigation">
                     <button image="navigate_beginning" getEnabled="getEnabled" label="Erster Datensatz" 
                         id="btnErster" onAction="onAction" size="large"/>
                     <button image="navigate_left" getEnabled="getEnabled" label="Vorheriger Datensatz" 
                         id="btnVorheriger" onAction="onAction" size="large"/>
                     <editBox label="Aktueller Datensatz" id="txtAktuellerDatensatz" getText="getText" 
                         onChange="onChange" sizeString="100 von 100" getEnabled="getEnabled"/>
                     <button image="navigate_right" getEnabled="getEnabled" label="Nächster Datensatz" 
                         id="btnNaechster" onAction="onAction" size="large"/>
                     <button image="navigate_end" getEnabled="getEnabled" label="Letzter Datensatz" 
                         id="btnLetzter" onAction="onAction" size="large"/>
                     <button image="add" getEnabled="getEnabled" label="Neuer Datensatz" id="btnNeu" 
                         onAction="onAction" size="large"/>
                 </group>
             </tab>
         </tabs>
     </ribbon>
</customUI>

Listing 1: Definition des Ribbons für die Datensatznavigation

Vorbereitung zum Aktualisieren der Ribbonelemente

Wenn wir zur Laufzeit die Eigenschaften von Ribbonelementen aktualisieren wollen, was hier absehbar ist, weil Schaltflächen aktiviert und deaktiviert werden sollen und auch das editBox-Element die jeweilige Position des Datensatzzeigers anzeigen soll, müssen wir eine Variable des Typs IRibbonUI deklarieren. Dieses bietet die Methode Invalidate an, mit der wir dafür sorgen können, dass die Attribute wie getEnabled, getText et cetera ihre Werte per Callbackprozedur erneut abrufen. Diese Variable deklarieren wir wie folgt:

Public objRibbon_Navigation As IRibbonUI

Außerdem implementieren wir die für das Attribut onLoad hinterlegte Prozedur wie folgt, damit diese mit der Variablen objRibbon_Navigation gleich beim Laden einen Verweis auf die angewendete Ribbondefinition hinterlegt:

Sub onLoad_Navigation(ribbon As IRibbonUI)
     Set objRibbon_Navigation = ribbon
End Sub

Unterhalb des customUI-Elements befinden sich die obligatorischen ribbon-, tabs– und tab-Elemente. Das tab-Element mit der Bezeichnung tabNavigation enthält das Callback-Attribut getLabel, welches die Beschriftung des tab-Elements ermitteln soll. Diese Beschriftung soll entweder den Wert der Eigenschaft Beschriftung des Formulars enthalten oder, wenn diese nicht angegeben wurde, den Namen des Formulars als tab-Beschriftung anzeigen.

tab-Beschriftung ermitteln

Die für das Attribut getLabel hinterlegte Prozedur wird direkt beim Anzeigen des Ribbons aufgerufen und sieht wie folgt aus:

Sub getLabel(control As IRibbonControl, ByRef label)
     Dim frm As Form
     Select Case control.ID
         Case "tabNavigation"
             Set frm = Screen.ActiveForm
             If Len(frm.Caption) = 0 Then
                 label = frm.Name
             Else
                 label = frm.Caption
             End If
     End Select
End Sub

Sie prüft per Select Case, ob sie für das Element tabNavigation aufgerufen wurde und referenziert dann das aktuelle Formular. Wenn wir, wie geplant, diese Ribbondefinition als Ribbon für ein Formular angeben, ist auf jeden Fall ein Formular geöffnet, wenn das Ribbon erscheint. Für dieses prüfen wir dann, ob die Eigenschaft Caption leer ist. Ist das der Fall, gibt die Prozedur den Namen des Formulars zurück, anderenfalls die Beschriftung aus der Eigenschaft Caption.

Gruppe mit Steuerelementen

Unter dem tab-Element folgt das group-Element, das die eigentlichen Steuerelemente enthält. Die button-Elemente erhalten jeweils den Namen des anzuzeigenden Icons für das image-Attribut, zum Beispiel navigate_beginning. Außerdem zeigen sie eine Beschriftung an, die wir für das Attribut label angeben und ein eindeutiger Bezeichner, der im Attribut id landet, darf auch nicht fehlen.

Schließlich enthält jedes button-Element noch zwei Attribute, für die wir VBA-Prozeduren hinterlegen:

  • Für das Attribut onAction hinterlegen wir die Prozedur, die beim Anklicken ausgelöst werden soll. Diese heißt OnAction.
  • Für das Attribut getEnabled hinterlegen wir eine Prozedur, die anhand der aktuellen Position des Datensatzzeigers und weiterer Parameter prüfen soll, ob die Schaltfläche derzeit aktiviert sein darf.

Anzeige der Datensatzzeigerposition

In einem Textfeld wollen wir einen Wert wie 1/100 anzeigen und somit die Position des Datensatzzeigers sowie die Anzahl der Datensätze darstellen. Das erledigen wir mit einem editBox-Element. Für dieses hinterlegen wir auch drei Callback-Attribute:

  • Für getText hinterlegen wir die Prozedur, welche die aktuelle Position des Datensatzzeigers und die Anzahl der Datensätze liefert.
  • Für onChange geben wir die Prozedur an, die ausgelöst wird, wenn der Benutzer einen Zahlenwert eingibt, um den Datensatzzeiger zu der angegebenen Position zu bewegen.
  • Für getEnabled hinterlegen wir eine Prozedur, die ermittelt, ob das editBox-Element aktiviert sein soll. Das ist eigentlich nur nicht der Fall, wenn das Recordset leer ist und nicht bearbeitet werden kann.

Bilder bereitstellen

Die fünf Bilddateien, welche die Schaltflächen als Icon anzeigen sollen, speichern wir in der Tabelle MSysResources. Um diese dort zu hinterlegen, gibt es einen einfachen Trick: Wir zeigen ein Formular in der Entwurfsansicht an, klicken auf den Ribbonbefehl zum Hinzufügen eines Bild-Steuerelements, wählen das Bild mit dem dann erscheinenden Dateiauswahl-Dialog aus und brechen dann das Hinzufügen des Bild-Steuerelements ab.

Damit legen wir zwar kein Bild-Steuerelement an, aber das soeben ausgewählte Bild wird in der Tabelle MSysResources gespeichert. Diesen Schritt wiederholen wir einfach für alle anzuzeigenden Bilder, bis die Tabelle MSysResources wie in Bild 4 aussieht. Diese Tabelle wird im Navigationsbereich übrigens nur erscheinen, wenn die Anzeige von Systemobjekten und ausgeblendeten Objekten aktiviert ist. Man kann sie aber auch mit dem folgenden Befehl öffnen (das gilt auch für die Tabelle USysRibbons):

Tabelle zum Speichern der Bilder, die als Icons der Schaltflächen im Ribbon erscheinen sollen

[

Bild 4: Tabelle zum Speichern der Bilder, die als Icons der Schaltflächen im Ribbon erscheinen sollen

DoCmd.OpenTable "MSysResources"

Für die Tabelle USysRibbons analog:

DoCmd.OpenTable "USysRibbons"

Das die Bilder letztlich angezeigt werden, erreichen wir durch die Kombination der Angabe des Wertes aus dem Feld Name für das Attribut image der button-Elemente sowie der Verwendung der für das Attribut loadImage des customUI-Elements hinterlegten Callbackprozedur.

Text für die Anzeige der Position des Datensatzzeigers anzeigen

Damit das editBox-Element einen Text wie 1/100 anzeigt und diese Anzeige zu gegebenen Zeitpunkten aktualisiert, müssen wir eine entsprechende Callbackprozedur hinterlegen. Diese sieht wie folgt aus:

Sub getText(control As IRibbonControl, ByRef text)
     Dim frm As Form
     Dim rst As DAO.Recordset
     Set frm = Screen.ActiveForm
     Set rst = frm.Recordset
     If Not rst.Updatable And rst.RecordCount = 0 Then
         text = ""
     Else
         If Not frm.NewRecord Then
             text = rst.AbsolutePosition + 1 & "/" _
                 & rst.RecordCount
         Else
             text = rst.RecordCount + 1 & "/" _
                 & rst.RecordCount + 1
         End If
     End If
End Sub

Die Prozedur referenziert das aktuell im Fokus befindliche Formular mit der Variablen frm und das darin enthaltene Recordset mit rst. Dann prüft die Prozedur, ob das Recordset nicht aktualisierbar ist und keinen Datensatz enthält. In diesem Fall soll das editBox-Element keinen Wert anzeigen.

Anderenfalls folgt die Unterscheidung, ob das Formular gerade keinen neuen Datensatz anzeigt oder einen neuen. Im ersteren Fall ermittelt die Prozedur mit rst.AbsolutePosition + 1 die aktuelle Position des Datensatzzeigers und mit rst.RecordCount die Anzahl der Datensätze. Diese beiden Werte fügt sie, durch einen Schrägstrich voneinander getrennt, zusammen und gibt sie mit dem Parameter text an das aufrufende Steuerelement zurück. Falls das Formular gerade einen neuen, leeren Datensatz anzeigt, stellen wir den Inhalt des Textfeldes auf den Ausdruck rst.RecordCount + 1 & “/” & rst.RecordCount + 1 ein – also wenn es 100 Recordsets enthält, auf 101/101. Wie und wann wir die Anzeige aktualisieren, zeigen wir in wenigen Abschnitten.

Aktivieren und Deaktivieren der Schaltflächen

Für jede der fünf Schaltflächen haben wir mit dem Attribut getEnabled den Namen der Callbackprozedur hinterlegt, mit der wir den Enabled-Zustand der jeweiligen Schaltfläche ermitteln wollen. Diese Prozedur sieht wie in Listing 2 aus. Sie nimmt mit dem Parameter control einen Verweis auf das auslösende Steuerelement entgegen und erwartet für den Parameter enabled die Angabe des Wertes True oder False.

Sub getEnabled(control As IRibbonControl, ByRef enabled)
     Dim frm As Form
     Dim rst As DAO.Recordset
     Set frm = Screen.ActiveForm
     Set rst = frm.Recordset
     Select Case control.ID
         Case "btnErster", "btnLetzter", "txtAktuellerDatensatz"
             If rst.RecordCount = 0 And rst.Updatable = False Then
                 enabled = False
             Else
                 enabled = True
             End If
         Case "btnVorheriger"
             If rst.AbsolutePosition = 0 Or rst.AbsolutePosition = -1 Then
                 enabled = False
             Else
                 enabled = True
             End If
         Case "btnNaechster"
             If Not frm.NewRecord Then
                 If rst.AbsolutePosition = rst.RecordCount - 1 Then
                     If rst.Updatable Then
                         enabled = True
                     Else
                         enabled = False
                     End If
                 Else
                     enabled = True
                 End If
             Else
                 enabled = False
             End If
         Case "btnNeu"
             If rst.Updatable Then
                 If Not frm.NewRecord Then
                     enabled = True
                 Else
                     enabled = False
                 End If
             Else
                 enabled = False
             End If
     End Select
End Sub

Listing 2: Die Prozedur getEnabled zum Ermitteln des enabled-Zustands der Schaltflächen

Die Prozedur referenziert mit der Variablen frm das aktuell angezeigte und mit Screen.ActiveForm ermittelte Formular sowie mit rst das darin enthaltene Recordset. Das setzt voraus, das aktuell ein Formular angezeigt wird – was erfüllt ist, da das Ribbon nur in Zusammenhang mit einem Formular geöffnet wird. Ein Recordset sollte auch vorliegen, sonst macht die Zuweisung dieses Ribbons keinen Sinn.

Danach prüft die Prozedur mit dem Wert der Eigenschaft ID des mit control gelieferten IRibbonControl-Elements, für welches Steuerelement die Prozedur aufgerufen wurde.

Aktivieren der Schaltflächen für die Anzeige des ersten oder letzten Datensatzes

Für die Schaltfläche btnErster ist der Fall klar: Sie soll eigentlich immer aktiviert sein, also könnten wir mit dem Parameter enabled den Wert True zurückgeben. Das Gleiche gilt für die Schaltfläche btnLetzter.

Aber es gibt einen Sonderfall: Wenn wir nämlich auf ein Formular stoßen, dessen Recordset leer ist und wenn gleichzeitig die Bearbeitung der Daten deaktiviert ist, dann sind die original Navigationsschaltflächen ebenfalls deaktiviert. Also fragen wir ab, ob rst.RecordCount gleich 0 ist und rst.Updatable gleich False. Ist beides wahr, sollen die Schaltflächen btnErster und btnLetzter deaktiviert werden.

Aktivieren der Schaltfläche für die Anzeige des vorherigen Datensatzes

Für die Aktivierung der Schaltfläche btnVorheriger prüfen wir, ob der Datensatzzeiger sich auf der ersten Position befindet – und zwar mit rst.AbsolutePosition = 0. Ist dies wahr, gibt es keinen vorherigen Datensatz mehr – also soll diese Schaltfläche deaktiviert werden. In allen anderen Fällen ist sie aktiviert.

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

den kompletten Artikel im PDF-Format mit Beispieldatenbank

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar