Zusammenfassung
Erfahren Sie, wie Sie Variablen sitzungsübergreifend in Datenbank-Properties speichern können.
Techniken
SMTP, VBA
Voraussetzungen
Access 2000 und höher
Beispieldatenbank
DBProperties.mdb
Persistente Variablen
Sascha Trowitzsch, Berlin
Komplexere Anwendungen benötigen in der Regel bestimmte Einstellungen, die ihr Verhalten oder manche Vorgabewerte bestimmen. In der Regel findet man im Menü einen Eintrag Optionen, der ein Dialogfenster für diese Einstellungen hervorbringt. Gespeichert werden diese Werte normalerweise in der Registry, wobei eine Installationsprozedur hier meist bereits Vorgabewerte setzt. Auch umfangreiche Access-Datenbanken brauchen häufig solche Voreinstellungen. Dieser Beitrag zeigt, wie Sie diese Werte in benutzerdefinierten Datenbankeigenschaften speichern.
Dauerhaft gespeicherte Werte
Ein Beispiel für eine Einstellung, die man über Anwendungssitzungen hinweg speichern könnte, ist die Position von Fenstern.
Wird ein Fenster erneut geöffnet, dann soll es die gleiche Position und Größe auf dem Bildschirm haben, wie beim letzten Schließen. Komfortabel wäre auch, wenn beim Starten der Datenbank automatisch das zuletzt geöffnete Formular sichtbar würde - möglichst noch mit dem Datensatz, in dem man sich zuletzt befunden hat. Im Beitrag Steuerelemente mit Gedächtnis (Shortlink 394) können Sie lesen, wie sich diese Einstellungen entweder in einer speziellen Tabelle oder in der Registry ablegen lassen.
Andere Wege wären INI-Dateien (Textdateien) oder auch persistente disconnected ADO-Recordsets: Immerhin lassen sich auch ADO-Recordsets als Dateien abspeichern.
Die letzten zwei genannten Möglichkeiten setzen die Existenz separater Dateien voraus, die Registry-Methode funktioniert nur innerhalb des Windows-Kontos des jeweiligen Benutzers. Die Tabellenlösung ist nicht schlecht, benötigt aber einiges an Code und eine Tabelle, die besser vor den Augen des Anwenders versteckt sein sollte.
Es gibt aber noch eine fünfte und einfache Lösung, die sich die benutzerdefinierten Eigenschaften des Database-Objekts zu Nutze macht. In der Beispieldatenbank finden Sie Anwendungsbeispiele dazu.
Database-Properties
Eine geöffnete Access-Datenbank im MDB-Format besitzt immer ein von der Bibliothek DAO abgeleitetes Database-Objekt.
Das ist selbst dann so, wenn kein Verweis auf die DAO-Bibliothek gesetzt ist. Das standardmäßig vorhandene Application-Objekt hat die Eigenschaften CurrentDb und DBEngine. Dabei ist DBEngine(0)(0) das eigentliche Database-Objekt, während CurrentDb eine kopierte Instanz desselben darstellt.
Und wie fast alle Objekte der DAO-Bibliothek hat auch das Database-Objekt eine Auflistung namens Properties.
Dahinter verbergen sich verschiedene Eigenschaften der Datenbank wie etwa Name (der Pfad zur MDB), Version (die JET-Version, unter der die Datenbank erstellt wurde) oder StartUpForm (das Formular, das beim Öffnen der Datenbank angezeigt wird, falls angegeben).
Access stellt diese Eigenschaften selbst ein, um dann beim Öffnen der MDB zu wissen, wie es mit der Datenbank verfahren soll.
Diese Eigenschaften sind eingebaut und können theoretisch weder per VBA gesetzt, noch gelöscht werden.
Zumindest äußert sich die Online-Hilfe von Access so - eine Fehlinformation, wie Sie später sehen werden.
Die einzelnen Versionen von Access enthalten außerdem unterschiedliche, aber abwärtskompatible Eigenschaften. (In der Betaversion Access 2007 sind etwa die Eigenschaften des neuen Navigationsbereichs hinzugekommen.)
Neben den eingebauten Properties lassen sich aber beliebige selbst definierte Eigenschaften erzeugen und dann Werte in ihnen speichern.
Property auslesen
Sie lesen eine Database-Eigenschaft per VBA entweder über den Namen der Eigenschaft oder über ihre Ordinalnummer aus:
Debug.Print CurrentDb.Properties("Name")
Debug.Print CurrentDb.Properties(1)
Ersetzt man die 1 in voriger Code-Zeile durch eine Variable, dann lassen sich alle Properties über eine Schleife auflisten:
For i = 0 To 1000
Debug.Print _ Currentdb.Properties(i).Name
Next i
Diese Schleife wird nach einigen Durchläufen einen Fehler hervorrufen, nämlich dann, wenn i einen Wert hat, der keiner gültigen Ordinalzahl mehr entspricht.
Eleganter ist deshalb die folgende For Each-Schleife:
Dim prp As DAO.Property
For Each prp In CurrentDb.Properties
Debug.Print prp.Name, prp.Value
Next prp
Neben den Namen der Eigenschaften gibt diese Routine gleichzeitig noch mit prp.Value die Werte derselben aus.
In der Beispieldatenbank finden Sie eine etwas ausführlichere und fehlerresistente Version dieser List-Funktion im Modul mdlProperties und in der Prozedur ListProperties.
Property setzen
Der Wert für eine Property wird durch einfache Zuweisung gesetzt:
CurrentDb.Properties("StartUpForm")= "frmStart"
Das ist eigentlich die Kurzform, denn eigentlich wird hier die Eigenschaft Value des Properties gesetzt (in einer Zeile):
CurrentDb.Properties ("StartUpForm").Value
Value ist jedoch die Default-Eigenschaft des Property-Objekts und kann daher weggelassen werden.
Der Datentyp einer Property ist Variant, daher kann die Property beliebige Werte annehmen. Es können also zum Beispiel Long-Werte, Strings oder Datumswerte übergeben werden.
Property erzeugen
Nun wird es interessant: Über die DAO-Methode CreateProperty kann man eine benutzerdefinierte Eigenschaft erzeugen:
CurrentDb.CreateProperty <Name>, _
<Datentyp>, <Wert>
Der Datentyp ist eine der Konstanten der Enumeration DAO.DataTypeEnum - etwa dbText für einen String oder dbLong für einen Long-Integer. Der übergebene Wert muss dazu passen.
Mit obiger Code-Zeile befindet sich das Property noch quasi im luftleeren Raum - ohne Bezug zum Datenbank-Objekt. Erst durch das Zuweisen zur Properties-Auflistung des Datenbank-Objekts wird es dann auch gespeichert:
Dim prp As DAO.Property
Set prp = CurrentDb.CreateProperty _ ("Autor", dbText, "Trowitzsch")
CurrentDB.Properties.Append prp
Somit ist in der benutzerdefinierten Eigenschaft "Autor" der Datenbank der Wert "Trowitzsch" gesetzt. Eine längere Alternative zu diesem Code wäre diese:
Dim prp As DAO.Property
Set prp = CurrentDb.CreateProperty _ ("Autor")
prp.Type = dbText
prp.Value = "Trowitzsch"
CurrentDB.Properties.Append prp
Property löschen
Das Löschen einer Datenbankeigenschaft erfolgt mit dieser Anweisung:
CurrentDb.Properties.Delete "Autor"
Normalerweise dürfte das selten notwendig sein. Ein Fall wäre, dass Sie eine Property mit einem Datentyp angelegt hätten, diesen aber etwa von dbText in dbLong umwandeln wollten. Das geht nämlich nicht: Die Zuweisung eines neuen Datentyps führt zu einem Laufzeitfehler. Sie müssen die Property erst löschen und unter dem gleichen Namen mit dem neuen Datentyp anlegen.
HinweisWenn Sie versuchen, eine eingebaute Access-Eigenschaft zu löschen, beispielsweise Version, so löst das einen Fehler aus. Soweit entspricht dies der Aussage in der Online-Hilfe. Fraglich ist hier nur, was unter "eingebauter" Eigenschaft zu verstehen ist. So existiert ein Property Build, das die Versionsnummer der msaccess.exe zurückgibt, mit der die Datenbank erstellt wurde. Man kann die Property löschen, sie wird beim nächsten Start der Datenbank aber automatisch wieder gesetzt. Das lässt sich auch noch mit weiteren von Access erzeugten Properties machen, wovon allerdings abzuraten ist. Es kann dazu führen, dass die Datenbank sich nicht mehr starten lässt. Im Versuch habe ich festgestellt, dass eine auf diese Weise "behandelte" Datenbank mit keinerlei Reparaturversuchen wieder zum Leben zu erwecken war. Erstaunlicherweise gelang dann allerdings das Öffnen in der Beta-Version von Access 2007 ... (
Persistente Variablen mit Properties
Mit diesen Grundlagen können Sie nun öffentliche Routinen basteln, mit deren Hilfe sich von überall aus der Datenbank heraus Werte dauerhaft speichern und auslesen lassen. In der Beispieldatenbank finden Sie etwa die in Quellcode 1 dargestellte Routine.
Die Prozedur SetAProperty erwartet als Parameter die Angabe eines Eigenschaftennamens in sName und des Wertes in AValue.
Die Routine versucht zunächst, eine Objektvariable oPrp auf die Eigenschaft zu setzen. Schlägt dies fehl, weil etwa die Eigenschaft nicht existiert, so wird sie im If-Teil neu angelegt.
Da dabei die Angabe des Datentyps zwingend notwendig ist, muss dieser aus dem übergebenen Wert selbst bestimmt werden. Das erledigt die VBA-Funktion VarType(). Leider stimmen die von dieser Funktion zurückgegebenen Konstanten nicht mit den Datentypen von DAO überein. Deshalb wird eine Übersetzung der Konstanten in einer Hilfsfunktion TranslateType vorgenommen, die hier nicht abgebildet ist.
Quellcode 1: Routine zum dauerhaften Speichern einer "Variablen"
Sub SetAProperty(sName, AValue As Variant)
Dim dbs As Database
Dim oPrp As DAO.Property
Set dbs = DBEngine(0)(0)
On Error Resume Next
Set oPrp = dbs.Properties(sName)
If oPrp Is Nothing Then
On Error GoTo 0
Set oPrp = dbs.CreateProperty(sName, TranslateType(AValue), AValue)
dbs.Properties.Append oPrp
Else
oPrp.Value = AValue
End If
Set oPrp = Nothing
Set dbs = Nothing
End Sub
Die folgende Version ist wesentlich kürzer:
Function GetAProperty(sName) As Variant
On Error Resume Next
GetAProperty = DBEngine(0)(0). _ Properties(sName).Value
End Function
Ihnen fällt vielleicht auf, dass in diesen beiden Quellcodes statt CurrentDb die Variante DBEngine(0)(0) zum Einsatz kommt. Grund ist die wesentlich höhere Geschwindigkeit dieses Ausdrucks.
Als Beleg das Resultat eines Tests, den Sie mit der Prozedur BenchmarkSetGetProp der Beispieldatenbank auch selbst ausführen können:
Wird beim Auslesen CurrentDb.Properties verwendet, dann lässt sich die Funktion GetAProperty auf aktueller Hardware circa 1.500 Mal pro Sekunde aufrufen. Mit DBEngine(0)(0).Properties erhöht sich dieser Wert auf ca. 200.000!
Das zeigt, dass sich diese benutzerdefinierten Datenbank-Properties auch gut in Schleifenkonstrukten verwenden lassen, weil das Auslesen nur sehr wenig Zeit kostet.
Beispielanwendung
In der Beispieldatenbank DBProperties.mdb finden Sie einige Anwendungsfälle für die in Kapitel 3 abgebildeten Routinen.
So werden die Fensterpositionen der beiden Formulare frmKundenSimple und frmKundenKomplex beim Schließen in einer benutzerdefinierten Eigenschaft gespeichert und beim nächsten Öffnen wiederhergestellt.
Des Weiteren öffnet sich beim Start der Datenbank nach dem Intro-Formular immer das Beispielformular, das Sie zuletzt geöffnet hatten.
Die dafür verantwortliche Code-Zeile sieht so aus:
DoCmd.OpenForm GetAProperty("LastForm")
Im UnLoad-Ereignis der Formulare steht jeweils:
SetAProperty "LastForm", Me.Name
Außerdem wird jedes Formular mit dem Datensatz geöffnet, mit dem man es verlassen hat. Dazu wird beim Entladen in einer Eigenschaft der Primärschlüssel des Datensatzes gespeichert und beim Öffnen mit Me.Recordset.FindFirst wieder angesprungen. Das funktioniert im Formular frmKundenKomplex sogar für den Detaildatensatz des eingebetteten Unterformulars.
|