window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-TCJTE9L38H');

Klassenmodule und benutzerdefinierte Typen

Lies diesen Artikel und viele weitere mit einem kostenlosen, einwöchigen Testzugang.

Die allermeisten Prozeduren und Funktionen des VBA-Projekts Ihrer Datenbank werden Sie in ganz normalen Modulen unterbringen. Dabei wird leicht übersehen, dass es sich etwa bei Formularen um eine andere Art Module handelt, nämlich um Klassenmodule. Diese können Sie aber auch selbst anlegen. Erfahren Sie hier, wie Sie dabei vorgehen. Wir leiten das Thema zunächst jedoch mit den benutzerdefinierten Typen ein, welche Klassenmodule verständlicher machen.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 1601_Klassenmodule.accdb

Benutzerdefinierte Typen

Ein benutzerdefinierter Typ ist nichts weiter, als eine zusammengefasste Struktur von Variablen.

Interessanterweise findet man Beispiele dafür im VBA-Objektkatalog weder in der Access-Bibliothek, noch unter den VBA- oder Office-Bibliotheken. Also stellen wir gleich eine eigene Kreation vor:

Public Type TAdresse
     ID As Long
     Nachname As String
     Vorname As String
     Strasse As String
     PLZ As String
     Ort As String
End Type

Der reservierte Begriff Type definiert, dass unter dem folgenden Namen TAdresse eine Variablenstruktur folgt. Solche Typen können im Gültigkeitsbereich auch mit Private gekennzeichnet sein, was sie nur für das Modul zugreifbar macht, in dem sich die Deklaration befindet. Public bedeutet hier, dass nun das gesamte VBA-Projekt den Typen kennt und auch aus anderen Modulen heraus verwendet werden kann.

Die einzelnen Variablen des Typs werden in den folgenden Zeilen durch Namen und Variablentypen definiert. Jeglicher Datentyp ist hier möglich – etwa auch As Object.

Wozu ist das gut Kann man die Elemente der Struktur nicht auch als einzelne Variablen deklarieren Es gibt sicher viele Fälle, für die die Antwort hier Ja lautet. Doch schauen wir uns an, welche Vorteile der benutzerdefinierte Typ bietet. Denn er verhält sich genau so, wie jeder andere Datentyp. Also kann er einer Variablen zugeordnet werden:

Dim A As TAdresse

Der Zugriff auf die Elemente des Typs geschieht über Punkt und Namen:

A.Nachname = "Minhorst"
A.Vorname = "André"
A.Ort = "Duisburg"
Debug.Print A.Nachname

Der Code lässt sich noch über die With-Anweisung vereinfachen:

With A
     .Nachname = "Minhorst"
     .Vorname = "André"
     .Ort = "Duisburg"
     Debug.Print .Nachname
End With

Das wäre schon einmal Vorteil Nummer eins. Nach Eingabe von A und Punkt stellt das IntelliSense von VBA gleich alle Elemente des Typs in der Auswahlliste vor. Und das übrigens auch innerhalb des With-Blocks, wo allein die Punkt-Eingabe für das Auslösen von IntelliSense ausreicht.

Besonders nützlich sind benutzerdefinierte Typen jedoch dann, wenn Sie in Arrays Verwendung finden:

Dim Adressen() As TAdresse
ReDim Adressen(99)
Adressen(0).Nachname = "Minhorst"
Adressen(0).Vorname = "André"
Adressen(1).Nachname = "Trowitzsch"
Adressen(1).Vorname = "Sascha"
...

Das Array Adressen ist vom Typ TAdresse. Es wird im ersten Schritt auf Hundert Elemente dimensioniert. Danach folgt die Zuweisung der Werte einerseits über die Indizes des Arrays, andererseits über den Namen der Elemente. Wollte man das über separate Variablen erreichen, so benötigte man für jedes Element des Typs ein eigenes gesondertes Array. Das machte die Angelegenheit deutlich unüberschaubarer, als es das Handling mit einem Array des benutzerdefinierten Typs erlaubt.

Die Sache lässt sich noch weiterspinnen. Durch den Fakt, dass ein Typelement Variablen beliebigen Datentyps aufweisen darf, kommt für dieses selbst ein benutzerdefinierter Typ infrage:

Public Type TAdressen
     Items() As TAdresse
End Type

Zwar enthält der Typ nur ein Element, welches jedoch als Array des benutzerdefinierten TAdresse ausgelegt ist. Damit ist nur eine Variable vom Typ TAdressen imstande mehrere Adressen zu speichern:

Dim A As TAdressen
ReDim A.Items(99)
With A
     .Items(0).Nachname = "Minhorst"
     .Items(0).Vorname = "André"
     .Items(1).Nachname = "Trowitzsch"
     .Items(1).Vorname = "Sascha"
    ...
End With
Debug.Print A.Items(1).Vorname

Auch hier führt der benutzerdefinierte Typ zu strukturierterer Programmierung. Treiben wir es noch einen Schritt weiter. Es lägen etwa Adressen für verschiedene Anwendungsbereiche vor, seien es Kundenadressen, Ansprechpartner, Behörden, Firmen.

Dann könnten alle Adressen zusammen wiederum in einen neuen Typ überführt werden:

Public Enum eAddressType
     eAddressUnspecific = 0 
     eAddressCustomer = 1
     eAddressPartner = 2
     eAddressCompany = 3
End Enum
Public Type TAdressenGesamt
     AddressType As sAddressType
     AddressBlock As TAdressen
End Type

Die Zuordnung der Werte wird nun zwar etwas länglich, ist aber dennoch gut zu überblicken:

Dim A() As TAdressenGesamt
ReDim A(3)
A(1).AddressType = eAddressCustomer
Redim A(1).AdressBlock.Items(9)
With A(1).AdressBlock
    .Items(0).Nachname = "Minhorst"
    .Items(0).Vorname = "Minhorst"
    .Items(1).Nachname = "Trowitzsch"
    .Items(1).Vorname = "Sascha"
     ...
End With
A(2).AddressType = eAddressUnspecific
Redim A(2).AdressBlock.Items(9)
With A(2).AdressBlock
    .Items(0).Nachname = "Häberle"
    .Items(0).Vorname = "Adam"
    .Items(1).Nachname = "Gutschmidt"
    .Items(1).Vorname = "Andrea"
     ...
End With
...

Immerhin haben wir nun mehrere Adressblöcke unterschiedlichen Umfangs und Typs in einer Variablen vereint! Das einzig hinderlich ist die Notwendigkeit, die Items-Arrays jeweils neu zu dimensionieren. Das ließe sich mit einer festen Anzahl von Elementen umgehen:

Public Type TAdressen
     Items(99) As TAdresse
End Type

Damit läge die Maximalzahl von Adressen für einen Block bei Hundert. Probleme gibt stellen sich also ein, wenn doch mehr benötigt würden. Und bei kleinerer Anzahl würde außerdem Speicherplatz vergeudet. Dieses Beispiel sollte eigentlich nur eines demonstrieren, nämlich wie sich benutzerdefinierte Typen im Interesse von Strukturierung verschachteln lassen. Das Ganze geht in Richtung Objektorientierter Programmierung, mit der wir es später bei den Klassenmodulen noch mehr zu tun bekommen.

Tabellendaten in benutzerdefinierten Typen

Damit es nicht bei trockener Theorie bleibt, folgt ein Beispiel, das in der einen oder anderen Datenbank durchaus vertreten sein kann. Es geht hier darum, die Datensätze einer Tabelle oder Abfrage in VBA-Strukturen zu überführen.

Listing 1 zeigt das Grundgerüst mit der Prozedur TestUDT. Nebenbei erwähnt ist UDT das Kürzel für einen benutzerdefinierten Typ (User Defined Type). Eine Datensatzgruppe rs wird auf alle Datensätze der Tabelle tblAdressen geöffnet. In einer Do-Loop-Schleife werden sie alle durchlaufen und die einzelnen Feldinhalte den Elementen der Variablen T des Typs TAdresse verabreicht. Da mache Felder der Datensätze leer sind und Leer nicht in einem String gespeichert werden kann – Strasse ist etwa vom Typ String – müssen die Werte noch einer Vorbehandlung mit der Funktion Nz unterzogen werden, die aus Nullwerten ordnungsgemäße Strings der Länge 0 macht.

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

Testzugang

eine Woche kostenlosen Zugriff auf diesen und mehr als 1.000 weitere Artikel

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar