Klassen programmieren unter VBA

Lies in den Artikel rein und unten bekommst Du ein unschlagbares Angebot!

Neben den Standardmodulen gibt es in VBA-Projekten auch noch einen weiteren Typ von Modulen, nämlich die Klassenmodule. Diese kommen wiederum in zwei Ausführungen: Es gibt alleinstehende Klassenmodule und eingebaute Klassenmodule, die den Code zu bestimmten Objekten enthalten – wie beispielsweise die Klassenmodule zu Formularen und Berichten in Access, zu Word-Dokumenten für das Dokument oder in Excel für Worksheet und Workbook. Auch in Outlook gibt es Klassenmodule. Wir wollen uns an dieser Stelle jedoch auf die alleinstehenden Klassenmodule konzentrieren, also auf solche, die wir selbst anlegen müssen. Hier schauen wir uns an, warum man diese überhaupt nutzen sollte, welche Anwendungszwecke es gibt und welche Best Practices sich für uns etabliert haben.

Unterschied zwischen Standardmodulen, eingebauten Klassenmodulen und benutzerdefinierten Klassenmodulen

Zunächst einmal wollen wir den Unterschied zwischen den verschiedenen Typen von Modulen erläutern.

Standardmodule

Wir beginnen mit den Standardmodulen:

  • Standardmodule sind einfache Container für VBA-Code, die in einer VBA-Projektdatei enthalten sind und die selbst hinzufügen müssen.
  • Sie enthalten normalerweise Funktionen, Prozeduren und Variablen, die in verschiedenen Teilen des Projekts verwendet werden können.
  • Ein Standardmodul kann Funktionen und Prozeduren enthalten, die unabhängig voneinander sind und direkt vom Rest des Codes aufgerufen werden können.
  • Standardmodule werden oft verwendet, um allgemeine Funktionen und Hilfsroutinen zu speichern, die von verschiedenen Teilen des Projekts benötigt werden.

Klassenmodule allgemein

Wir schauen uns erst einmal die allgemeinen Eigenschaften an, die allen Klassenmodulen gemein sind:

  • Klassenmodule ermöglichen die Definition benutzerdefinierter Datenstrukturen, die als Objekte bezeichnet werden.
  • Sie dienen dazu, spezifische Arten von Objekten zu definieren, die Eigenschaften, Methoden und Ereignisse haben können.
  • Klassenmodule ermöglichen die Erstellung von Objekten mit spezifischen Verhaltensweisen und Datenstrukturen, was die Codeorganisation und -wiederverwendbarkeit verbessert.
  • Sowohl eingebauten als auch alleinstehenden Klassenmodulen können wir benutzerdefinierte Eigenschaften, Methoden und Ereignisse hinzufügen.

Eingebaute Klassenmodule

Unter eingebauten Klassenmodulen verstehen wir solche Klassenmodule, wie sie beispielsweise in Word, Excel, Outlook oder Access automatisch mit den dortigen Objekten angelegt werden oder dort bereits vorhanden sind, wenn wir den VBA-Editor erstmalig öffnen.

Bild 1 zeigt ein Klassenmodul eines Word-Dokuments.

Ein eingebautes Klassenmodul, hier in Word.

Bild 1: Ein eingebautes Klassenmodul, hier in Word.

  • Klassenmodule können Ereignisse enthalten, die es Objekten ermöglichen, auf bestimmte Aktionen zu reagieren, wie das Öffnen oder Schließen einer Arbeitsmappe in Excel, das Öffnen eines Word-Dokuments oder das Anzeigen eines Formulars in Access.
  • Sie enthalten vorgefertigte Ereignisse, auf deren Basis wir Ereignisprozeduren implementieren können.

Alleinstehende Klassenmodule

Im Gegensatz zu den eingebauten Klassenmodulen können wir beliebig viele alleinstehende Klassenmodule erstellen. Dazu nutzen wir den Kontextmenübefehl Einfügen|Klassenmodul des Projekt-Explorers (siehe Bild 2) oder den gleichnamigen Befehl der Menüleiste des VBA-Editors.

Hinzufügen eines Klassenmoduls

Bild 2: Hinzufügen eines Klassenmoduls

Damit erzeugen wir erst einmal einen neuen Eintrag im Projekt-Explorer, der standardmäßig Klasse1 genannt und direkt als neues Fenster im VBA-Editor angezeigt wird (siehe Bild 3).

Das neue Klassenmodul im Projekt-Explorer

Bild 3: Das neue Klassenmodul im Projekt-Explorer

In dieses Fenster können wir nun Code schreiben, genau wie in einem Standardmodul. Wir können den dort enthaltenen Code aber nicht so einfach ausführen in einem Standardmodul. Wir müssen die Klasse erst in Form eines Objekts instanziieren und dieses einer Objektvariablen zuweisen, damit wir auf seine Eigenschaften, Methoden oder Ereignisse zugreifen können.

Klassenmodul umbenennen

Als Erstes erledigen wir allerdings eine wichtige Aufgabe: Das Klassenmodul soll einen anderen Namen als Klasse1 erhalten (siehe Bild 4).

Umbenennen eines Klassenmoduls

Bild 4: Umbenennen eines Klassenmoduls

Zu Beispielzwecken wollen wir die Klasse clsAdresse nennen. Das Präfix cls steht für Class, also Klasse. Mit der Klasse wollen wir die typischen Eigenschaften einer Adresse aufnehmen und abfragen können. Nach dem Umbenennen sehen wir den neuen Klassennamen gleich an mehreren Stellen (siehe Bild 5).

Der neue Klassenname tritt gleich an drei Stellen in Erscheinung.

Bild 5: Der neue Klassenname tritt gleich an drei Stellen in Erscheinung.

Welche Elemente machen eine Klasse nach außen hin aus?

Es gibt drei verschiedene Elemente, die wir von außen nutzen können, die eine Klasse charakterisieren:

  • Eigenschaften: Eigenschaften erscheinen als lesbare und/oder schreibbare Eigenschaften. Wir können für Klassen beliebig viele Eigenschaften festlegen und durch bestimmte Schreibweisen angeben, ob man diese bei der Programmierung der Klasse lesend, schreibend oder lesend und schreibend zugreifbar möchte.
  • Methoden/Funktionen: Dies sind im Prinzip Sub– und Function-Prozeduren wie in Klassenmodulen, nur dass sie nur aufgerufen werden können, wenn es eine Objektvariable mit einem Verweis auf eine Instanz der Klasse gibt, über die wir auf diese Methoden und Funktionen zugreifen können. Was Instanzen und Objektvariablen sind, erläutern wir weiter unten.
  • Ereignisse: Ereignisse sind eine echte Besonderheit von Klassenmodulen gegenüber Standardmodulen. Die Ereignisse von eingebauten Standardmodulen werden meist durch Benutzeraktionen ausgelöst. Wir können dann Ereignisprozeduren programmieren, die als Reaktion auf solche Benutzeraktionen gestartet werden sollen – zum Beispiel beim Öffnen eines Dokuments, beim Anklicken einer Schaltfläche, beim Absenden einer E-Mail et cetera. Bei Klassenmodulen können wir allerdings selbst genau festlegen, wann ein solches Ereignis ausgelöst werden soll. Wie das gelingt und wozu wir das nutzen können, zeigen wir ebenfalls weiter unten.

Welche Elemente gibt es noch innerhalb einer Klasse?

Innerhalb von Klassenmodulen können wir all die von herkömmlichen Modulen bekannten Elemente nutzen – Prozeduren, Funktionen, Variablen, Konstanten und so weiter.

Es gibt allerdings einen wichtigen Unterschied zwischen den Eigenschaften, Methoden und Ereignissen, die wir bei Vorhandensein einer Instanz der Klasse nutzen können, und den übrigen Elementen: Die Eigenschaften, Methoden und Ereignisse sind öffentliche Elemente, wir müssen diese daher mit dem Schlüsselwort Public deklarieren. Die Elemente, die wir nur innerhalb des Klassenmoduls nutzen wollen, deklarieren wir mit dem Schlüsselwort Private. Diese sind nicht von außerhalb sichtbar.

Instanziieren einer Klasse

Da unsere Klasse noch keine Elemente enthält, also keine Eigenschaften, Methoden oder Ereignisse, behelfen wir uns für die Beschreibung der Instanziierung einer Klasse einer eingebauten Klasse. In diesem Fall wollen wir uns die Klasse Collection ansehen. Diese ist eine Klasse, die den Umgang mit Auflistungen vereinfacht. Wir können dieser Klasse mit der Add-Methode Elemente hinzufügen, mit Remove Elemente entfernen, mit Count die enthaltenen Elemente zählen und mit Item(i) auf das Element mit dem Index i zugreifen.

Vor allem aber müssen wir, bevor wir das alles erledigen können, eine Instanz der Klasse erstellen und diese mit einer Objektvariablen referenzieren. Umgangssprachlich redet man auch von Speichern eines Objekts in einer Objektvariablen, was aber nicht ganz korrekt ist: Tatsächlich wird in einer Objektvariablen nämlich nur eine Zahl als Zeiger auf die Speicherposition des Objekts gespeichert. Das Objekt selbst befindet sich an der angegebenen Stelle im Speicher.

Doch schreiten wir zur Tat. Wir benötigen zwei Schritte, um eine Objektvariable mit einer neuen Instanz eines Objekts zu füllen:

  • das Deklarieren der Objektvariablen und
  • das Instanziieren des Objekts auf Basis der Klasse und Zuweisen an die Objektvariable.

Bei der Deklaration verwenden wir den Namen der Klasse als Datentyp:

Dim objCollection As Collection

Das Instanziieren erfolgt mit dem Schlüsselwort New mit dem Typ des zu erzeugenden Elements, hier Collection, und dem Zuweisen an die Objektvariable:

Set objCollection = New Collection

Wir können diese beiden Anweisungen sogar in einer einzigen Anweisung zusammenfassen:

Dim objCollection As New Collection

Wo deklarieren und wo instanziieren wir Klassen?

Bevor wir das Instanziieren anhand der Collection-Klasse ausprobieren, benötigen wir ein Modul, dem wir den Code hinzufügen können. Dazu erstellen wir ein neues Standardmodul, das wir unter dem Namen mdlBeispiele speichern. Warum ein Standardmodul und nicht etwa ein alleinstehendes Klassenmodul, wo wir doch schon über diesen Modultyp reden? Weil wir diese Anweisungen direkt ausprobieren wollen, und das gelingt am einfachsten in Standardmodulen. Hier legen wir eine Prozedur an, welche die beiden oben genannten Schritte enthält:

Public Sub CollectionInstanziieren()
     Dim col As Collection
     Set col = New Collection
End Sub

Wenn wir diese Prozedur ausführen, geschieht nichts Sichtbares. Dazu benötigen wir noch einige weitere Anweisungen wie hier:

Public Sub CollectionInstanziieren()
     Dim objCollection As Collection
     Dim i As Integer
     Set objCollection = New Collection
     objCollection.Add "Element 1"
     objCollection.Add "Element 2"
     For i = 1 To objCollection.Count
         Debug.Print objCollection.Item(i)
     Next i
End Sub

Dies erstellt ein neues Collection-Objekt in der Variablen objCollection, fügt mit der Add-Methode zwei Elemente hinzu und durchläuft anschließend eine Schleife über alle Elemente, wobei sie deren Inhalt im Direktbereich des VBA-Editors ausgibt. Die Objektvariable wird nach dem Verlassen der Prozedur automatisch zerstört, weil sich der Gültigkeitsbereich durch die Deklaration innerhalb der Prozedur auf diese beschränkt. Um das zu ändern, können wir nun noch die Deklaration von objCollection aus der Prozedur herausziehen und diese im Kopf des Standardmoduls speichern:

Dim objCollection As Collection

Dadurch verlängern wir die Lebenszeit dieses Objekts. Wir instanziieren es wieder und fügen zwei Elemente hinzu:

Public Sub CollectionFuellen()
     Set objCollection = New Collection
     objCollection.Add "Element 1"
     objCollection.Add "Element 2"
End Sub

Da die Variable nun außerhalb der Prozedur gespeichert wurde, können wir von einer weiteren Prozedur aus, die wir anschließend aufrufen, immer noch auf die Inhalte der Collection zugreifen:

Public Sub CollectionAuslesen()
     Dim i As Integer
     For i = 1 To objCollection.Count
         Debug.Print objCollection.Item(i)
     Next i
End Sub

Nun wissen wir schon einmal, wie wir Objekte auf Basis von eingebauten Klassen instanziieren. Nun wollen wir eine eigene Klasse programmieren und diese instanziieren und nutzen.

Sichtbarkeit der Elemente einer Klasse

Dazu kehren wir zu der Klasse clsAdresse zurück, die wir bereits weiter oben erstellt haben. Dieser Klasse fügen wir nun eine erste öffentliche Eigenschaft namens Vorname hinzu. Diese deklarieren wir einfach als öffentliche Variable des Typs String:

Public Vorname As String

Die Klasse sieht bisher also wie in Bild 6 aus. Dennoch können wir die Klasse bereits als vollwertige Klasse einsetzen. Dazu erstellen wir ein weiteres Modul, dem wir die Deklaration einer Objektvariablen auf Basis unserer Klasse hinzufügen:

Der bisherige Code der Klasse

Bild 6: Der bisherige Code der Klasse

Dim objAdresse As clsAdresse

Instanziieren wir die Klasse nun in einer Prozedur, können wir anschließend nach Eingabe des Variablennamens und des Punkts per IntelliSense auf die bisher einzige Eigenschaft der Klasse zugreifen – siehe Bild 7.

Instanziieren und Verwenden einer benutzerdefinierten Klasse

Bild 7: Instanziieren und Verwenden einer benutzerdefinierten Klasse

Wir weisen der Eigenschaft wie folgt einen Wert zu:

Public Sub AdresseInstanziieren()
     Set objAdresse = New clsAdresse
     objAdresse.Vorname = "André"
End Sub

In einer weiteren Prozedur können wir nun immer noch auf die Eigenschaft zugreifen, da das Objekt in der Variablen objAdresse des Standardmoduls gespeichert und somit immer noch gültig ist:

 

Schreibe einen Kommentar