André Minhorst, Duisburg
VBA wird im Allgemeinen die Eigenschaft abgesprochen, eine objektorientierte Programmiersprache zu sein. Um diese Aussage zu untersuchen, müsste man erst einmal festlegen, ab wann eine Sprache objektorientiert ist und welche Eigenschaften für diese Bezeichnung vorhanden sein müssen. Lässt man einmal außen vor, dass Vererbung und Polymorphismus im VBA-Sprachgebrauch Fremdwörter sind, kann man VBA sicher als objektorientierte Sprache auffassen. Wie auch immer – im vorliegenden Beitrag erfahren Sie, wie Sie sich die objektorientierten Eigenschaften von VBA zu Nutze machen.
Wer mit Access arbeitet und dabei VBA für die Entwicklung von Datenbankanwendungen verwendet, kann vermutlich mit Objekten verschiedenen Typs umgehen – wenn er auch vielleicht noch nie einen eigenen Objekttyp erstellt hat.
Sicher hat jeder schon einmal ein Recordset via VBA geöffnet und auf die darin enthaltenen Methoden wie Open, MoveNext, AddNew, Update oder Close zugegriffen oder Informationen aus Eigenschaften wie RecordCount, EOF oder Filter verwendet. Beispiele dafür zeigt die Routine aus Quellcode 1, die eine auf der Tabelle tblKontakte basierende Datensatzgruppe öffnet und den Inhalt der einzelnen Datensätze ausgibt. Mit diesem Code erzeugt man unter anderem eine Instanz des Objekttyps Recordset, legt einige seiner Eigenschaften wie beispielsweise die Datenherkunft und die zugrunde liegende Verbindung fest und greift anschließend auf die so verfügbar gemachten Daten zu.
Public Sub OpenRecordset() Dim cnn As ADODB.Connection Dim rst As ADODB.Recordset Set cnn = CurrentProject.Connection Set rst = New ADODB.Recordset rst.Open "tblKontakte", cnn, adOpenKeyset, _ adLockPessimistic Do While Not rst.EOF Debug.Print rst!KontaktID, rst!Vorname, _ rst!Nachname rst.MoveNext Loop rst.Close Set rst = Nothing Set cnn = Nothing End Sub
Quellcode 1
Eine weitere, ganz offensichtliche Objektart ist beispielsweise ein Formular – wie für ein Objekt üblich, verfügt es über Methoden, Eigenschaften und Ereignisse. Ein Formularobjekt kann wiederum Steuerelemente enthalten, die ebenfalls Objekttypen repräsentieren.
Mit den in Access vorhandenen Objekttypen lässt sich jede Menge nützlicher Dinge anstellen. Ein wichtiger Aspekt dabei ist, dass die unterschiedlichen Objekttypen bestimmte Methoden, Eigenschaften und Ereignisse enthalten. Ein Objekt des Typs Recordset fasst beispielsweise eine Menge Funktionalität zusammen. Und das Beste daran ist, dass man sich gar nicht darum kümmern muss, was im Innern dieses Objekts passiert – es reicht völlig aus, dass man die Methoden, Eigenschaften und Ereignisse kennt. Wen interessiert denn schon, was intern alles abläuft, wenn man die Methode AddNew eines Recordset-Objekts aufruft Wichtig ist allein das Kennen der Schnittstelle und dass das Objekt die gewünschten Reaktionen und Ergebnisse auf die getätigten Eingaben liefert.
Hinweis
Um keine Verwirrung bezüglich der hier verwendeten Begriffe aufkommen zu lassen, sollen folgende Definitionen gelten: Der Inhalt eines Klassenmoduls definiert eine Klasse. Eine Klasse wird auch Objekttyp genannt. Das Instanzieren einer Klasse beziehungsweise eines Objekttyps erzeugt ein Objekt. Auf das Objekt kann man in der Folge über die Objektvariable zugreifen. Das Klassenmodul enthält also einen Entwurf dessen, wie das Objekt sich verhalten soll. Dieser Entwurf besteht aus der Definition geeigneter Eigenschaften, Methoden und Ereignisse sowie der dahinter liegenden Funktionen.
Nun bietet Access nur eine begrenzte Menge Objekttypen, die allerdings bereits viele Anforderungen abdecken. Genau genommen sind Art und Menge der Objekttypen gerade so bemessen, dass sie als vernünftige Grundlage für den Aufbau der jeweils individuell abzubildenden Geschäftsprozesse einer Anwendung dienen.
Damit gibt es bereits einige gute Gründe, um eigene Objekttypen zu schaffen:
Wie bereits oben erwähnt, ist einer der Vorteile der Verwendung von Objekttypen, dass man reelle Objekte und deren Eigenschaften in einer Einheit zusammenfassen kann, deren Methoden, Eigenschaften und Ereignisse über eine entsprechende Schnittstelle erreichbar sind. Unter VBA spricht man in diesem Zusammenhang von einem Klassenmodul.
Als Beispiel für die Erläuterung des Aufbaus und der Verwendung von Objekttypen dient ein Kontakt. Er enthält bestimmte Informationen zu einer Person wie Vorname, Nachname, Geschlecht und Adressdaten. Um nicht nur die Verwendung von Eigenschaften, sondern auch den Einsatz von Methoden vorzustellen, erhält die Beispielobjektklasse zusätzlich eine Routine zur Ausgabe der Adressdaten in Form einer Anschrift.
Hinweis
Die zu den nachfolgenden Beispielen gehörenden Objekte und Codes finden Sie in der Beispieldatenbank OOMitAccess.mdb für Access 2000 und höher auf der beiliegenden CD.
Erstellen eines Klassenmoduls
Access stellt drei unterschiedliche Modularten zur Verfügung: die Klassenmodule von Formularen und Berichten, Standardmodule sowie Klassenmodule, die nicht an ein bestimmtes Objekt wie ein Formular oder einen Bericht gebunden sind. Die Klassenmodule von Formularen und Berichten sind prinzipiell mit den im Anschluss vorgestellten Klassenmodulen identisch; der einzige Unterschied ist, dass Letztere keine Oberfläche in Form eines Formulars oder Berichts enthalten.
Um ein solches Klassenmodul zu erstellen, wählen Sie im VBA-Editor von Access den Menüeintrag Einfügen/Klassenmodul aus. Daraufhin öffnet Access ein fast leeres neues Klassenmodul, das Sie am besten direkt unter dem gewünschten Namen speichern – beispielsweise clsKontakt. Wählen Sie den Namen eines Klassenmoduls immer so, dass er auch verrät, welches Objekt sich dahinter verbirgt – später werden Sie über diesen Namen auf diese Klasse zugreifen.
Praxis-Tipp
Damit der Debugger sich meldet, wenn eine Variable nicht ordnungsgemäß deklariert ist, sollten Sie im Prozedurkopf die Anweisung Option Explicit hinzufügen.
Schreib- und lesbare Variablen
Die Eigenschaften eines Objekttyps speichert man in herkömmlicher Weise in Variablen. Die Zugriffsmöglichkeiten auf diese Variablen kann man allerdings wesentlich flexibler gestalten als in Prozeduren in Standardmodulen.
Sie können eine Variable innerhalb eines Klassenmoduls natürlich als öffentlich zugänglich deklarieren, indem Sie etwa folgende Anweisung verwenden:
Public Vorname As String
Damit können Sie den Wert dieser Variablen von außen lesen und auch ändern, haben aber keinerlei Vorteile der in Klassenmodulen üblichen Art der Deklaration.
Dort gibt es nämlich so genannte Property-Funktionen, über die man von außen den Wert einer Variablen lesen und ändern kann. Daher deklariert man Variablen in Klassenmodulen niemals als öffentlich, sondern immer als privat. Die Property-Funktionen erlauben nicht nur den schreibenden und lesenden Zugriff auf die privaten Variablen (wobei es für jede Zugriffsart eine eigene Funktion gibt), sondern man kann dort beliebige weitere Anweisungen unterbringen. Auf diese Weise lässt sich beispielsweise ein Zeiger setzen, der Informationen darüber enthält, ob sich eine Variable seit Erstellung der Objektinstanz geändert hat.
Namenskonventionen
Die Variablen, die über Property-Funktionen für die Außenwelt erreichbar sein sollen, kennzeichnet man durch Voranstellen eines weiteren Buchstabens zum eigentlichen Variablennamen. Genau genommen wählt man einen Namen aus, unter dem die Variable nach außen erscheinen soll, wie beispielsweise Vorname, und nennt die Variable intern mVorname.
Hinzufügen einer Variablen
Um eine bessere Vorstellung davon zu bekommen, was es mit diesen Property-Funktionen auf sich hat, fügen Sie der Klasse einfach eine Variable hinzu und erstellen zwei passende Property-Funktionen:
Dim mVorname As String Public Property Get Vorname() As String Vorname = mVorname End Property Public Property Let Vorname(strVorname _ As String) mVorname = strVorname End Property
Testen der Variablen
Zum Testen der Funktionsweise verwenden Sie eine Prozedur namens BeispielKontakt in einem beliebigen Standardmodul. Um die Klasse verfügbar zu machen, deklarieren Sie zunächst eine entsprechende Objektvariable:
Public Sub KontaktBeispiel() Dim objKontakt As clsKontakt ''... weitere Anweisungen End Sub
Nach der Eingabe des Schlüsselwortes As erscheint die Liste aller verfügbaren Objekttypen, unter denen sich nun auch die neu erstellte Klasse befindet – wenn Sie also später mal eine ganze Menge eigener Objekttypen verwenden, müssen Sie sich noch nicht einmal mehr deren Namen genau merken.
Anschließend erzeugen Sie eine Instanz dieses Objekttyps, die Sie in der Prozedur verwenden möchten:
Set objKontakt = New clsKontakt
Sie könnten die ersten beiden Anweisungen auch zu einer einzigen Anweisung zusammenfassen:
Dim objKontakt As New clsKontakt
Dadurch sparen Sie zwar eine Zeile, aber wenn Sie die Objektinstanz möglicherweise erst später benötigen oder es sich erst im Verlaufe der Prozedur herausstellt, ob Sie diese überhaupt brauchen, verschwenden Sie unter Umständen wertvolle Ressourcen.
Damit auch alles seine Ordnung hat und Objekte nicht unnötig Speicherplatz belegen, obwohl sie nicht mehr benötigt werden, legen Sie vorsichtshalber jetzt schon die Anweisung zum Zerstören des Objektes an:
Set objKontakt = Nothing
Das ist zwar im vorliegenden Fall nicht unbedingt erforderlich, da das Objekt ohnehin nach Beenden der Prozedur zerstört wird, aber es ist programmiertechnisch sauberer. Die folgenden Beispielanweisungen fügen Sie natürlich vor dieser Anweisung ein, da sie sich sonst auf ein nicht mehr vorhandenes Objekt bezögen.
Und nun geht“s an die Variable mVorname und deren Property-Prozeduren, die zusammen die les- und schreibbare Eigenschaft Vorname ergeben. Die folgende Anweisung setzt den Wert dieser Variablen auf den Wert Heinz:
objKontakt.Vorname = "Heinz"
Dank Intellisense erscheint der Eigenschaftsname direkt nach der Eingabe des Punktes hinter objKontakt in der Liste der verfügbaren Eigenschaften, Methoden und Ereignisse dieses Objekts – bisher also allein auf weiter Flur (s. Abb. 1).
Abb. 1: Intellisense vereinfacht den Zugriff auf Objekteigenschaften.
Um zu überprüfen, ob die Zuweisung funktioniert, geben Sie mit nachfolgender Anweisung den Inhalt der Eigenschaft im Testfenster aus:
Debug.Print objKontakt.Vorname
Was ist passiert
über die Zuweisung der Zeichenkette an objKontakt.Vorname haben Sie die Property Let-Methode aufgerufen. Diese nimmt den übergebenen Wert über den Parameter strVorname entgegen und weist ihn der privaten Variablen mVorname zu.
Bei der nachfolgenden Ausgabe läuft es umgekehrt: Die Propery Get-Prozedur ermittelt den Wert der Variablen mVorname und schickt ihn über die Eigenschaft Vorname zur Ausgabe.
Hinweis
Vielleicht haben Sie den großen Vorteil dieser Vorgehensweise schon erkannt: Sie können mit Hilfe von Get- und Let-Prozeduren für eine Property festlegen, ob diese zum Lesen, Schreiben oder gar für beide Zugriffsarten verfügbar sein soll.
Im Gleichschritt: Marsch!
Nachdem Sie sich von der Funktionsweise der Eigenschaft Vorname überzeugt haben, können Sie nun die Variablen für die weiteren Eigenschaften des Kontakts wie Nachname, Strasse, PLZ, Ort, Land und Unternehmen sowie die entsprechenden Property Let und Property Get-Prozeduren anlegen. Damit hätten Sie eine Klasse erzeugt, die wichtige Eigenschaften eines Kontaktes erhalten und wieder ausgeben kann.
Die Ereignisse Initialize und Terminate
Klassenmodule stellen zwei eingebaute Ereigniseigenschaften zur Verfügung, die beim Erzeugen und beim Zerstören einer Objektinstanz ausgelöst werden.
Um die entsprechenden Ereigniseigenschaften zu erzeugen, wählen Sie einfach im VBA-Editor im linken Kombinationsfeld den Eintrag Class und im rechten den Namen der gewünschten Ereignisprozedur aus (s. Abb. 2).
Abb. 2: Erzeugen einer Ereignisprozedur einer Klasse
Diese Eigenschaften können Sie beispielsweise verwenden, um beim Erzeugen der Objektinstanz Variablen auf bestimmte Startwerte oder Verweise auf andere Objekte zu setzen. Beim Zerstören einer Objektinstanz erledigt man mit der entsprechenden Ereignisprozedur notwendige Aufräumarbeiten.
Verwenden von Enumerationen
Sicher kennen Sie die Möglichkeit, per Intellisense eine vordefinierte Konstante für einen Parameter auszuwählen. Das Gleiche ist auch mit den Eigenschaften einer Objektinstanz möglich. Um bei unserem Beispiel mit den Kontaktdaten zu bleiben, fügen wir diesem noch eine Eigenschaft namens Geschlecht hinzu. Dafür gibt es unter normalen Umständen nur zwei mögliche Einträge, deren Auswahl man dem Benutzer gerne abnimmt. Die passende Enumeration sieht folgendermaßen aus:
Public Enum enumGeschlecht männlich weiblich End Enum
Die Definition der entsprechenden Variablen sowie der Property Let- und Get-Prozeduren sieht wie folgt aus:
Dim mGeschlecht As enumGeschlecht Public Property Get Geschlecht() As enumGeschlecht Geschlecht = mGeschlecht End Property Public Property Let Geschlecht(lngGeschlecht As enumGeschlecht) mGeschlecht = lngGeschlecht End Property
Es gibt also prinzipiell keine Unterschiede zu herkömmlichen Datentypen, mit der Ausnahme, dass der Datentyp als Enumeration definiert wird.
Hier sind zwei Dinge zu beachten: Beim Zugriff auf diese Objekteigenschaft stehen zwar die beiden Konstanten zur Verfügung (s. Abb. 3). Wenn man sich den ausgewählten Wert nachher ausgeben lässt, erhält man aber nicht den ausgewählten Ausdruck, sondern die dahinter stehende Konstante.
Public Function Anschrift() As String Dim strAnschrift As String Dim strAnrede As String If mGeschlecht = männlich Then strAnrede = "Herrn " Else strAnrede = "Frau " End If If Not mUnternehmen = "" Then strAnschrift = strAnschrift & mUnternehmen & vbCrLf strAnschrift = strAnschrift & strAnrede & mVorname & " " & mNachname & vbCrLf Else strAnschrift = strAnschrift & strAnrede & vbCrLf strAnschrift = strAnschrift & mVorname & " " & strNachname & vbCrLf End If strAnschrift = strAnschrift & mStrasse & vbCrLf strAnschrift = strAnschrift & mPLZ & " " & mOrt & vbCrLf strAnschrift = strAnschrift & mLand Anschrift = strAnschrift End Function
Quellcode 1
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
den kompletten Artikel im PDF-Format mit Beispieldatenbank
diesen und alle anderen Artikel mit dem Jahresabo