Base Handbuch

Kapitel 9
Makros

LibreOffice 24.2

Copyright

Dieses Dokument unterliegt dem Copyright © 2024. Die Beitragenden sind unten aufgeführt. Sie dürfen dieses Dokument unter den Bedingungen der GNU General Public License (http://www.­gnu.org/licenses/gpl.html), Version 3 oder höher, oder der Creative Commons Attribution License (http://creativecommons.org/licenses/by/3.0/), Version 3.0 oder höher, verändern und/oder weitergeben.

Warennamen werden ohne Gewährleistung der freien Verwendbarkeit benutzt.

Fast alle Hardware- und Softwarebezeichnungen und weitere Stichworte und sonstige Angaben, die in diesem Buch verwendet werden, sind als eingetragene Marken geschützt.

Da es nicht möglich ist, in allen Fällen zeitnah zu ermitteln, ob ein Markenschutz besteht, wird das Symbol (R) in diesem Buch nicht verwendet.

Mitwirkende/Autoren

Robert Großkopf

Jost Lange

Jochen Schiffers

Jürgen Thomas

Michael Niedermair

 

Rückmeldung (Feedback)

Kommentare oder Vorschläge zu diesem Dokument können Sie in deutscher Sprache an die Adresse discuss@de.libreoffice.org senden.

Alles, was an eine Mailingliste geschickt wird, inklusive der E-Mail-Adresse und anderer persönlicher Daten, die die E-Mail enthält, wird öffentlich archiviert und kann nicht gelöscht werden. Also, schreiben Sie mit Bedacht!

Datum der Veröffentlichung und Softwareversion

Veröffentlicht am 01.02.2024. Basierend auf der Version LibreOffice 24.2.

Inhalt

Allgemeines zu Makros

Der Makro-Editor

Benennung von Modulen, Dialogen und Bibliotheken

Makros in Base

Makros benutzen

Makros zuweisen

Ereignisse eines Formulars beim Öffnen oder Schließen des Fensters

Ereignisse eines Formulars bei geöffnetem Fenster

Ereignisse innerhalb eines Formulars

Bestandteile von Makros

Der «Rahmen» eines Makros

Variablen definieren

Arrays definieren

Zugriff auf das Formular

Zugriff auf Elemente eines Formulars

Zugriff auf die Datenbank

Die Verbindung zur Datenbank

SQL-Befehle

Vorbereitete SQL-Befehle mit Parametern

Datensätze lesen und benutzen

Mithilfe des Formulars

Ergebnis einer Abfrage

Mithilfe eines Kontrollfelds

Datensätze wechseln und bestimmte Datensätze ansteuern

Datensätze bearbeiten – neu anlegen, ändern, löschen

Inhalt eines Kontrollfelds ändern

Zeile einer Datenmenge ändern

Zeilen anlegen, ändern, löschen

Kontrollfelder prüfen und ändern

Englische Bezeichner in Makros

Eigenschaften bei Formularen und Kontrollfeldern

Schrift

Formular

Einheitlich für alle Arten von Kontrollfeld

Einheitlich für viele Arten von Kontrollfeld

Textfeld – weitere Angaben

Numerisches Feld

Datumsfeld

Zeitfeld

Währungsfeld

Formatiertes Feld

Listenfeld

Kombinationsfeld

Markierfeld, Optionsfeld

Maskiertes Feld

Tabellenkontrollfeld

Beschriftungsfeld

Gruppierungsrahmen

Schaltfläche

Navigationsleiste

Methoden bei Formularen und Kontrollfeldern

In einer Datenmenge navigieren

Datenzeilen bearbeiten

Einzelne Werte bearbeiten

Parameter für vorbereitete SQL-Befehle

Arbeit mit UNO-Befehlen in Formularen

Bedienbarkeit verbessern

Automatisches Aktualisieren von Formularen

Filtern von Datensätzen

Daten über den Formularfilter filtern

Filterdialog über einen Button starten

Durch Datensätze mit der Bildlaufleiste scrollen

Daten aus Textfeldern auf SQL-Tauglichkeit vorbereiten

Beliebige SQL-Kommandos speichern und bei Bedarf ausführen

Werte in einem Formular vorausberechnen

Die aktuelle Office-Version ermitteln

Wert von Listenfeldern ermitteln

Listenfelder durch Eingabe von Anfangsbuchstaben einschränken

Listenfelder mit eingeschränkter Auswahl

Listenfelder zur Mehrfachauswahl nutzen

Datumswert aus einem Formularwert in eine Datumsvariable umwandeln

Eingabemöglichkeiten in Feldern einschränken

Suchen von Datensätzen

Suchen in Formularen und Ergebnisse farbig hervorheben

Rechtschreibkontrolle während der Eingabe

Kombinationsfelder als Listenfelder mit Eingabemöglichkeit

Textanzeige im Kombinationsfeld

Fremdschlüsselwert vom Kombinationsfeld zum numerischen Feld übertragen

Kontrollfunktion für die Zeichenlänge der Kombinationsfelder

Datensatzaktion erzeugen

Navigation von einem Formular zum anderen

Datensatz im Formular direkt öffnen

Tabellen, Abfragen, Formulare und Berichte öffnen

Hierarchische Listenfelder

Filterung des Formulars mit hierarchischen Listenfeldern

Hierarchische Listenfelder in der Formulareingabe nutzen

Zeiteingaben mit Millisekunden

Ein Ereignis – mehrere Implementationen

Eingabekontrolle bei Formularen

Erforderliche Eingaben absichern

Fehlerhafte Eingaben vermeiden

Abspeichern nach erfolgter Kontrolle

Primärschlüssel aus Nummerierung und Jahreszahl

Datenbankaufgaben mit Makros erweitert

Verbindung mit Datenbanken erzeugen

Daten von einer Datenbank in eine andere kopieren

Direkter Import von Daten aus Calc

Zugriff auf Abfragen

Datenbanksicherungen erstellen

Interne Datenbanken sicher schließen

Tabellenindex heruntersetzen bei Autowert-Feldern

Drucken aus Base heraus

Druck von Berichten aus einem internen Formular heraus

Start, Formatierung, direkter Druck und Schließen des Berichts

Druck von Berichten aus einem externen Formular heraus

Serienbriefdruck aus Base heraus

Drucken über Textfelder

Drucken über Tabellen in Writer

Einfacher Druck von Text in Tabellen

Tabellendruck mit formatierten Zellen

Tabellendruck mit Bildern in der Tabelle

Rechnungen mit Übertrag im Tabellendruck

Aufruf von Anwendungen zum Öffnen von Dateien

Aufruf eines Mailprogramms mit Inhaltsvorgaben

Aufruf einer Kartenansicht zu einer Adresse

Mauszeiger ändern

Änderung beim Überfahren eines Links

Änderung bei gedrückter Strg-Taste und Mausklick

Formulare ohne Symbolleisten präsentieren

Formulare ohne Symbolleisten in einem Fenster

Formulare im Vollbildmodus

Formular direkt beim Öffnen der Datenbankdatei starten

Markierfelder durch Schaltflächen ersetzen

MySQL-Datenbank mit Makros ansprechen

MySQL-Code in Makros

Temporäre Tabelle als individueller Zwischenspeicher

Filterung über die Verbindungsnummer

Gespeicherte Prozeduren

Automatischer Aufruf einer Prozedur

Übertragung der Ausgabe einer Prozedur in eine temporäre Tabelle

PostgreSQL und Makros

Autowertrückgabe mit Returning

Datentyp «Array»

Dialoge

Dialoge starten und beenden

Einfacher Dialog zur Eingabe neuer Datensätze

Dialog zum Bearbeiten von Daten in einer Tabelle

Dialog zum Bearbeiten von Daten aus einer Tabellenübersicht

Fortschrittsbalken für den Ablauf mehrerer Prozeduren

Fehleinträge von Tabellen mit Hilfe eines Dialogs bereinigen

Makrozugriff mit Access2Base

Python als Makrosprache für Datenbanken

Speicherort für das erste Python Makro

Speicherort für Module zum Importieren in ein Makro

Verwaltung von Modulen in Bibliotheken

Abfrage an eine geöffnete Datenbankdatei

Abfrage an eine registrierte Datenbank

Fertige Module in die Base-Datei einbinden

Allgemeines zu Makros

Prinzipiell kommt eine Datenbank unter Base ohne Makros aus. Irgendwann kann aber das Bedürfnis kommen,

Es ist natürlich jedem selbst überlassen, wie intensiv er/sie Makros in Base nutzen will. Makros können zwar die Bedienbarkeit verbessern, sind aber auch immer mit geringen, bei ungünstiger Programmierung auch stärkeren Geschwindigkeitseinbußen des Programms verbunden. Es ist immer besser, zuerst einmal die Möglichkeiten der Datenbank und die vorgesehenen Einstellmöglichkeiten in Formularen auszureizen, bevor mit Makros zusätzliche Funktionen bereitgestellt werden. Makros sollten deshalb auch immer wieder mit größeren Datenbanken getestet werden, um ihren Einfluss auf die Verarbeitungsgeschwindigkeit abschätzen zu können.

Makros werden über den Weg Extras → Makros → Makros verwalten → LibreOffice Basic... erstellt. Es erscheint ein Fenster, das den Zugriff auf alle Makros ermöglicht. Makros für Base werden meistens in dem Bereich gespeichert, der dem Dateinamen der Base-Datei entspricht.

 

Über den Button Neu im Fenster «LibreOffice Basic Makros» wird ein zweites Fenster geöffnet. Hier wird lediglich nach der Bezeichnung für das Modul (Ordner, in dem das Makro abgelegt wird) gefragt. Der Name kann gegebenenfalls auch noch später geändert werden.

Sobald dies bestätigt wird, erscheint der Makro-Editor und auf seiner Eingabefläche wird bereits der Start und das Ende für eine Prozedur angegeben:

  1. 1 REM  *****  BASIC  ***** 

  2. 2    

  3. 3 Sub Main 

  4. 4    

  5. 5 End Sub 

Um Makros, die dort eingegeben wurden, nutzen zu können, sind folgende Schritte notwendig:

Einige Grundprinzipien zur Nutzung des Basic-Codes in LibreOffice:

Zu weiteren Details siehe auch das Handbuch «Erste Schritte Makros mit LibreOffice».

Makros in diesem Kapitel sind entsprechend den Vorgaben aus dem Makro-Editor von LibreOffice eingefärbt:
  1. Makro-Bezeichner
    Makro-Kommentar
    Makro-Operator
    Makro-Reservierter-Ausdruck
    Makro-Zahl
    Makro-Zeichenkette

 

Bezeichner können frei gewählt werden, sofern sie nicht einem reservierten Ausdruck entsprechen. Viele Makros sind in dieser Anleitung mit an die deutsche Sprache angelehnten Bezeichnern versehen. Dies führte bei der englischsprachigen Übersetzung allerdings zu zusätzlichen Problemen. Deshalb sind die Bezeichner in neueren Makros an die englische Sprache angelehnt.

Die hier aufgezeigten Makros sind nahezu ausschließlich innerhalb der Base-Datei gespeichert und dort auch getestet. So kann z.B. der Kontakt zu einer Datenbank mit ThisDatabaseDocument nur innerhalb einer Base-Datei hergestellt werden. Sollen die Makros außerhalb der Datei unter Meine Makros und Dialoge gespeichert werden, so kann eventuell statt ThisDatabaseDocument einfach ThisComponent zum Ziel führen. Es kann aber auch sein, dass dann bestimmte Methoden einfach nicht zur Verfügung stehen.

Der Makro-Editor

 

Der Objektkatalog auf der linken Seite zeigt alle zur Zeit verfügbaren Bibliotheken und darin Module an, die über ein Ereignis aufgerufen werden können. Meine Makros & Dialoge ist für alle Dokumente eines Benutzers verfügbar. LibreOffice Makros & Dialoge sind für alle Benutzer des Rechners und auch anderer Rechner nutzbar, da sie standardmäßig mit LibreOffice installiert werden. Hinzu kommen noch die Bibliotheken, die in dem jeweiligen Dokument, hier Medien_mit_Makros.odb, abgespeichert sind.

Prinzipiell ist es zwar möglich, aus allen verfügbaren Bibliotheken die Module und die darin liegenden Makros zu nutzen. Für eine sinnvolle Nutzung empfiehlt es sich aber nicht, Makros aus anderen Dokumenten zu nutzen, da diese eben nur bei Öffnung des entsprechenden Dokumentes verfügbar sind. Ebenso ist es nicht empfehlenswert, Bibliotheken aus «Meine Makros & Dialoge» einzubinden, wenn die Datenbankdatei auch an andere Nutzer weitergegeben werden soll. Ausnahmen können hier Erweiterungen («Extensions») sein, die dann mit der Datenbankdatei weiter gegeben werden.

In dem Eingabebereich wird aus dem Modul Aktualisierung die Prozedur Ausleihe_aktualisieren angezeigt. Eingegebene Zeilen enden mit einem Return. Groß- und Kleinschreibung sowie Einrückung des Codes sind in Basic beliebig. Lediglich der Verweis auf Zeichenketten, z.B. "Filter", muss genau der Schreibweise in dem dort gemeinten Formular entsprechen.

Makros können schrittweise für Testzwecke durchlaufen werden. Entsprechende Veränderungen der Variablen werden im Beobachter angezeigt.

Benennung von Modulen, Dialogen und Bibliotheken

Die Benennung von Modulen, Dialogen und Bibliotheken sollte erfolgen, bevor irgendein Makro in die Datenbank eingebunden wird. Sie definieren schließlich den Pfad, in dem das auslösende Ereignis nach dem Makro sucht.

Innerhalb einer Bibliothek kann auf alle Makros der verschiedenen Module zugegriffen werden. Sollen Makros anderer Bibliotheken genutzt werden, so müssen diese extra geladen werden:

  1. 1 GlobalScope.BasicLibraries.LoadLibrary("Tools") 

lädt die Bibliothek «Tools», die eine Bibliothek von LibreOffice Makros ist.

 

Über Extras → Makros verwalten → LibreOffice Basic → Verwalten kann der obige Dialog aufgerufen werden. Hier können neue Module und Dialoge erstellt und mit einem Namen versehen werden. Die Namen können allerdings nicht hier, sondern nur in dem Makroeditor selbst verändert werden.

 

Im Makroeditor wird mit einem rechten Mausklick auf die Reiter mit der Modulbezeichnung direkt oberhalb der Suchleiste ein Kontextmenü geöffnet, das u.a. die Änderung des Modulnamens ermöglicht.

 

Neue Bibliotheken können innerhalb der Base-Datei angelegt werden. Die Bezeichnung «Standard» der ersten erstellten Bibliothek lässt sich nicht ändern. Die Namen der weiteren Bibliotheken sind frei wählbar, anschließend aber auch nicht änderbar. In eine Bibliothek können Makros aus anderen Bibliotheken importiert werden. Sollte also der dringende Wunsch bestehen, eine andere Bibliotheksbezeichnung zu erreichen, so müsste eine neue Bibliothek mit diesem Namen erstellt werden und sämtlicher Inhalt der alten Bibliothek in die neue Bibliothek exportiert werden. Dann kann anschließend die alte Bibliothek gelöscht werden.

Bei der Bibliothek «Standard» ist es nicht möglich, ein Kennwort zu setzen. Sollen Makros vor den Blicken des normalen Nutzers verborgen bleiben, so muss dafür eine neue Bibliothek erstellt werden. Diese lässt sich dann mit einem Kennwort schützen.

Makros in Base

Makros benutzen

Der «direkte Weg» über Extras → Makros → Makros ausführen ist zwar auch möglich, aber bei Base-Makros nicht üblich. Ein Makro wird in der Regel einem Ereignis zugeordnet und durch dieses Ereignis gestartet.

Der «direkte Weg» ist vor allem dann nicht möglich auch nicht zu Testzwecken , wenn eines der Objekte thisComponent (siehe den Abschnitt Zugriff auf das Formular) oder oEvent (siehe den Abschnitt Zugriff auf Elemente eines Formulars) benutzt wird.

Makros zuweisen

Damit ein Makro durch ein Ereignis gestartet werden kann, muss es zunächst definiert werden (siehe den einleitenden Abschnitt Allgemeines zu Makros). Dann kann es einem Ereignis zugewiesen werden. Dafür gibt es vor allem zwei Stellen.

Ereignisse eines Formulars beim Öffnen oder Schließen des Fensters

Maßnahmen, die beim Öffnen oder Schließen eines Formulardokuments erledigt werden sollen, werden so registriert:

 

Dann kann diese Zuweisung mit OK bestätigt werden.

Die Einbindung von Makros in das Datenbankdokument erfolgt auf dem gleichen Weg. Nur stehen hier teilweise andere Ereignisse zur Wahl.

Beim Starten eines Datenbankdokumentes kann, wenn die Makrounterstützung nicht aktiviert wurde, die Nachfrage kommen, ob Makros ausgeführt werden. Wird in einem Datenbankdokument das Ereignis Ansicht wurde erzeugt gewählt, so wird dieses Ereignis nicht ausgeführt, auch wenn die Frage nach dem Ausführen der Makros bejaht wurde.
Das Ereignis Dokument öffnen wird hingegen ausgeführt. Soll also auf jeden Fall beim Start des Datenbankdokumentes ein Makro ausgeführt werden, so sollte dieses Ereignis gewählt werden.

Ereignisse eines Formulars bei geöffnetem Fenster

Nachdem das Fenster für die gesamten Inhalte des Formulars (Formulardokument) geöffnet wurde, kann auf die einzelnen Elemente des Formulardokuments zugegriffen werden. Hierzu gehören auch die dem Formular zugeordneten Formularelemente.

 

Die Formularelemente können, wie in obigem Bild, über den Formularnavigator angesteuert werden. Sie sind genauso gut über jedes einzelne Kontrollfeld der Formularoberfläche über das Kontextmenü des Kontrollfeldes zu erreichen.

Die unter Formular-Eigenschaften → Ereignisse aufgezeigten Ereignisse finden statt während das Formularfenster geöffnet ist. Sie können für jedes Formular oder Unterformular des Formularfensters separat ausgewählt werden.

Der Gebrauch des Begriffes «Formular» ist bei Base leider nicht eindeutig. Der Begriff wird zum einen für das Fenster benutzt, das zur Eingabe von Daten geöffnet wird. Zum anderen wird der Begriff für das Element genutzt, das mit diesem Fenster eine bestimmte Datenquelle (Tabelle oder Abfrage) verbindet.
Es können auf einem Formularfenster sehr wohl mehrere Formulare mit unterschiedlichen Datenquelle untergebracht sein. Im Formularnavigator steht zuerst immer der Begriff «Formulare», dem dann in einem einfachen Formular lediglich ein Formular untergeordnet wird.
Das Formularfenster wird in dieser Dokumentation zur besseren Unterscheidung deshalb oft auch als «Formulardokument» bezeichnet.

Ereignisse innerhalb eines Formulars

Alle anderen Makros werden bei den Eigenschaften von Teilformularen und Kontrollfeldern über das Register Ereignisse registriert.

 

Über mehrfaches OK wird diese Zuweisung bestätigt.

Bestandteile von Makros

In diesem Abschnitt sollen einige Teile der Makro-Sprache erläutert werden, die in Base vor allem bei Formularen immer wieder benutzt werden. (Soweit möglich und sinnvoll, werden dabei die Beispiele der folgenden Abschnitte benutzt.)

Der «Rahmen» eines Makros

Die Definition eines Makros beginnt mit dem Typ des Makros SUB oder FUNCTION   und endet mit END SUB bzw. END FUNCTION. Einem Makro, das einem Ereignis zugewiesen wird, können Argumente (Werte) übergeben werden; sinnvoll ist aber nur das Argument oEvent. Alle anderen Routinen, die von einem solchen Makro aufgerufen werden, können abhängig vom Zweck mit oder ohne Rückgabewert definiert werden und beliebig mit Argumenten versehen werden.

  1. 1 SUB Ausleihe_aktualisieren 

  2. 2 END SUB 

     

  3. 1 SUB Zu_Formular_von_Formular(oEvent AS OBJECT) 

  4. 2 END SUB 

     

  5. 1 FUNCTION Loeschen_bestaetigen(oEvent AS OBJECT) AS BOOLEAN 

  6. 2    Loeschen_bestaetigen = FALSE 

  7. 3 END FUNCTION 

Es ist hilfreich, diesen Rahmen sofort zu schreiben und den Inhalt anschließend einzufügen. Bitte vergessen Sie nicht, Kommentare zur Bedeutung des Makros nach dem Grundsatz «so viel wie nötig, so wenig wie möglich» einzufügen. Außerdem unterscheidet Basic nicht zwischen Groß- und Kleinschreibung. In der Praxis werden feststehende Begriffe wie SUB vorzugsweise groß geschrieben, während andere Bezeichner Groß- und Kleinbuchstaben mischen.

Variablen definieren

Im nächsten Schritt werden am Anfang der Routine mit der DIM-Anweisung die Variablen, die innerhalb der Routine vorkommen, mit dem jeweiligen Datentyp definiert. Basic selbst verlangt das nicht, sondern akzeptiert, dass während des Programmablaufs neue Variablen auftreten. Der Programmcode ist aber «sicherer», wenn die Variablen und vor allem die Datentypen festgelegt sind. Viele Programmierer verpflichten sich selbst dazu, indem sie Basic über Option Explicit gleich zu Beginn eines Moduls mitteilen: Erzeuge nicht automatisch irgendwelche Variablen, sondern nutze nur die, die ich auch vorher definiert habe.

  1. 1 DIM oDoc AS OBJECT 

  2. 2 DIM oDrawpage AS OBJECT 

  3. 3 DIM oForm AS OBJECT 

  4. 4 DIM sName AS STRING 

  5. 5 DIM bOKEnabled AS BOOLEAN, iCounter AS INTEGER, dBirthday AS DATE 

Die Deklaration von Variablen ist in Einzelzeilen oder zusammengefasst in einer Zeile, getrennt durch ein Komma, möglich.

Für die Namensvergabe stehen nur Buchstaben (AZ oder az), Ziffern und der Unterstrich '_' zur Verfügung, aber keine Umlaute oder Sonderzeichen. (Unter Umständen ist das Leerzeichen zulässig. Sie sollten aber besser darauf verzichten.) Das erste Zeichen muss ein Buchstabe sein.

Üblich ist es, durch den ersten Buchstaben den Datentyp deutlich zu machen.1 Dann erkennt man auch mitten im Code den Typ der Variablen. Außerdem sind «sprechende Bezeichner» zu empfehlen, sodass die Bedeutung der Variablen schon durch den Namen erkannt werden kann.

Die Liste der möglichen Datentypen in Star-Basic steht im Anhang des Handbuches. An verschiedenen Stellen sind Unterschiede zwischen der Datenbank, von Basic und der LibreOffice-API zu beachten. Darauf wird bei den Beispielen hingewiesen.

Arrays definieren

Gerade für Datenbanken ist die Sammlung von mehreren Variablen in einem Datensatz von Bedeutung. Werden mehrere Variablen zusammen in einer gemeinsamen Variablen gespeichert, so wird dies als ein Array bezeichnet. Ein Array muss definiert werden, bevor Daten in das Array geschrieben werden können.

  1. 1 DIM arDaten() 

erzeugt eine leeres Array.

  1. 2 arDaten = array("Lisa","Schmidt") 

So wird ein Array auf eine bestimmte Größe von 2 Elementen festgelegt und gleichzeitig mit Daten versehen.

Über

  1. 3 print arDaten(0), arDaten(1) 

werden die beiden definierten Inhalte auf dem Bildschirm ausgegeben. Die Zählung für die Felder in Arrays beginnt hier mit 0.

  1. 1 DIM arDaten(2) 

  2. 2 arDaten(0) = "Lisa" 

  3. 3 arDaten(1) = "Schmidt" 

  4. 4 arDaten(2) = "Köln" 

Dies erstellt ein Array, in dem 3 Elemente beliebigen Typs gespeichert werden können, also z.B. ein Datensatz mit den Variablen «Lisa» «Schmidt» «Köln». Mehr passt leider in dieses Array nicht hinein. Sollen mehr Elemente gespeichert werden, so muss das Array vergrößert werden. Wird während der Laufzeit eines Makros allerdings die Größe eines Arrays einfach nur neu definiert, so ist das Array anschließend leer wie eben ein neues Array.

  1. 5 ReDIM Preserve arDaten(3) 

  2. 6 arDaten(3) = "18.07.2003" 

Durch den Zusatz Preserve werden die vorherigen Daten beibehalten, das Array also tatsächlich zusätzlich um die Datumseingabe, hier in Form eines Textes, erweitert.

Das oben aufgeführte Array kann leider nur einen einzelnen Satz an Daten speichern. Sollen stattdessen, wie in einer Tabelle einer Datenbank, mehrere Datensätze gespeichert werden, so muss dem Array zu Beginn eine zusätzliche Dimension hinzugefügt werden.

  1. 1 DIM arDaten(2,1) 

  2. 2 arDaten(0,0) = "Lisa" 

  3. 3 arDaten(1,0) = "Schmidt" 

  4. 4 arDaten(2,0) = "Köln" 

  5. 5 arDaten(0,1) = "Egon" 

  6. 6 arDaten(1,1) = "Müller" 

  7. 7 arDaten(2,1) = "Hamburg" 

Auch hier gilt bei einer Erweiterung über das vorher definierte Maß hinaus, dass der Zusatz Preserve die vorher eingegebenen Daten mit übernimmt.

Zugriff auf das Formular

Das Formular liegt in dem momentan aktiven Dokument. Der Bereich, der dargestellt wird, wird als drawpage bezeichnet. Der Behälter, in dem alle Formulare aufbewahrt werden, heißt forms – im Formularnavigator ist dies sozusagen der oberste Begriff, an den dann sämtliche Formulare angehängt werden. Die o.g. Variablen erhalten auf diesem Weg ihre Werte:

  1. 1 oDoc = thisComponent 

  2. 2 oDrawpage = oDoc.drawpage 

  3. 3 oForm = oDrawpage.forms.getByName("Filter") 

Das Formular, auf das zugegriffen werden soll, ist hier mit dem Namen «Filter» versehen. Dies ist der Name, der auch im Formularnavigator in der obersten Ebene sichtbar ist. (Standardmäßig erhält das erste Formular den Namen «MainForm».) Unterformulare liegen – hierarchisch angeordnet – innerhalb eines Formulars und können Schritt für Schritt erreicht werden:

  1. 4 DIM oSubForm AS OBJECT 

  2. 5 DIM oSubSubForm AS OBJECT 

  3. 6 oSubForm = oForm.getByName("Leserauswahl") 

  4. 7 oSubSubForm = oSubForm.getByName("Leseranzeige") 

Anstelle der Variablen in den «Zwischenstufen» kann man auch direkt zu einem bestimmten Formular gelangen. Ein Objekt der Zwischenstufen, das mehr als einmal verwendet wird, sollte selbständig deklariert und zugewiesen werden. (Im folgenden Beispiel wird oSubForm nicht mehr benutzt.)

  1. 6 oForm = thisComponent.drawpage.forms.getByName("Filter") 

  2. 7 oSubSubForm = oForm.getByName("Leserauswahl").getByName("Leseranzeige") 

Sofern ein Name ausschließlich aus Buchstaben und Ziffern besteht (keine Umlaute, keine Leer- oder Sonderzeichen), kann der Name in der Zuweisung auch direkt verwendet werden:
  1. 6 oForm = thisComponent.drawpage.forms.Filter 

  2. 7 oSubSubForm = oForm.Leserauswahl.Leseranzeige 

 

Einen anderen Zugang zum Formular ermöglicht das auslösende Ereignis für das Makro.

Startet ein Makro über ein Ereignis des Formulars, wie z. B. Formular-Eigenschaften → Vor der Datensatzaktion, so wird das Formular selbst folgendermaßen erreicht:

  1. 1 SUB MakrobeispielBerechne(oEvent AS OBJECT) 

  2. 2    oForm = oEvent.Source 

  3. 3    ... 

  4. 4 END SUB 

Startet ein Makro über ein Ereignis eines Formularfeldes, wie z. B. Eigenschaften: Textfeld → Bei Fokusverlust, so kann sowohl das Formularfeld als auch das Formular ermittelt werden:

  1. 1 SUB MakrobeispielBerechne(oEvent AS OBJECT) 

  2. 2    oFeld = oEvent.Source.Model 

  3. 3    oForm = oFeld.Parent 

  4. 4    ... 

  5. 5 END SUB 

Die Zugriffe über das Ereignis haben den Vorteil, dass kein Gedanke darüber verschwendet werden muss, ob es sich bei dem Formular um ein Hauptformular oder Unterformular handelt. Auch interessiert der Name des Formulars für die Funktionsweise des Makros nicht.

Zugriff auf Elemente eines Formulars

In gleicher Weise kann man auf die Elemente eines Formulars zugreifen: Deklarieren Sie eine entsprechende Variable als object und suchen Sie das betreffende Kontrollfeld innerhalb des Formulars:

  1. 1 DIM btnOK AS OBJECT  ' Button »OK» 

  2. 2 btnOK = oSubSubForm.getByName("Schaltfläche 1")   ' aus dem Formular Leseranzeige 

Dieser Weg funktioniert immer dann, wenn bekannt ist, mit welchem Element das Makro arbeiten soll. Wenn aber im ersten Schritt zu prüfen ist, welches Ereignis das Makro gestartet hat, ist der o.g. Weg über oEvent sinnvoll. Dann wird die Variable innerhalb des Makro-"Rahmens" deklariert und beim Start des Makros zugewiesen. Die Eigenschaft Source liefert immer dasjenige Element, das das Makro gestartet hat; die Eigenschaft Model beschreibt das Kontrollfeld im Einzelnen:

  1. 1 SUB Auswahl_bestaetigen(oEvent AS OBJECT) 

  2. 2    DIM btnOK AS OBJECT 

  3. 3    btnOK = oEvent.Source.Model 

  4. 4 END 

Mit dem Objekt, das man auf diesem Weg erhält, werden die weiteren angestrebten Maßnahmen ausgeführt.

Bitte beachten Sie, dass auch Unterformulare als Bestandteile eines Formulars gelten.

Zugriff auf die Datenbank

Normalerweise wird der Zugriff auf die Datenbank über Formulare, Abfragen, Berichte oder die Serienbrief-Funktion geregelt, wie es in allen vorhergehenden Kapiteln beschrieben wurde. Wenn diese Möglichkeiten nicht genügen, kann ein Makro auch gezielt die Datenbank ansprechen, wofür es mehrere Wege gibt.

Die Verbindung zur Datenbank

Das einfachste Verfahren benutzt dieselbe Verbindung wie das Formular, wobei oForm wie oben bestimmt wird:

  1. 1 DIM oConnection AS OBJECT 

  2. 2 oConnection = oForm.activeConnection() 

Oder man holt die Datenquelle, also die Datenbank, durch das Dokument und benutzt die vorhandene Verbindung auch für das Makro:

  1. 1 DIM oDatasource AS OBJECT 

  2. 2 DIM oConnection AS OBJECT 

  3. 3 oDatasource = thisComponent.Parent.dataSource 

  4. 4 oConnection = oDatasource.getConnection("","") 

Ein weiterer Weg stellt sicher, dass bei Bedarf die Verbindung zur Datenbank hergestellt wird:

  1. 1 DIM oDatasource AS OBJECT 

  2. 2 DIM oConnection AS OBJECT 

  3. 3 oDatasource = thisComponent.Parent.CurrentController 

  4. 4 IF NOT (oDatasource.isConnected()) THEN oDatasource.connect() 

  5. 5 oConnection = oDatasource.ActiveConnection() 

Die IF-Bedingung bezieht sich hier nur auf eine Zeile. Deshalb ist END IF nicht erforderlich.

Wenn das Makro durch die Benutzeroberfläche nicht aus einem Formulardokument heraus gestartet werden soll, ist folgende Variante geeignet. Dazu muss das Makro innerhalb der Base-Datei gespeichert werden.

  1. 1 DIM oDatasource AS OBJECT 

  2. 2 DIM oConnection AS OBJECT 

  3. 3 oDatasource = thisDatabaseDocument.CurrentController 

  4. 4 IF NOT (oDatasource.isConnected()) THEN oDatasource.connect() 

  5. 5 oConnection = oDatasource.ActiveConnection() 

Der Zugriff auf Datenbanken außerhalb der aktuellen Datenbank ist folgendermaßen möglich:

  1. 1 DIM oDatabaseContext AS OBJECT 

  2. 2 DIM oDatasource AS OBJECT 

  3. 3 DIM oConnection AS OBJECT 

  4. 4 oDatabaseContext = createUnoService("com.sun.star.sdb.DatabaseContext") 

  5. 5 oDatasource = oDatabaseContext.getByName("angemeldeter Name der Datenbank in LO") 

  6. 6 oConnection = oDatasource.GetConnection("","") 

Auch die Verbindung zu nicht in LO angemeldete Datenbanken ist möglich. Hier muss dann lediglich statt des angemeldeten Namens der Pfad zur Datenbank mit «file:///..../Datenbank.odb» angegeben werden.

Ergänzende Hinweise zur Datenbankverbindung stehen im Abschnitt Verbindung mit Datenbanken erzeugen.

SQL-Befehle

Die Arbeit mit der Datenbank erfolgt über SQL-Befehle. Ein solcher muss also erstellt und an die Datenbank geschickt werden; je nach Art des Befehls wird das Ergebnis ausgewertet und weiter verarbeitet. Mit der Anweisung createStatement wird das Objekt dafür erzeugt:

  1. 1 DIM oSQL_Statement AS OBJECT   ' das Objekt, das den SQL-Befehl ausführt 

  2. 2 DIM stSql AS STRING            ' Text des eigentlichen SQL-Befehls 

  3. 3 DIM oResult AS OBJECT          ' Ergebnis für executeQuery 

  4. 4 DIM iResult AS INTEGER         ' Ergebnis für executeUpdate 

  5. 5 oSQL_Statement = oConnection.createStatement() 

Um Daten abzufragen, wird mit dem Befehl die Methode executeQuery aufgerufen und ausgeführt; das Ergebnis wird anschließend ausgewertet. Tabellennamen und Feldnamen werden üblicherweise in doppelte Anführungszeichen gesetzt. Diese müssen im Makro durch weitere doppelte Anführungszeichen maskiert werden, damit sie im Befehl erscheinen.

  1. 6 stSql = "SELECT * FROM ""Tabelle1""" 

  2. 7 oResult = oSQL_Statement.executeQuery(stSql) 

Um Daten zu ändern also für INSERT, UPDATE oder DELETE oder um die Struktur der Datenbank zu beeinflussen,  wird mit dem Befehl die Methode executeUpdate aufgerufen und ausgeführt. Je nach Art des Befehls und der Datenbank erhält man kein nutzbares Ergebnis (ausgedrückt durch die Zahl 0) oder die Anzahl der bearbeiteten Datensätze.

  1. 8 stSql = "DROP TABLE ""Suchtmp"" IF EXISTS" 

  2. 9 iResult = oSQL_Statement.executeUpdate(stSql) 

Der Vollständigkeit halber sei noch ein Spezialfall erwähnt: Wenn oSQL_Statement unterschiedlich für SELECT oder für andere Zwecke benutzt wird, steht die Methode execute zur Verfügung. Diese benutzen wir nicht; wir verweisen dazu auf die API-Referenz.

SQL-Befehle, die so abgesandt werden, entsprechen nicht genau dem, was z. B. bei den Abfragen über direkte SQL-Ausführung erreicht wird. Eine Abfrage wie
… "Name" LIKE '%*%'
gibt nicht nur die Namen mit einem '*' wieder, da intern aus dem '*' ein '%' erstellt wird.
Um wirklich das gleiche Verhalten mit direkter SQL-Ausführung zu erhalten, muss
oSQL_Statement.
EscapeProcessing = False
nach der Erstellung von oSQL_Statement und vor der Ausführung des Codes eingefügt werden:
  1. 1 oSQL_Statement = oConnection.createStatement() 

  2. 2 oSQL_Statement.EscapeProcessing = False 

 

Vorbereitete SQL-Befehle mit Parametern

In allen Fällen, in denen manuelle Eingaben der Benutzer in einen SQL-Befehl übernommen werden, ist es einfacher und sicherer, den Befehl nicht als lange Zeichenkette zu erstellen, sondern ihn vorzubereiten und mit Parametern zu benutzen. Das vereinfacht die Formatierung von Zahlen, Datumsangaben und auch Zeichenketten (die ständigen doppelten Anführungszeichen entfallen) und verhindert Datenverlust durch böswillige Eingaben.

Bei diesem Verfahren wird zunächst das Objekt für einen bestimmten SQL-Befehl erstellt und vorbereitet:

  1. 1 DIM oSQL_Statement AS OBJECT                ' das Objekt, das den SQL-Befehl ausführt 

  2. 2 DIM stSql AS STRING                                        ' Text des eigentlichen SQL-Befehls 

  3. 3 stSql = "UPDATE ""Verfasser"" " _ 

  4. 4    & "SET ""Nachname"" = ?, ""Vorname"" = ?" _ 

  5. 5    & "WHERE ""ID"" = ?" 

  6. 6 oSQL_Statement = oConnection.prepareStatement(stSql) 

Das Objekt wird mit prepareStatement erzeugt, wobei der SQL-Befehl bereits bekannt sein muss. Jedes Fragezeichen markiert eine Stelle, an der später vor der Ausführung des Befehls ein konkreter Wert eingetragen wird. Durch das «Vorbereiten» des Befehls stellt sich die Datenbank darauf ein, welche Art von Angaben in diesem Fall zwei Zeichenketten und eine Zahl vorgesehen ist. Die verschiedenen Stellen werden durch die Position (ab 1 gezählt) unterschieden.

Anschließend werden mit passenden Anweisungen die Werte übergeben und danach der SQL-Befehl ausgeführt. Die Werte werden hier aus Kontrollfeldern des Formulars übernommen, können aber auch aus anderen Makro-Elementen stammen oder im Klartext angegeben werden:

  1. 7 oSQL_Statement.setString(1, oTextfeld1.Text)   ' Text für den Nachnamen 

  2. 8 oSQL_Statement.setString(2, oTextfeld2.Text)   ' Text für den Vornamen 

  3. 9 oSQL_Statement.setLong(3, oZahlenfeld1.Value)  ' Wert für die betreffende ID 

  4. 10 iResult = oSQL_Statement.executeUpdate 

Die vollständige Liste der Zuweisungen findet sich im Abschnitt Parameter für vorbereitete SQL-Befehle.

Es ist auch möglich, direkt mehrere Datensätze über einen vorbereiteten SQL-Befehl einzufügen:

  1. 11 stSql = "UPDATE ""Person"" " _ 

  2. 12    & "SET ""Name"" = ?" _ 

  3. 13    & "WHERE ""ID"" = ?" 

  4. 14 oSQL_Statement = oConnection.prepareStatement(stSql) 

  5. 15 oSQL_Statement.setString(1, "Bill") 

  6. 16 oSQL_Statement.setLong(2, 1) 

  7. 17 oSQL_Statement.addBatch() 

  8. 18 oSQL_Statement.setString(1, "Michaela") 

  9. 19 oSQL_Statement.setLong(2, 2) 

  10. 20 oSQL_Statement.addBatch() 

  11. 21 oSQL_Statement.executeBatch() 

Mit clearBatch könnte der gesamte Inhalt vor der Ausführung auch wieder zurückgenommen werden.

Wer sich weiter über die Vorteile dieses Verfahrens informieren möchte, findet hier Erläuterungen:

Datensätze lesen und benutzen

Es gibt abhängig vom Zweck mehrere Wege, um Informationen aus einer Datenbank in ein Makro zu übernehmen und weiter zu verarbeiten.

Bitte beachten Sie: Wenn hier von einem «Formular» gesprochen wird, kann es sich auch um ein Unterformular handeln. Es geht dann immer um dasjenige (Teil-) Formular, das mit einer bestimmten Datenmenge verbunden ist.

Mithilfe des Formulars

Der aktuelle Datensatz und seine Daten stehen immer über das Formular zur Verfügung, das die betreffende Datenmenge (Tabelle, Abfrage, Ansicht (View)) anzeigt. Dafür gibt es mehrere Methoden, die mit get und dem Datentyp bezeichnet sind, beispielsweise diese:

  1. 1 DIM ID AS LONG 

  2. 2 DIM sName AS STRING 

  3. 3 DIM dValue AS CURRENCY 

  4. 4 DIM dEintritt AS NEW com.sun.star.util.Date 

  5. 5 ID = oForm.getLong(1) 

  6. 6 sName = oForm.getString(2) 

  7. 7 dValue = oForm.getDouble(4) 

  8. 8 dEintritt = oForm.getDate(7) 

Bei allen diesen Methoden ist jeweils die Nummer der Spalte in der Datenmenge anzugeben gezählt ab 1.

Bei allen Methoden, die mit Datenbanken arbeiten, wird ab 1 gezählt. Das gilt sowohl für Spalten als auch für Zeilen.

Soll anstelle der Spaltennummern mit den Spaltennamen der zugrundeliegenden Datenmenge (Tabelle, Abfrage, Ansicht (View)) gearbeitet werden, so kann die Spaltennummer über die Methode findColumn ermittelt werden. Hier ein Beispiel zum Auffinden der Spalte "Name":

  1. 1 DIM sName AS STRING 

  2. 2 DIM nName AS STRING 

  3. 3 nName = oForm.findColumn("Name") 

  4. 4 sName = oForm.getString(nName) 

Das Ergebnis ist immer ein Wert des Typs der Methode, wobei die folgenden Sonderfälle zu beachten sind.

Die vollständige Liste dieser Methoden findet sich im Abschnitt Datenzeilen bearbeiten.

Sollen Werte aus einem Formular für direkte Weiterverarbeitung in SQL genutzt werden (z.B. für die Eingabe der Daten in eine andere Tabelle), so ist es wesentlich einfacher, nicht nach dem Typ der Felder zu fragen.
Das folgende Makro, an Eigenschaften: Schaltfläche → Ereignisse → Aktion ausführen gekoppelt, liest das erste Feld des Formulars aus – unabhängig von dem für die Weiterverarbeitung in Basic erforderlichen Typ.
  1. 1 SUB WerteAuslesen(oEvent AS OBJECT) 

  2. 2    DIM oForm AS OBJECT 

  3. 3    DIM stFeld1 AS STRING 

  4. 4    oForm = oEvent.Source.Model.Parent 

  5. 5    stFeld1 = oForm.getString(1) 

  6. 6 END SUB 

 

Grundsätzlich können alle Felder mit getString ausgelesen werden. Um sie in Makros weiter zu verarbeiten, müssen die erhaltenen Strings dann gegebenenfalls anschließend in die jeweiligen Variablen umgewandelt werden.
Das Auslesen mit getString hat den Vorteil, dass auch leere Felder einwandfrei ermittelt werden können. Werden die (passenden) Variablen stattdessen z.B. über getInt ausgelesen, so ergibt selbst ein leeres Feld '0'. Dies täuscht also vor, dass in dem Feld eben diese Zahl enthalten war. So etwas ist besonders lästig, wenn eben auch der Wert '0' selbst vorkommt – bei der internen Hsqldb z. B. beim automatisch hoch zählenden Schlüsselwert als Startwert.
Alternativ muss immer mit wasNull nachgesehen werden, ob denn der Inhalt vielleicht leer gewesen ist.

Ergebnis einer Abfrage

In gleicher Weise kann die Ergebnismenge einer Abfrage benutzt werden. Im Abschnitt SQL-Befehle steht die Variable oResult für diese Ergebnismenge, die üblicherweise so oder ähnlich ausgelesen wird:

  1. 1 WHILE oResult.next         ' einen Datensatz nach dem anderen verarbeiten 

  2. 2    rem übernimm die benötigten Werte in Variablen 

  3. 3    stVar = oResult.getString(1) 

  4. 4    inVar = oResult.getLong(2) 

  5. 5    boVar = oResult.getBoolean(3) 

  6. 6    rem mach etwas mit diesen Werten 

  7. 7 WEND 

Je nach Art des SQL-Befehls, dem erwarteten Ergebnis und dem Zweck kann vor allem die WHILE-Schleife verkürzt werden oder sogar entfallen. Aber grundsätzlich wird eine Ergebnismenge immer nach diesem Schema ausgewertet.

Soll nur der erste Datensatz ausgewertet werden, so wird mit

  1. 1 oResult.next 

zuerst die Zeile auf diesen Datensatz bewegt und dann mit

  1. 2 stVar = oResult.getString(1) 

z.B. der Inhalt des ersten Datenfeldes gelesen. Die Schleife entfällt hier.

  1. 3 IF wasNull THEN 

  2. 4    ... 

  3. 5 END IF 

Mit dieser Nachfrage würde die gerade getätigte Abfrage zu stVar darauf überprüft, ob sie nach SQL-Standard NULL gewesen ist.

Die Abfrage zu dem obigen Beispiel hat in der ersten Spalte einen Text, in der zweiten Spalte einen Integer-Zahlenwert (Integer aus der Datenbank entspricht Long in Basic) und in der dritten Spalte ein Ja/Nein-Feld. Die Felder werden durch den entsprechenden Indexwert angesprochen. Der Index für die Felder beginnt hier, im Gegensatz zu der sonstigen Zählung bei Arrays, mit dem Wert '1'.

In dem so erstellten Ergebnis ist allerdings keine Navigation möglich. Nur einzelne Schritte zum nächsten Datensatz sind erlaubt. Um innerhalb der Datensätze navigieren zu können, muss der ResultSetType bei der Erstellung der Abfrage bekannt sein. Hierauf wird über

  1. 1 oSQL_Anweisung.ResultSetType = 1004 

oder

  1. 1 oSQL_Anweisung.ResultSetType = 1005 

zugegriffen. Der Typ 1004 - SCROLL_INTENSIVE erlaubt eine beliebige Navigation. Allerdings bleibt eine Änderung an den Originaldaten während des Auslesens unbemerkt. Der Typ 1005 – SCROLL_SENSITIVE berücksichtigt zusätzlich gegebenenfalls Änderungen an den Originaldaten, die das Abfrageergebnis beeinflussen könnten.

Soll zusätzlich in dem Ergebnissatz eine Änderung der Daten ermöglicht werden, so muss die ResultSetConcurrency vorher definiert werden. Die Update-Möglichkeit wird über

  1. 1 oSQL_Anweisung.ResultSetConcurrency = 1008 

hergestellt. Der Typ 1007 - READ_ONLY ist hier die Standardeinstellung.

Die Anzahl der Zeilen, die die Ergebnismenge enthält, kann nur nach Wahl der entsprechenden Typen so bestimmt werden:

  1. 1 DIM iResult AS LONG 

  2. 2 IF oResult.last THEN           ' gehe zum letzten Datensatz, sofern möglich 

  3. 3    iResult = oResult.getRow    ' die laufende Nummer ist die Anzahl 

  4. 4 ELSE 

  5. 5    iResult = 0 

  6. 6 END IF 

Sollen viele Daten über die Schleife WHILE oResult.next ausgelesen werden, so macht sich hier ein Geschwindigkeitsunterschied bei verschiedenen Datenbanken deutlich bemerkbar. Die interne Firebird Datenbank arbeitet hier deutlich schneller als die interne Hsqldb. Selbst bei gleichen Datenbanken kann es abhängig vom Treiber zu deutlichen Unterschieden kommen. Bei der MariaDB mit direkter Verbindung ist die Geschwindigkeit auf dem Level der Firebird Datenbank. Mit der JDBC-Verbindung hingegen ist das Auslesen so langsam wie mit der Hsqldb.
Dieses Verhalten kann schon bei einer Abfrage getestet werden, wenn zum Ausführen die direkte SQL-Verbindung genutzt wird. Das Scrollen zum letzten Datensatz braucht bei vielen Zeilen deutlich länger mit der internen Hsqldb und MariaDB über die JDBC-Verbindung als mit der internen Firebird Datenbank und MariaDB mit der direkten Verbindung.

Mithilfe eines Kontrollfelds

Wenn ein Kontrollfeld mit einer Datenmenge verbunden ist, kann der Wert auch direkt ausgelesen werden, wie es im nächsten Abschnitt beschrieben wird. Das ist aber teilweise mit Problemen verbunden. Sicherer ist neben dem Verfahren Mithilfe des Formulars  der folgende Weg, der für verschiedene Kontrollfelder gezeigt wird:

  1. 1 sValue = oTextField.BoundField.Text        ' Beispiel für ein Textfeld 

  2. 2 nValue = oNumericField.BoundField.Value    ' Beispiel für ein numerisches Feld 

  3. 3 dValue = oDateField.BoundField.Date        ' Beispiel für ein Datumsfeld 

BoundField stellt dabei die Verbindung her zwischen dem (sichtbaren) Kontrollfeld und dem eigentlichen Inhalt der Datenmenge.

Datensätze wechseln und bestimmte Datensätze ansteuern

Im vorletzten Beispiel wurde mit der Methode Next von einer Zeile der Ergebnismenge zur nächsten gegangen. In gleicher Weise gibt es weitere Maßnahmen und Prüfungen, und zwar sowohl für die Daten eines Formulars angedeutet durch die Variable oForm als auch für eine Ergebnismenge. Beispielsweise kann man beim Verfahren Automatisches Aktualisieren von Formularen den vorher aktuellen Datensatz wieder markieren:

  1. 1 DIM loRow AS LONG 

  2. 2 loRow = oForm.getRow()   ' notiere die aktuelle Zeilennummer 

  3. 3 oForm.reload()           ' lade die Datenmenge neu 

  4. 4 oForm.absolute(loRow)    ' gehe wieder zu der notierten Zeilennummer 

Im Abschnitt In einer Datenmenge navigieren stehen alle dazu passenden Methoden.

Die Methode mit getRow() funktioniert aber nur dann einwandfrei, wenn es sich bei dem aktuellen Datensatz nicht um einen neuen Datensatz handelt. Deswegen hier eine weitere Möglichkeit der Navigation über den Bookmark-Befehl. Damit lässt sich der Cursor auch dann sicher positionieren, wenn bei einer neuen Zeile getRow() 0 ermittelt.

  1. 1 DIM var AS VARIANT 

  2. 2 var = oForm.getBookmark() 

  3. 3 loRow = oForm.getRow() 

  4. 4 oForm.reload() 

  5. 5 IF loRow = 0 THEN 

  6. 6    oForm.MoveToInsertRow() 

  7. 7 ELSE 

  8. 8    oForm.MoveToBookmark(var) 

  9. 9 END IF 

Bei einem neuen Datensatz wird die Zeile mit '0' angegeben. In diesem Fall wird dann der Cursor nach dem Neuladen mit MoveToInsertRow auf diese Position gesetzt. Mit MoveToBookmark würde hier der Cursor auf die letzte Zeile mit Inhalt gesetzt.

Datensätze bearbeiten neu anlegen, ändern, löschen

Um Datensätze zu bearbeiten, müssen mehrere Teile zusammenpassen: Eine Information muss vom Anwender in das Kontrollfeld gebracht werden; das geschieht durch die Tastatureingabe. Anschließend muss die Datenmenge «dahinter» diese Änderung zur Kenntnis nehmen; das geschieht durch das Verlassen eines Feldes und den Wechsel zum nächsten Feld. Und schließlich muss die Datenbank selbst die Änderung erfahren; das erfolgt durch den Wechsel von einem Datensatz zu einem anderen.

Bei der Arbeit mit einem Makro müssen ebenfalls diese Teilschritte beachtet werden. Wenn einer fehlt oder falsch ausgeführt wird, gehen Änderungen verloren und «landen» nicht in der Datenbank. In erster Linie muss die Änderung nicht in der Anzeige des Kontrollfelds erscheinen, sondern in der Datenmenge. Es ist deshalb sinnlos, die Eigenschaft Text des Kontrollfelds zu ändern. Diese Eigenschaft dient nur zur Anzeige des Wertes.

Bitte beachten Sie, dass nur Datenmengen vom Typ «Tabelle» problemlos geändert werden können. Bei anderen Datenmengen ist dies nur unter besonderen Bedingungen möglich.

Inhalt eines Kontrollfelds ändern

Wenn es um die Änderung eines einzelnen Wertes geht, wird das über die Eigenschaft BoundField des Kontrollfelds mit einer passenden Methode erledigt. Anschließend muss nur noch die Änderung an die Datenbank weitergegeben werden. Beispiel für ein Datumsfeld, in das das aktuelle Datum eingetragen werden soll:

  1. 1 DIM unoDate AS NEW com.sun.star.util.Date 

  2. 2 unoDate.Year = Year(Date) 

  3. 3 unoDate.Month = Month(Date) 

  4. 4 unoDate.Day = Day(Date) 

  5. 5 oDateField = oForm.getByName("Datum") 

  6. 6 oDateField.BoundField.updateDate( unoDate ) 

  7. 7 oForm.updateRow()       ' Weitergabe der Änderung an die Datenbank 

Für BoundField wird diejenige der updateXxx-Methoden aufgerufen, die zum Datentyp des Feldes passt hier geht es um einen Date-Wert. Als Argument wird der gewünschte Wert übergeben hier das aktuelle Datum, konvertiert in die vom Makro benötigte Schreibweise. Die entsprechende Erstellung des Datums kann auch durch die Formel CDateToUnoDate erreicht werden:

  1. 1 oDateField = oForm.getByName("Datum") 

  2. 2 oDateField.BoundField.updateDate( CDateToUnoDate(NOW()) ) 

  3. 3 oForm.updateRow()       ' Weitergabe der Änderung an die Datenbank 

Zeile einer Datenmenge ändern

Wenn mehrere Werte in einer Zeile geändert werden sollen, ist der vorstehende Weg ungeeignet. Zum einen müsste für jeden Wert ein Kontrollfeld existieren, was oft nicht gewünscht oder sinnvoll ist. Zum anderen muss man sich für jedes dieser Felder ein Objekt «holen». Der einfache und direkte Weg geht über das Formular, beispielsweise so:

  1. 1 DIM unoDate AS NEW com.sun.star.util.Date 

  2. 2 unoDate.Year = Year(Date) 

  3. 3 unoDate.Month = Month(Date) 

  4. 4 unoDate.Day = Day(Date) 

  5. 5 oForm.updateDate(3, unoDate ) 

  6. 6 oForm.updateString(4, "ein Text") 

  7. 7 oForm.updateDouble(6, 3.14) 

  8. 8 oForm.updateInt(7, 16) 

  9. 9 oForm.updateRow() 

Für jede Spalte der Datenmenge wird die zum Datentyp passende updateXxx-Methode aufgerufen. Als Argumente werden die Nummer der Spalte (ab 1 gezählt) und der jeweils gewünschte Wert übergeben. Anschließend muss nur noch die Änderung an die Datenbank weitergegeben werden.

Zeilen anlegen, ändern, löschen

Die genannten Änderungen beziehen sich immer auf die aktuelle Zeile der Datenmenge des Formulars. Unter Umständen muss vorher eine der Methoden aus In einer Datenmenge navigieren aufgerufen werden. Es werden also folgende Maßnahmen benötigt:

  1. 1.Wähle den aktuellen Datensatz. 

  2. 2.Ändere die gewünschten Werte, wie im vorigen Abschnitt beschrieben. 

  3. 3.Bestätige die Änderungen mit folgendem Befehl:
    oForm.updateRow()  

  4. 4.Als Sonderfall ist es auch möglich, die Änderungen zu verwerfen und den vorherigen Zustand wiederherzustellen:
    oForm.cancelRowUpdates()  

Für einen neuen Datensatz gibt es eine spezielle Methode (vergleichbar mit dem Wechsel in eine neue Zeile im Tabellenkontrollfeld). Es werden also folgende Maßnahmen benötigt:

  1. 1.Bereite einen neuen Datensatz vor:
    oForm.moveToInsertRow()  

  2. 2.Trage alle vorgesehenen und benötigten Werte ein. Dies geht ebenfalls mit den updateXxx-Methoden, wie im vorigen Abschnitt beschrieben. 

  3. 3.Bestätige die Neuaufnahme mit folgendem Befehl:
    oForm.insertRow()  

  4. 4.Die Neuaufnahme kann nicht einfach rückgängig gemacht werden. Stattdessen ist die soeben neu angelegte Zeile wieder zu löschen. 

Für das Löschen eines Datensatzes gibt es einen einfachen Befehl; es sind also folgende Maßnahmen nötig:

  1. 1.Wähle wie für eine Änderung den gewünschten Datensatz und mache ihn zum aktuellen. 

  2. 2.Bestätige die Löschung mit folgendem Befehl:
    oForm.deleteRow()  

Damit eine Änderung in die Datenbank übernommen wird, ist sie durch updateRow bzw. insertRow ausdrücklich zu bestätigen. Während beim Betätigen des Speicher-Buttons die passende Funktion automatisch ermittelt wird, muss vor dem Abspeichern ermittelt werden, ob der Datensatz neu ist (Insert) oder ein bestehender Datensatz bearbeitet wurde (Update).
  1. 1 IF oForm.isNew THEN 

  2. 2    oForm.insertRow() 

  3. 3 ELSE 

  4. 4    oForm.updateRow() 

  5. 5 END IF 

 

Kontrollfelder prüfen und ändern

Neben dem Inhalt, der aus der Datenmenge kommt, können viele weitere Informationen zu einem Kontrollfeld gelesen, verarbeitet und geändert werden. Das betrifft vor allem die Eigenschaften, die im Kapitel «Formulare» aufgeführt werden. Eine Übersicht steht im Abschnitt Eigenschaften bei Formularen und Kontrollfeldern.

In mehreren Beispielen des Abschnitts Bedienbarkeit verbessern wird die Zusatzinformation eines Feldes benutzt:

  1. 1 SUB Main(oEvent AS OBJECT) 

  2. 2    DIM stTag AS STRING 

  3. 3    stTag = oEvent.Source.Model.Tag 

Die Eigenschaft Text kann wie im vorigen Abschnitt erläutert nur dann sinnvoll geändert werden, wenn das Feld nicht mit einer Datenmenge verbunden ist. Aber andere Eigenschaften, die «eigentlich» bei der Formulardefinition festgelegt werden, können zur Laufzeit angepasst werden. Beispielsweise kann in einem Beschriftungsfeld die Textfarbe gewechselt werden, wenn statt einer Meldung ein Hinweis oder eine Warnung angezeigt werden soll:

  1. 1 SUB showWarning(oField AS OBJECT, iType AS INTEGER) 

  2. 2    SELECT CASE iType 

  3. 3       CASE 1  

  4. 4          oField.TextColor = RGB(0,0,255)   ' 1 = blau 

  5. 5       CASE 2  

  6. 6          oField.TextColor = RGB(255,0,0)   ' 2 = rot 

  7. 7       CASE ELSE 

  8. 8          oField.TextColor = RGB(0,255,0)   ' 0 = grün (weder 1 noch 2) 

  9. 9    END SELECT 

  10. 10 END SUB 

Englische Bezeichner in Makros

Während der Formular-Designer in der deutschen Version auch deutsche Bezeichnungen für die Eigenschaften und den Datenzugriff verwendet, müssen in Basic englische Begriffe verwendet werden. Diese sind in den folgenden Übersichten aufgeführt.

Eigenschaften, die üblicherweise nur in der Formular-Definition festgelegt werden, stehen nicht in den Übersichten. Gleiches gilt für Methoden (Funktionen und Prozeduren), die nur selten verwendet werden oder für die kompliziertere Erklärungen nötig wären.

Die Übersichten nennen folgende Angaben:

Weitere Informationen finden sich vor allem in der API-Referenz mit Suche nach der englischen Bezeichnung des Kontrollfelds. Gut geeignet, um herauszufinden, welche Eigenschaften und Methoden denn eigentlich bei einem Element zur Verfügung stehen, ist auch das Tool Xray .

  1. 1 SUB Main(oEvent AS OBJECT) 

  2. 2    Xray(oEvent) 

  3. 3 END SUB 

Hiermit wird die Erweiterung Xray aus dem Aufruf heraus gestartet.

Eigenschaften bei Formularen und Kontrollfeldern

Das «Modell» eines Kontrollfelds beschreibt seine Eigenschaften. Je nach Situation kann der Wert einer Eigenschaft nur gelesen und nur geändert werden. Die Reihenfolge orientiert sich an den Aufstellungen «Eigenschaften der Kontrollfelder» im Kapitel «Formular».

Wenn mit

  1. 1 oFeld = oForm.getByName("Name des Kontrollfeldes") 

auf ein Kontrollfeld zugegriffen wird, so werden die Eigenschaften einfach durch ein Anhängen an dieses Objekt mit einem Punkt als Verbinder angesprochen:

  1. 1 oFeld.FontHeight = 16 

definiert also z. B. die Schriftgröße in 16 Punkten.

Schrift

In jedem Kontrollfeld, das Text anzeigt, können die Eigenschaften der Schrift angepasst werden.

Name

Datentyp

L/S

Eigenschaft

FontName

string

L+S

Schriftart.

FontHeight

single

L+S

Schriftgröße.

FontWeight

single

L+S

Schriftstärke.

FontSlant

integer

L+S

Art der Schrägstellung.

FontUnderline

integer

L+S

Art der Unterstreichung.

FontStrikeout

integer

L+S

Art des Durchstreichens.

Formular

Englische Bezeichnung: Form

Name

Datentyp

L/S

Eigenschaft

ApplyFilter

boolean

L+S

Filter aktiviert.

Filter

string

L+S

Aktueller Filter für die Datensätze.

FetchSize

long

L+S

Anzahl der Datensätze, die «am Stück» geladen werden.

Row

long

L

Nummer der aktuellen Zeile.

RowCount

long

L

Anzahl der Datensätze. Entspricht der Anzeige der Gesamtdatensätze in der Navigationsleiste. Da nicht direkt alle Datensätze über FetchSize in den Cache gelesen werden steht hier z.B. '41*', obwohl die Tabelle deutlich mehr Datensätze hat. RowCount gibt dann leider auch nur '41' aus.

Einheitlich für alle Arten von Kontrollfeld

Englische Bezeichnung: Control siehe auch FormComponent

Name

Datentyp

L/S

Eigenschaft

Name

string

L+(S)

Bezeichnung für das Feld.

Enabled

boolean

L+S

Aktiviert: Feld kann ausgewählt werden.

EnableVisible

boolean

L+S

Sichtbar: Feld wird dargestellt.

ReadOnly

boolean

L+S

Nur lesen: Inhalt kann nicht geändert werden.

TabStop

boolean

L+S

Feld ist in der Tabulator-Reihenfolge erreichbar.

Align

integer

L+S

Horizontale Ausrichtung:
0 = links, 1 = zentriert, 2 = rechts

BackgroundColor

long

L+S

Hintergrundfarbe.

Tag

string

L+S

Zusatzinformation.

HelpText

string

L+S

Hilfetext als «Tooltip».

Einheitlich für viele Arten von Kontrollfeld

Name

Datentyp

L/S

Eigenschaft

Text

string

(L+S)

Inhalt des Feldes aus der Anzeige. Bei Textfeldern nach dem Lesen auch zur weiteren Verarbeitung geeignet, andernfalls nur in Ausnahmefällen.

Spin

boolean

L+S

Drehfeld eingeblendet (bei formatierten Feldern).

TextColor

long

L+S

Textfarbe.

DataField

string

L

Name des Feldes aus der Datenmenge

BoundField

object

L

Objekt, das die Verbindung zur Datenmenge herstellt und vor allem dem Zugriff auf den Feldinhalt dient.

Textfeld weitere Angaben

Englische Bezeichnung: TextField

Name

Datentyp

L/S

Eigenschaft

String

string

L+S

Inhalt des Feldes aus der Anzeige.

MaxTextLen

integer

L+S

Maximale Textlänge.

DefaultText

string

L+S

Standardtext.

MultiLine

boolean

L+S

Mehrzeilig oder einzeilig.

EchoChar

(integer)

L+S

Zeichen für Kennwörter (Passwort-Eingabe verstecken).

Numerisches Feld

Englische Bezeichnung: NumericField

Name

Datentyp

L/S

Eigenschaft

ValueMin

double

L+S

Minimalwert zur Eingabe.

ValueMax

double

L+S

Maximalwert zur Eingabe.

Value

double

L+(S)

Aktueller Wert
nicht für Werte aus der Datenmenge verwenden.

ValueStep

double

L+S

Intervall bei Verwendung mit Mausrad oder Drehfeld.

DefaultValue

double

L+S

Standardwert.

DecimalAccuracy

integer

L+S

Nachkommastellen.

ShowThousandsSeparator

boolean

L+S

Tausender-Trennzeichen anzeigen.

Datumsfeld

Englische Bezeichnung: DateField

Datumswerte werden als Datentyp long definiert und im ISO-Format YYYYMMDD angezeigt, also 20120304 für den 04.03.2012. Zur Verwendung dieses Typs zusammen mit getDate und updateDate sowie dem Typ com.sun.star.util.Date verweisen wir auf die Beispiele.

Name

Daten-typ

Datentyp ab LO 4.1.1

L/S

Eigenschaft

DateMin

long

com.sun.star.util.Date

L+S

Minimalwert zur Eingabe.

DateMax

long

com.sun.star.util.Date

L+S

Maximalwert zur Eingabe.

Date

long

com.sun.star.util.Date

L+(S)

Aktueller Wert
nicht für Werte aus der Datenmenge verwenden.

DateFormat

integer

 

L+S

Datumsformat nach Festlegung des Betriebssystems:
0 = kurze Datumsangabe (einfach)
1 = kurze Datumsangabe tt.mm.jj (Jahr zweistellig)
2 = kurze Datumsangabe tt.mm.jjjj (Jahr vierstellig)
3 = lange Datumsangabe (mit Wochentag und Monatsnamen)
Weitere Möglichkeiten sind der Formulardefinition oder der
API-Referenz zu entnehmen.

DefaultDate

long

com.sun.star.util.Date

L+S

Standardwert.

DropDown

boolean

 

L+S

Aufklappbaren Monatskalender anzeigen.

Zeitfeld

Englische Bezeichnung: TimeField

Auch Zeitwerte werden als Datentyp long definiert.

Name

Daten-typ

Datentyp ab LO 4.1.1

L/S

Eigenschaft

TimeMin

long

com.sun.star.util.Time

L+S

Minimalwert zur Eingabe.

TimeMax

long

com.sun.star.util.Time

L+S

Maximalwert zur Eingabe.

Time

long

com.sun.star.util.Time

L+(S)

Aktueller Wert
nicht für Werte aus der Datenmenge verwenden.

TimeFormat

integer

 

L+S

Zeitformat:
0 = kurz als hh:mm (Stunde, Minute, 24 Stunden)
1 = lang als hh:mm:ss (dazu Sekunden, 24 Stunden)
2 = kurz als hh:mm (12 Stunden AM/PM)
3 = lang als hh:mm:ss (12 Stunden AM/PM)
4 = als kurze Angabe einer Dauer
5 = als lange Angabe einer Dauer

DefaultTime

long

com.sun.star.util.Time

L+S

Standardwert.

Währungsfeld

Englische Bezeichnung: CurrencyField

Ein Währungsfeld ist ein numerisches Feld mit den folgenden zusätzlichen Möglichkeiten.

Name

Datentyp

L/S

Eigenschaft

CurrencySymbol

string

L+S

Währungssymbol (nur zur Anzeige).

PrependCurrencySymbol

boolean

L+S

Anzeige des Symbols vor der Zahl.

Formatiertes Feld

Englische Bezeichnung: FormattedControl

Ein formatiertes Feld wird wahlweise für Zahlen, Währungen oder Datum/Zeit verwendet. Sehr viele der bisher genannten Eigenschaften gibt es auch hier, aber mit anderer Bezeichnung.

Name

Datentyp

L/S

Eigenschaft

CurrentValue

variant

L

Aktueller Wert des Inhalts; der konkrete Datentyp hängt vom Inhalt des Feldes und dem Format ab.

EffectiveValue

L+(S)

EffectiveMin

double

L+S

Minimalwert zur Eingabe.

EffectiveMax

double

L+S

Maximalwert zur Eingabe.

EffectiveDefault

variant

L+S

Standardwert.

FormatKey

long

L+(S)

Format für Anzeige und Eingabe. Es gibt kein einfaches Verfahren, das Format durch ein Makro zu ändern.

EnforceFormat

boolean

L+S

Formatüberprüfung: Bereits während der Eingabe sind nur zulässige Zeichen und Kombinationen möglich.

Listenfeld

Englische Bezeichnung: ListBox

Der Lese- und Schreibzugriff auf den Wert, der hinter der ausgewählten Zeile steht, ist etwas umständlich, aber möglich.

Name

Datentyp

L/S

Eigenschaft

ListSource

array of string

L+S

Datenquelle: Herkunft der Listeneinträge oder Name der Datenmenge, die die Einträge liefert.

ListSourceType

integer

L+S

Art der Datenquelle:
0 = Werteliste
1 = Tabelle
2 = Abfrage
3 = Ergebnismenge eines SQL-Befehls
4 = Ergebnis eines Datenbank-Befehls
5 = Feldnamen einer Datenbank-Tabelle

StringItemList

array of string

L

Listeneinträge, die zur Auswahl zur Verfügung stehen.

ItemCount

integer

L

Anzahl der vorhandenen Listeneinträge.

ValueItemList

array of string

L

Liste der Werte, die über das Formular an die Tabelle weitergegeben werden.

DropDown

boolean

L+S

Aufklappbar.

LineCount

integer

L+S

Anzahl der angezeigten Zeilen im aufgeklappten Zustand.

MultiSelection

boolean

L+S

Mehrfachselektion vorgesehen.

SelectedItems

array of integer

L+S

Liste der ausgewählten Einträge, und zwar als Liste der Positionen in der Liste aller Einträge.

Das (erste) ausgewählte Element aus dem Listenfeld erhält man auf diesem Weg:

  1. 1 oControl = oForm.getByName("Name des Listenfelds") 

  2. 2 sEintrag = oControl.ValueItemList( oControl.SelectedItems(0) ) 

Seit LO 4.1 wird direkt der Wert ermittelt, der bei einem Listenfeld an die Datenbank weitergegeben wird.
  1. 1 oControl = oForm.getByName("Name des Listenfelds") 

  2. 2 iD = oControl.getCurrentValue() 

 

Soll für die Einschränkung einer Auswahlmöglichkeit die Abfrage für ein Listenfeld ausgetauscht werden, so ist dabei zu beachten, dass es sich bei dem Eintrag um ein «array of string» handelt:

  1. 1 SUB Listenfeldfilter 

  2. 2    DIM stSql(0) AS STRING 

  3. 3    DIM oDoc AS OBJECT 

  4. 4    DIM oDrawpage AS OBJECT 

  5. 5    DIM oForm AS OBJECT 

  6. 6    DIM oFeld AS OBJECT 

  7. 7    oDoc = thisComponent 

  8. 8    oDrawpage = oDoc.drawpage 

  9. 9    oForm = oDrawpage.forms.getByName("MainForm") 

  10. 10    oFeld = oForm.getByname("Listenfeld") 

  11. 11    stSql(0) = "SELECT ""Name"", ""ID"" FROM ""Filter_Name"" ORDER BY ""Name""" 

  12. 12    oFeld.ListSource = stSql 

  13. 13    oFeld.refresh 

  14. 14 END SUB 

Soll der gerade geänderte Wert eines Listenfeldes ausgelesen werden, der noch nicht im Formular abgespeichert ist, so geht dies über die Listenposition:
  1. 1 SUB Kontofilter_Feldstart(oEvent AS OBJECT) 

  2. 2    DIM oFeld AS OBJECT 

  3. 3    DIM inID AS INTEGER 

  4. 4    oFeld = oEvent.Source.Model 

  5. 5    inID = oFeld.ValueItemList(oEvent.Selected)  

  6. 6    ... 

  7. 7 END SUB 

 

Kombinationsfeld

Englische Bezeichnung: ComboBox

Trotz ähnlicher Funktionalität wie beim Listenfeld weichen die Eigenschaften teilweise ab.

Hier verweisen wir ergänzend auf das Beispiel Kombinationsfelder als Listenfelder mit Eingabemöglichkeit.

Name

Datentyp

L/S

Eigenschaft

Autocomplete

boolean

L+S

Automatisch füllen.

StringItemList

array of string

L+S

Listeneinträge, die zur Auswahl zur Verfügung stehen.

ItemCount

integer

L

Anzahl der vorhandenen Listeneinträge.

DropDown

boolean

L+S

Aufklappbar.

LineCount

integer

L+S

Anzahl der angezeigten Zeilen im aufgeklappten Zustand.

Text

string

L+S

Aktuell angezeigter Text.

DefaultText

string

L+S

Standardeintrag.

ListSource

string

L+S

Name der Datenquelle, die die Listeneinträge liefert.

ListSourceType

integer

L+S

Art der Datenquelle; gleiche Möglichkeiten wie beim Listenfeld (nur die Auswahl «Werteliste» wird ignoriert).

Markierfeld, Optionsfeld

Englische Bezeichnungen: CheckBox (Markierfeld) bzw. RadioButton (Optionsfeld; auch «Option Button» möglich)

Name

Datentyp

L/S

Eigenschaft

Label

string

L+S

Titel (Beschriftung)

State

short

L+S

Status
0 = nicht ausgewählt
1 = ausgewählt
2 = unbestimmt

MultiLine

boolean

L+S

Wortumbruch (bei zu langem Text).

RefValue

string

L+S

Referenzwert

Maskiertes Feld

Englische Bezeichnung: PatternField

Neben den Eigenschaften für «einfache» Textfelder sind folgende interessant.

Name

Datentyp

L/S

Eigenschaft

EditMask

string

L+S

Eingabemaske.

LiteralMask

string

L+S

Zeichenmaske.

StrictFormat

boolean

L+S

Formatüberprüfung bereits während der Eingabe.

Tabellenkontrollfeld

Englische Bezeichnung: GridControl

Name

Datentyp

L/S

Eigenschaft

Count

long

L

Anzahl der Spalten.

ElementNames

array of string

L

Liste der Spaltennamen.

HasNavigationBar

boolean

L+S

Navigationsleiste vorhanden.

RowHeight

long

L+S

Zeilenhöhe.

Beschriftungsfeld

Englische Bezeichnung: FixedText auch Label ist üblich

Name

Datentyp

L/S

Eigenschaft

Label

string

L+S

Der angezeigte Text.

MultiLine

boolean

L+S

Wortumbruch (bei zu langem Text).

Gruppierungsrahmen

Englische Bezeichnung: GroupBox

Keine Eigenschaft dieses Kontrollfelds wird üblicherweise durch Makros bearbeitet. Wichtig ist der Status der einzelnen Optionsfelder.

Schaltfläche

Englische Bezeichnungen: CommandButton für die grafische Schaltfläche ImageButton

Name

Datentyp

L/S

Eigenschaft

Label

string

L+S

Titel Text der Beschriftung.

State

short

L+S

Standardstatus «ausgewählt» bei «Umschalten».

MultiLine

boolean

L+S

Wortumbruch (bei zu langem Text).

DefaultButton

boolean

L+S

Standardschaltfläche

Navigationsleiste

Englische Bezeichnung: NavigationBar

Weitere Eigenschaften und Methoden, die mit der Navigation zusammenhängen z.B. Filter und das Ändern des Datensatzzeigers –, werden über das Formular geregelt.

Name

Datentyp

L/S

Eigenschaft

IconSize

short

L+S

Symbolgröße.

ShowPosition

boolean

L+S

Positionierung anzeigen und eingeben.

ShowNavigation

boolean

L+S

Navigation ermöglichen.

ShowRecordActions

boolean

L+S

Datensatzaktionen ermöglichen.

ShowFilterSort

boolean

L+S

Filter und Sortierung ermöglichen.

Methoden bei Formularen und Kontrollfeldern

Die Datentypen der Parameter werden durch Kürzel angedeutet:

In einer Datenmenge navigieren

Diese Methoden gelten sowohl für ein Formular als auch für die Ergebnismenge einer Abfrage.

Mit «Cursor» ist in den Beschreibungen der Datensatzzeiger gemeint.

Name

Datentyp

Beschreibung

Prüfungen für die Position des Cursors

isBeforeFirst

boolean

Der Cursor steht vor der ersten Zeile, wenn der Cursor nach dem Einlesen noch nicht gesetzt wurde.

isFirst

boolean

Gibt an, ob der Cursor auf der ersten Zeile steht.

isLast

boolean

Gibt an, ob der Cursor auf der letzten Zeile steht.

isAfterLast

boolean

Der Cursor steht hinter der letzten Zeile, wenn er von der letzten Zeile aus mit next weiter gesetzt wurde.

getRow

long

Nummer der aktuellen Zeile

Setzen des Cursors
Beim Datentyp boolean steht das Ergebnis «true» dafür, dass das Navigieren erfolgreich war.

beforeFirst

Wechselt vor die erste Zeile.

first

boolean

Wechselt zur ersten Zeile.

previous

boolean

Geht um eine Zeile zurück.

next

boolean

Geht um eine Zeile vorwärts.

last

boolean

Wechselt zur letzten Zeile.

afterLast

Wechselt hinter die letzte Zeile.

absolute(n)

boolean

Geht zu der Zeile mit der angegebenen Nummer.

relative(n)

boolean

Geht um eine bestimmte Anzahl von Zeilen weiter:
bei positivem Wert von n vorwärts, andernfalls zurück.

Maßnahmen zum Status der aktuellen Zeile

refreshRow

Liest die Werte der aktuellen Zeile neu ein. Nach dem Abspeichern der Zeile sind das auch die aktuellen Werte.

rowInserted

boolean

Gibt an, ob es sich um eine neue Zeile handelt.

rowUpdated

boolean

Gibt an, ob die aktuelle Zeile geändert wurde.

rowDeleted

boolean

Gibt an, ob die aktuelle Zeile gelöscht wurde.

Datenzeilen bearbeiten

Die Methoden zum Lesen stehen bei jedem Formular und bei einer Ergebnismenge zur Verfügung. Die Methoden zum Ändern und Speichern gibt es nur bei einer Datenmenge, die geändert werden kann (in der Regel also nur bei Tabellen, nicht bei Abfragen).

Name

Datentyp

Beschreibung

Maßnahmen für die ganze Zeile

insertRow

Speichert eine neue Zeile.

updateRow

Bestätigt Änderungen der aktuellen Zeile.

deleteRow

Löscht die aktuelle Zeile.

cancelRowUpdates

Macht Änderungen der aktuellen Zeile rückgängig.

moveToInsertRow

Wechselt den Cursor in die Zeile für einen neuen Datensatz.

moveToCurrentRow

Kehrt nach der Eingabe eines neuen Datensatzes zurück zur vorherigen Zeile.

 

Name

Datentyp

Beschreibung

Werte lesen

getString(c)

string

Liefert den Inhalt der Spalte als Zeichenkette. Kann auch zum Auslesen aller anderen Spalten genutzt werden und hat den Vorteil, dass leere Felder direkt bestimmt werden können. Strings können immer noch später in andere Datentypen umgewandelt werden.

getBoolean(c)

boolean

Liefert den Inhalt der Spalte als Wahrheitswert.

getByte(c)

byte

Liefert den Inhalt der Spalte als einzelnes Byte.

getShort(c)

short

Liefert den Inhalt der Spalte als ganze Zahl.

getInt(c)

integer

getLong(c)

long

getFloat(c)

float

Liefert den Inhalt der Spalte als Dezimalzahl von einfacher Genauigkeit.

getDouble(c)

double

Liefert den Inhalt der Spalte als Dezimalzahl von doppelter Genauigkeit. Wegen der automatischen Konvertierung durch Basic ist dies auch für decimal- und currency-Werte geeignet.

getBytes(c)

array of bytes

Liefert den Inhalt der Spalte als Folge einzelner Bytes.

getDate(c)

Date

Liefert den Inhalt der Spalte als Datumswert.

getTime(c)

Time

Liefert den Inhalt der Spalte als Zeitwert.

getTimestamp(c)

DateTime

Liefert den Inhalt der Spalte als Zeitstempel (Datum und Zeit).

In Basic selbst werden Datums- und Zeitwerte einheitlich mit dem Datentyp DATE verarbeitet. Für den Zugriff auf die Datenmenge gibt es verschiedene Datentypen: com.sun.star.util.Date für ein Datum, com.sun.star.util.Time für eine Zeit, com.sun.star.util.DateTime für einen Zeitstempel.

getBinaryStream(c)

Object

Liest den Inhalt eines Binärfeldes (z.B. Bild) aus. So ein Inhalt könnte als Datei gespeichert werden.

wasNull

boolean

Gibt an, ob der Wert der zuletzt gelesenen Spalte NULL war. Beim Auslesen z.B. mit getInt wird sonst für ein leeres Feld grundsätzlich '0' weitergegeben.

Werte speichern

updateNull(c)

Setzt den Inhalt der Spalte c auf NULL.

updateBoolean(c,b)

Setzt den Inhalt der Spalte c auf den Wahrheitswert b.

updateByte(c,x)

Speichert in Spalte c das angegebene Byte x.

updateShort(c,n)

Speichert in Spalte c die angegebene ganze Zahl n.

updateInt(c,n)

updateLong(c,n)

updateFloat(c,n)

Speichert in Spalte c die angegebene Dezimalzahl n.

updateDouble(c,n)

updateString(c,s)

Speichert in Spalte c die angegebene Zeichenkette s.

updateBytes(c,x)

Speichert in Spalte c das angegebene Byte-Array x.

updateDate(c,d)

Speichert in Spalte c das angegebene Datum d.

updateTime(c,d)

Speichert in Spalte c den angegebenen Zeitwert d.

updateTimestamp(c,d)

Speichert in Spalte c den angegeb. Zeitstempel d.

Einzelne Werte bearbeiten

Mit diesen Methoden wird über BoundField aus einem Kontrollfeld der Inhalt der betreffenden Spalte gelesen oder geändert. Diese Methoden entsprechen fast vollständig denen im vorigen Abschnitt; die Angabe der Spalte entfällt.

Name

Datentyp

Beschreibung

Werte lesen

getString

string

Liefert den Inhalt der Spalte als Zeichenkette.

getBoolean

boolean

Liefert den Inhalt der Spalte als Wahrheitswert.

getByte

byte

Liefert den Inhalt der Spalte als einzelnes Byte.

getShort

short

Liefert den Inhalt der Spalte als ganze Zahl.

getInt

integer

getLong

long

getFloat

float

Liefert den Inhalt der Spalte als Dezimalzahl von einfacher Genauigkeit.

getDouble

double

Liefert den Inhalt der Spalte als Dezimalzahl von doppelter Genauigkeit. Wegen der automatischen Konvertierung durch Basic ist dies auch für decimal- und currency-Werte geeignet.

getBytes

array of bytes

Liefert den Inhalt der Spalte als Folge einzelner Bytes.

getDate

Date

Liefert den Inhalt der Spalte als Datumswert.

getTime

Time

Liefert den Inhalt der Spalte als Zeitwert.

getTimestamp

DateTime

Liefert den Inhalt der Spalte als Zeitstempel (Datum und Zeit).

In Basic selbst werden Datums- und Zeitwerte einheitlich mit dem Datentyp DATE verarbeitet. Für den Zugriff auf die Datenmenge gibt es verschiedene Datentypen: com.sun.star.util.Date für ein Datum, com.sun.star.util.Time für eine Zeit, com.sun.star.util.DateTime für einen Zeitstempel.

getBinaryStream(c)

Object

Liest den Inhalt eines Binärfeldes (z.B. Bild) aus. So ein Inhalt könnte als Datei gespeichert werden.

wasNull

boolean

Gibt an, ob der Wert der zuletzt gelesenen Spalte NULL war.

Werte speichern

updateNull

Setzt den Inhalt der Spalte auf NULL.

updateBoolean(b)

Setzt den Inhalt der Spalte auf den Wahrheitswert b.

updateByte(x)

Speichert in der Spalte das angegebene Byte x.

updateShort(n)

Speichert in der Spalte die angegebene ganze Zahl n.

updateInt(n)

updateLong(n)

updateFloat(n)

Speichert in der Spalte die angegebene Dezimalzahl n.

updateDouble(n)

updateString(s)

Speichert in der Spalte die angegebene Zeichenkette s.

updateBytes(x)

Speichert in der Spalte das angegebene Byte-Array x.

updateDate(d)

Speichert in der Spalte das angegebene Datum d.

updateTime(d)

Speichert in der Spalte den angegebenen Zeitwert d.

updateTimestamp(d)

Speichert in der Spalte den angegebenen Zeitstempel d.

Parameter für vorbereitete SQL-Befehle

Die Methoden, mit denen die Werte einem vorbereiteten SQL-Befehl siehe Vorbereitete SQL-Befehle mit Parametern  übergeben werden, sind ähnlich denen der vorigen Abschnitte. Der erste Parameter mit i bezeichnet nennt seine Nummer (Position) innerhalb des SQL-Befehls.

Name

Datentyp

Beschreibung

setNull(i, n)

Setzt den Inhalt der Spalte auf NULL
n bezeichnet den SQL-Datentyp gemäß API-Referenz.

setBoolean(i, b)

Fügt den angegebenen Wahrheitswert b in den SQL-Befehl ein.

setByte(i, x)

Fügt das angegebene Byte x in den SQL-Befehl ein.

setShort(i, n)

Fügt die angegebene ganze Zahl n in den SQL-Befehl ein.

setInt(i, n)

setLong(i, n)

setFloat(i, n)

Fügt die angegebene Dezimalzahl n in den SQL-Befehl ein.

setDouble(i, n)

setString(i, s)

Fügt die angegebene Zeichenkette s in den SQL-Befehl ein.

setBytes(i, x)

Fügt das angegebene Byte-Array x in den SQL-Befehl ein.

setDate(i, d)

Fügt das angegebene Datum d in den SQL-Befehl ein.

setTime(i, d)

Fügt den angegebenen Zeitwert d in den SQL-Befehl ein.

setTimestamp(i, d)

Fügt den angegebenen Zeitstempel d in den SQL-Befehl ein.

clearParameters

Entfernt die bisherigen Werte aller Parameter eines SQL-Befehls.

Arbeit mit UNO-Befehlen in Formularen

Über den Makrorekorder können die Befehle ausgelesen werden, die z. B. mit den Buttons aus der Navigationsleiste der Formulare verbunden sind. Diese Befehle haben häufig eine umfassendere Funktion als die Funktionen, die sonst für Makros vorgesehen sind.

  1. 1 SUB FormularNeuLadenKontrollfelderAktualisieren 

  2. 2    DIM oDocument AS OBJECT 

  3. 3    DIM oDispatcher AS OBJECT 

  4. 4    DIM Array() 

  5. 5    oDocument = ThisComponent.CurrentController.Frame 

  6. 6    oDispatcher = createUnoService("com.sun.star.frame.DispatchHelper") 

  7. 7    oDispatcher.executeDispatch(oDocument, ".uno:Refresh", "", 0, Array()) 

  8. 8 END SUB 

Über den Dispatcher wird das Formular neu geladen und die Formularfelder aktualisiert. Würde nur das Formular über oForm.reload() neu eingelesen, nachdem aus einem anderen Formular heraus der Inhalt eines Listenfeldes geändert wurde, so würde die Änderung in dem Formular nicht angezeigt. Hier müsste auch noch jedes einzelne Feld mit oFeld.refresh() neu eingelesen werden.

Auch das Sichern eines Datensatz ist über .uno:RecSave einfacher als mittels der direkten Ansprache des Formulars. Bei der direkten Ansprache des Formulars muss erst geklärt werden, ob es sich um einen neuen Datensatz handelt, für den dann ein Insert durchgeführt wird, oder ob es sich um einen bestehenden Datensatz handelt, der dann ein Update erfordert.

Eine Übersicht über verschiedene UNO-Befehle befindet sich im Anhang des Handbuches. UNO-Befehle können auch z. B. über Extras → Anpassen → Symbolleisten → Beschreibung ermittelt werden.

Bedienbarkeit verbessern

Als erste Kategorie werden verschiedene Möglichkeiten vorgestellt, die zur Verbesserung der Bedienbarkeit von Base-Formularen dienen. Sofern nicht anders erwähnt, sind diese Makros Bestandteil der Beispieldatenbank «Medien_mit_Makros.odb».

Automatisches Aktualisieren von Formularen

Oft wird in einem Formular etwas geändert und in einem zweiten, auf der gleichen Seite liegenden Formular, soll die Änderung anschließend erscheinen. Hier hilft bereits ein kleiner Codeschnipsel, um das betreffende Anzeigeformular zu aktualisieren.

  1. 1 SUB Aktualisieren 

Zuerst wird einmal das Makro benannt. Die Standardbezeichnung für ein Makro ist SUB. Dies kann groß oder klein geschrieben sein, Mit SUB wird eine Prozedur durchgeführt, die nach außen in der Regel keinen Wert zurück gibt. Weiter unten wird im Gegensatz dazu einmal eine Funktion beschrieben, die im Unterschied dazu Rückgabewerte erzeugt.

Das Makro hat jetzt den Namen «Aktualisieren». Um sicher zu gehen, dass keine Variablen von außen eingeschleust werden, gehen viele Programmierer so weit, dass sie Basic über Option Explicit gleich zu Beginn mitteilen: Erzeuge nicht automatisch irgendwelche Variablen, sondern nutze nur die, die ich auch vorher definiert habe.

Deshalb werden jetzt standardmäßig erst einmal die Variablen deklariert. Bei allen hier deklarierten Variablen handelt es sich um Objekte (nicht z.B. Zahlen oder Texte), so dass der Zusatz AS OBJECT hinter der Deklaration steht. Um später noch zu erkennen, welchen Typ eine Variable hat, ist vor die Variablenbezeichnung ein « gesetzt worden. Prinzipiell ist aber die Variablenbezeichnung nahezu völlig frei wählbar.

  1. 2    DIM oDoc AS OBJECT 

  2. 3    DIM oDrawpage AS OBJECT 

  3. 4    DIM oForm AS OBJECT 

Das Formular liegt in dem momentan aktiven Dokument. Der Behälter, in dem alle Formulare aufbewahrt werden, wird als Drawpage bezeichnet. Im Formularnavigator ist dies sozusagen der oberste Begriff, an den dann sämtliche Formulare angehängt werden.

Das Formular, auf das zugegriffen werden soll, ist hier mit den Namen "Anzeige" versehen. Dies ist der Name, der auch im Formularnavigator sichtbar ist. So hat z.B. das erste Formular standardmäßig erst einmal den Namen "MainForm".

  1. 5    oDoc = thisComponent 

  2. 6    oDrawpage = oDoc.Drawpage 

  3. 7    oForm = oDrawpage.forms.getByName("Anzeige") 

Nachdem das Formular jetzt ansprechbar ist und der Punkt, an dem es angesprochen wurde, in der Variablen oForm gespeichert wurde, wird es jetzt mit dem Befehl reload() neu geladen.

  1. 8    oForm.reload() 

  2. 9 END SUB 

Die Prozedur hat mit SUB begonnen. Sie wird mit END SUB beendet.

Dieses Makro kann jetzt z.B. ausgelöst werden, wenn die Abspeicherung in einem anderen Formular erfolgt. Wird z.B. in einem Kassenformular an einer Stelle die Anzahl der Gegenstände und (über Barcodescanner) die Nummer eingegeben, so kann in einem anderen Formular im gleichen geöffneten Fenster hierdurch der Kassenstand, die Bezeichnung der Ware usw. nach dem Abspeichern sichtbar gemacht werden.

Filtern von Datensätzen

Der Filter selbst funktioniert ja schon ganz ordentlich in einer weiter oben beschriebenen Variante im Kapitel «Datenfilterung». Die untenstehende Variante ersetzt den Abspeicherbutton und liest die Listenfelder neu ein, so dass ein gewählter Filter aus einem Listenfeld die Auswahl in dem anderen Listenfeld einschränken kann.2
  1. 1 SUB Filter 

  2. 2    DIM oDoc AS OBJECT 

  3. 3    DIM oDrawpage AS OBJECT 

  4. 4    DIM oForm1 AS OBJECT 

  5. 5    DIM oForm2 AS OBJECT 

  6. 6    DIM oFeldList1 AS OBJECT 

  7. 7    DIM oFeldList2 AS OBJECT 

  8. 8    oDoc = thisComponent 

  9. 9    oDrawpage = oDoc.drawpage 

Zuerst werden die Variablen definiert und auf das Gesamtformular zugegriffen. Das Gesamtformular besteht aus den Formularen "Filter" und "Anzeige". Die Listenfelder befinden sich in dem Formular "Filter" und sind mit dem Namen "Liste_1" und "Liste_2" versehen.

  1. 10    oForm1 = oDrawpage.forms.getByName("Filter") 

  2. 11    oForm2 = oDrawpage.forms.getByName("Anzeige") 

  3. 12    oFeldList1 = oForm1.getByName("Liste_1") 

  4. 13    oFeldList2 = oForm1.getByName("Liste_2") 

Zuerst wird der Inhalt der Listenfelder an das darunterliegende Formular mit commit() weitergegeben. Die Weitergabe ist notwendig, da ansonsten die Änderung eines Listenfeldes bei der Speicherung nicht berücksichtigt wird. Genau genommen müsste der commit() nur auf dem Listenfeld ausgeführt werden, das gerade betätigt wurde. Danach wird der Datensatz mit updateRow() abgespeichert. Es existiert ja in unserer Filtertabelle prinzipiell nur ein Datensatz, und der wird zu Beginn einmal geschrieben. Dieser Datensatz wird also laufend durch ein Update-Kommando überschrieben.

  1. 14    oFeldList1.commit() 

  2. 15    oFeldList2.commit() 

  3. 16    oForm1.updateRow() 

Die Listenfelder sollen einander beeinflussen. Wird in einem Listenfeld z.B. eingegrenzt, dass an Medien nur CDs angezeigt werden sollen, so muss das andere Listenfeld bei den Autoren nicht noch sämtliche Buchautoren auflisten. Eine Auswahl im 2. Listenfeld hätte dann allzu häufig ein leeres Filterergebnis zur Folge. Daher müssen die Listenfelder jetzt neu eingelesen werden. Genau genommen müsste der refresh() nur auf dem Listenfeld ausgeführt werden, das gerade nicht betätigt wurde.

Anschließend wird das Formular2, das den gefilterten Inhalt anzeigen soll, neu geladen.

  1. 17    oFeldList1.refresh() 

  2. 18    oFeldList2.refresh() 

  3. 19    oForm2.reload() 

  4. 20 END SUB 

Soll mit diesem Verfahren ein Listenfeld von der Anzeige her beeinflusst werden, so kann das Listenfeld mit Hilfe verschiedener Abfragen bestückt werden.

Die einfachste Variante ist, dass sich die Listenfelder mit ihrem Inhalt aus dem Filterergebnis versorgen. Dann bestimmt der eine Filter, aus welchen Datenbestand anschließend weiter gefiltert werden kann.

  1. 1 SELECT  

  2. 2    "Feld_1" || ' - ' || "Anzahl" AS "Anzeige",  

  3. 3    "Feld_1"  

  4. 4 FROM  

  5. 5    ( SELECT COUNT( "ID" ) AS "Anzahl", "Feld_1" FROM "Suchtabelle"
         GROUP BY "Feld_1" )  

  6. 6 ORDER BY "Feld_1" 

Es wird der Feldinhalt und die Trefferzahl angezeigt. Um die Trefferzahl zu errechnen, wird eine Unterabfrage gestellt. Dies ist notwendig, da sonst nur die Trefferzahl ohne weitere Information aus dem Feld in der Listbox angezeigt würde.

Das Makro erzeugt durch dieses Vorgehen ganz schnell Listboxen, die nur noch mit einem Wert gefüllt sind. Steht eine Listbox nicht auf NULL, so wird sie schließlich bei der Filterung bereits berücksichtigt. Nach Betätigung der 2. Listbox stehen also bei beiden Listboxen nur noch die leeren Felder und jeweils 1 angezeigter Wert zur Verfügung. Dies mag für eine eingrenzende Suche erst einmal praktisch erscheinen. Was aber, wenn z.B. in einer Bibliothek die Zuordnung zur Systematik klar war, aber nicht eindeutig, ob es sich um ein Buch, eine CD oder eine DVD handelt? Wurde einmal die Systematik angewählt und dann die 2. Listbox auf CD gestellt so muss, um auch die Bücher zu sehen, die 2. Listbox erst einmal wieder auf NULL gestellt werden, um dann auch die Bücher anwählen zu können. Praktischer wäre, wenn die 2. Listbox direkt die verschiedenen Medienarten anzeigen würde, die zu der Systematik zur Verfügung stehen – natürlich mit den entsprechenden Trefferquoten.

Um dies zu erreichen, wurde die folgende Abfrage konstruiert, die jetzt nicht mehr direkt aus dem Filterergebnis gespeist wird. Die Zahlen für die Treffer müssen anders ermittelt werden.

  1. 1 SELECT  

  2. 2    COALESCE( "Feld_1" || ' - ' || "Anzahl", 'leer - ' || "Anzahl" )
         AS "Anzeige",  

  3. 3    "Feld_1"  

  4. 4 FROM  

  5. 5    ( SELECT COUNT( "ID" ) AS "Anzahl", "Feld_1" FROM "Tabelle"  

  6. 6    WHERE "ID" IN  

  7. 7       ( SELECT "Tabelle"."ID" FROM "Filter", "Tabelle"  

  8. 8          WHERE "Tabelle"."Feld_2" = COALESCE( "Filter"."Filter_2",  

  9. 9          "Tabelle"."Feld_2" ) )  

  10. 10    GROUP BY "Feld_1"  

  11. 11    ) 

  12. 12 ORDER BY "Feld_1" 

Diese doch sehr verschachtelte Abfrage kann auch unterteilt werden. In der Praxis bietet es sich häufig an, die Unterabfrage in einer Tabellenansicht ('VIEW') zu erstellen. Das Listenfeld bekommt seinen Inhalt dann über eine Abfrage, die sich auf diesen 'VIEW' bezieht.

Die Abfrage im Einzelnen:

Die Abfrage stellt 2 Spalten dar. Die erste Spalte enthält die Ansicht, die die Person sieht, die das Formular vor sich hat. In der Ansicht werden die Inhalte des Feldes und, mit einem Bindestrich abgesetzt, die Treffer zu diesem Feldinhalt gezeigt. Die zweite Spalte gibt ihren Inhalt an die zugrundeliegende Tabelle des Formulars weiter. Hier steht nur der Inhalt des Feldes. Die Listenfelder beziehen ihre Inhalte dabei aus der Abfrage, die als Filterergebnis im Formular dargestellt wird. Nur diese Felder stehen schließlich zur weiteren Filterung zur Verfügung.

Als Tabelle, aus der diese Informationen gezogen werden, liegt eine Abfrage vor. In dieser Abfrage werden die Primärschlüsselfelder gezählt (SELECT COUNT( "ID" ) AS "Anzahl"). Dies geschieht gruppiert nach der Bezeichnung, die in dem Feld steht (GROUP BY "Feld_1"). Als zweite Spalte stellt diese Abfrage das Feld selbst als Begriff zur Verfügung. Diese Abfrage wiederum basiert auf einer weiteren Unterabfrage:

  1. 1 SELECT "Tabelle"."ID"  

  2. 2 FROM "Filter", "Tabelle"  

  3. 3 WHERE "Tabelle"."Feld_2" = COALESCE( "Filter"."Filter_2",
      "Tabelle"."Feld_2" ) 

Diese Unterabfrage bezieht sich jetzt auf das andere zu filternde Feld. Prinzipiell muss das andere zu filternde Feld auch zu den Primärschlüsselnummern passen. Sollten noch mehrere weitere Filter existieren, so ist diese Unterabfrage zu erweitern:

  1. 1 SELECT "Tabelle"."ID"  

  2. 2 FROM "Filter", "Tabelle"  

  3. 3 WHERE "Tabelle"."Feld_2" = COALESCE( "Filter"."Filter_2",
         "Tabelle"."Feld_2" ) 

  4. 4    AND 

  5. 5    "Tabelle"."Feld_3" = COALESCE( "Filter"."Filter_3",
         "Tabelle"."Feld_3" ) 

Alle weiteren zu filternden Felder beeinflussen, was letztlich in dem Listenfeld des ersten Feldes, "Feld_1", angezeigt wird.

Zum Schluss wird die gesamte Abfrage nur noch nach dem zugrundeliegenden Feld sortiert.

Wie letztlich die Abfrage aussieht, die dem anzuzeigenden Formular zugrunde liegt, ist im Kapitel «Datenfilterung» nachzulesen.

Mit dem folgenden Makro kann über das Listenfeld gesteuert werden, welches Listenfeld abgespeichert werden muss und welches neu eingelesen werden muss.

Die Variablen für das Array werden in den Eigenschaften des Listenfeldes unter Zusatzinformationen abgelegt. Die erste Variable enthält dort immer den Namen des Listenfeldes selbst, die weiteren Variablen die Namen aller anderen Listenfelder, getrennt durch Kommata.

  1. 1 SUB Filter_Zusatzinfo(oEvent AS OBJECT) 

  2. 2    DIM oDoc AS OBJECT 

  3. 3    DIM oDrawpage AS OBJECT 

  4. 4    DIM oForm1 AS OBJECT 

  5. 5    DIM oForm2 AS OBJECT 

  6. 6    DIM stTag AS String 

  7. 7    stTag = oEvent.Source.Model.Tag 

Ein Array (Ansammlung von Daten, die hier über Zahlenverbindungen abgerufen werden können) wird gegründet und mit den Feldnamen der Listenfelder gefüllt. Der erste Name ist der Name des Listenfelds, das mit der Aktion (Event) verbunden ist.

  1. 8    aList() = Split(stTag, ",") 

  2. 9    oDoc = thisComponent 

  3. 10    oDrawpage = oDoc.drawpage 

  4. 11    oForm1 = oDrawpage.forms.getByName("Filter") 

  5. 12    oForm2 = oDrawpage.forms.getByName("Anzeige") 

Das Array wird von seiner Untergrenze (LBound()) bis zu seiner Obergrenze (UBound()) in einer Schleife durchlaufen. Alle Werte, die in den Zusatzinformationen durch Komma getrennt erschienen, werden jetzt nacheinander weitergegeben.

  1. 13    FOR i = LBound(aList()) TO Ubound(aList()) 

  2. 14       IF i = 0 THEN 

Das auslösende Listenfeld muss abgespeichert werden. Es hat die Variable aList(0). Zuerst wird die Information des Listenfeldes auf die zugrundeliegende Tabelle übertragen, dann wird der Datensatz gespeichert.

  1. 15          oForm1.getByName(aList(i)).commit() 

  2. 16          oForm1.updateRow() 

  3. 17       ELSE 

Die anderen Listenfelder müssen neu eingelesen werden, da sie ja in Abhängigkeit vom ersten Listenfeld jetzt andere Werte abbilden.

  1. 18          oForm1.getByName(aList(i)).refresh() 

  2. 19       END IF 

  3. 20    NEXT 

  4. 21    oForm2.reload() 

  5. 22 END SUB 

Die Abfragen für dieses besser nutzbare Makro sind natürlich die gleichen wie in diesem Abschnitt zuvor bereits vorgestellt.

Daten über den Formularfilter filtern

Alternativ zu dieser Vorgehensweise ist es auch möglich, die Filterfunktion des Formulars direkt zu bearbeiten.

  1. 1 SUB FilterSetzen 

  2. 2    DIM oDoc AS OBJECT 

  3. 3    DIM oForm AS OBJECT 

  4. 4    DIM oFeld  AS OBJECT 

  5. 5    DIM stFilter As String 

  6. 6    oForm = thisComponent.Drawpage.Forms.getByName("MainForm") 

  7. 7    oFeld = oForm.getByName("Filter") 

  8. 8    stFilter = oFeld.Text 

  9. 9    oForm.filter = " UPPER(""Name"") LIKE '%'||'" + UCase(stFilter) + "'||'%'" 

  10. 10    oForm.ApplyFilter = TRUE 

  11. 11    oForm.reload() 

  12. 12 End Sub 

Das Feld wird im Formular aufgesucht, der Inhalt ausgelesen. Der Filter wird entsprechend gesetzt. Die Filterung wird angeschaltet und das Formular neu geladen.

  1. 1 SUB FilterEntfernen 

  2. 2    DIM oForm AS OBJECT 

  3. 3    oForm = thisComponent.Drawpage.Forms.getByName("MainForm") 

  4. 4    oForm.ApplyFilter = False 

  5. 5    oForm.reload() 

  6. 6 END SUB 

Die Beendigung des Filters kann natürlich auch über die Navigationsleiste erfolgen. In diesem Fall wird einfach ein weiteres Makro genutzt.

Über diese Filterfunktion kann ein Formular auch direkt mit einem Filter z. B. für nur einen Datensatz gestartet werden. Aus dem startenden Formular wird ein Wert (z.B. ein Primärschlüssel für den aktuellen Datensatz) ausgelesen und an das Zielformular als Filterwert weiter gegeben.

Filterdialog über einen Button starten

Wird ein Formular geöffnet, so stehen über die Navigationsleiste verschiedene Filtermöglichkeiten zur Verfügung. Während in Tabellen, Abfaregn und über das Formularkontrollelement ein Filterdialog zur Verfügung steht, ist dieser über die Navigationsleiste des Formularfensters durch den Formularbasierten Filter ersetzt worden. Auch über eine einfache Schaltfläche lässt sich dieser Dialog nicht starten. Hier kann zur Zeit nur mit einem Makro nachgeholfen werden.

  1. 1 SUB<