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