IntelliSense für Tabellen und Felder

Wenn Sie mit dem VBA-Editor arbeiten und dort gelegentlich SQL-Anweisungen eingeben, müssen Sie die Namen der verwendeten Tabellen, Abfragen und Felder entweder kennen oder immer wieder zum Access-Fenster wechseln, um die Tabellen zu öffnen und die fehlenden Informationen nachzuschlagen. Dies können Sie mit dem hier beschriebenen Vorgehen in Zukunft wesentlich vereinfachen!

Beispiele für solche Texte sind die folgenden:

Set rst = db.OpenRecordset("SELECT tblArtikel.ArtikelID, tblArtikel.Artikelname 
FROM tblArtikel", dbOpenDynaset)
db.Execute "UPDATE tblArtikel SET tblArtikel.Einzelpreis = 10 
WHERE tblArtikel.Artikelname LIKE 'Chai'"

Wie wäre es denn, wenn Sie die SQL-Anweisung mit IntelliSense-Unterstützung erstellen könnten? Dies könnten Sie wie in folgendem Bild vom Direktfenster aus erledigen oder auch direkt im Codefenster:

Dazu geben Sie einfach nur die ersten Zeichen des gesuchten Tabellennamens ein und betätigen dann die Tastenkombination für IntelliSense, nämlich Umschalt + Leertaste.

Und es kommt noch besser: Wenn Sie den Tabellennamen eingegeben haben und einen Punkt hinzufügen, zeigt IntelliSense alle Feldnamen an:

Den so zusammengestellten SQL-Ausdruck müssen Sie nur noch mit vorangehendem

db.OpenRecordset("...", dbOpenDynaset) 

ausstatten, schon ist die SQL-Anweisung fertig.

Wie funktioniert das Ganze? Nun: Wir haben für die Tabelle tblArtikel eine gleichnamige Klasse erstellt, deren Inhalt zunächst wie folgt aussieht:

Public Property Get ArtikelID()
ArtikelID = rst!ArtikelID
End Property
Public Property Get Artikelname()
Artikelname = rst!Artikelname
End Property
Public Property Get LieferantID()
LieferantID = rst!LieferantID
End Property
Public Property Get KategorieID()
KategorieID = rst!KategorieID
End Property
Public Property Get Liefereinheit()
Liefereinheit = rst!Liefereinheit
End Property
Public Property Get Einzelpreis()
Einzelpreis = rst!Einzelpreis
End Property
Public Property Get Lagerbestand()
Lagerbestand = rst!Lagerbestand
End Property
Public Property Get BestellteEinheiten()
BestellteEinheiten = rst!BestellteEinheiten
End Property
Public Property Get Mindestbestand()
Mindestbestand = rst!Mindestbestand
End Property
Public Property Get Auslaufartikel()
Auslaufartikel = rst!Auslaufartikel
End Property

Das erklärt, warum nach Eingabe des Punktes die einzelnen Feldnamen angezeigt werden – es handelt sich schlicht um Property Get-Eigenschaften. Aber warum können wir einfach so die Klassennamen wie tblArtikel per IntelliSense nutzen? Dies gelingt bei herkömmlichen Klassen nicht. Wir haben an dieser Stelle einen kleinen Trick angewendet, der die Klasse ohne Instanzierung ausführbar macht. Dadurch erscheint auch der Klassenname direkt in der IntelliSense-Liste.

tblArtikel statt rst

Aber wir haben im gleichen Zuge noch ein cooles Feature zu der Klasse hinzugefügt. Damit können Sie nun direkt über den Tabellennamen auf die enthaltenen Daten zugreifen statt erst ein Recordset erstellen zu müssen. Das sieht dann etwa so aus:

Public Sub Test()
With tblArtikel
Do While Not .EOF
Debug.Print .ArtikelID, .Artikelname
.MoveNext
Loop
End With
End Sub

Neben den einzelnen Feldern stehen auch noch ein paar weitere Methoden und Eigenschaften wie MoveFirst, MoveLast, MovePrevious, MoveNext oder auch FindFirst zur Verfügung.

All dies erreichen wir durch entsprechende Methoden in der Klasse, die so aussehen:

Dim db As DAO.Database
Dim rst As DAO.Recordset

Private Sub Class_Initialize()
Set db = CurrentDb
Set rst = db.OpenRecordset("SELECT * FROM tblArtikel", dbOpenDynaset)
End Sub

Public Sub FindFirst(strFilter As String)
rst.FindFirst strFilter
End Sub

Public Sub MoveNext()
rst.MoveNext
End Sub

Public Sub MovePrevious()
rst.MovePrevious
End Sub

Public Sub MoveFirst()
rst.MoveFirst
End Sub

Public Sub MoveLast()
rst.MoveLast
End Sub

Public Function EOF() As Boolean
EOF = rst.EOF
End Function

Public Function BOF() As Boolean
BOF = rst.BOF
End Function

Die Klasse erstellt also direkt beim Initialisieren ein Database-Objekt und ein Recordset-Objekt mit allen Datensätzen der Tabelle. Über die Methoden und Eigenschaften können Sie dann genau wie bei einem Recordset-Objekt auf die Tabelle zugreifen.

Tabellenklassen per Code erzeugen

Nun wäre es etwas viel Aufwand, für jede Tabelle von Hand eine entsprechende Klasse zu erzeugen. Deshalb haben wir eine Prozedur geschrieben, welche die Klassen für alle mit tbl beginnenden Tabellen der Datenbank anlegt. Diese kopieren Sie einfach in ein Standardmodul und starten sie. Achtung: Sie benötigen einen Verweis auf die Bibliothek Microsoft Visual Basic For Applications Extensibility 5.3:

Hier ist der Code der Prozedur zum Erstellen der Klassen:

Public Sub TabelleZuKlasse()
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Dim objCodeModule As VBIDE.CodeModule
Dim objVBProject As VBIDE.VBProject
Dim objVBComponent As VBIDE.VBComponent
Dim strKlasse As String
Dim strCode As String
Set objVBProject = VBE.ActiveVBProject
Set db = CurrentDb
For Each tdf In db.TableDefs
If tdf.Name Like "tbl*" Then
On Error Resume Next
objVBProject.VBComponents.Remove objVBProject.VBComponents(tdf.Name)
On Error GoTo 0
End If
Next tdf
For Each tdf In db.TableDefs
If tdf.Name Like "tbl*" Then
Set objVBComponent = objVBProject.VBComponents.Add(vbext_ct_ClassModule)
objVBComponent.Name = tdf.Name
strCode = ""
strCode = strCode & "Dim db As DAO.Database" & vbCrLf
strCode = strCode & "Dim rst As DAO.Recordset" & vbCrLf
strCode = strCode & vbCrLf
strCode = strCode & "Private Sub Class_Initialize()" & vbCrLf
strCode = strCode & " Set db = CurrentDb" & vbCrLf
strCode = strCode & " Set rst = db.OpenRecordset(""SELECT * FROM " _
& tdf.Name & """, dbOpenDynaset)" & vbCrLf
strCode = strCode & "End Sub" & vbCrLf
strCode = strCode & vbCrLf
strCode = strCode & "Public Sub FindFirst(strFilter As String)" & vbCrLf
strCode = strCode & " rst.FindFirst strFilter" & vbCrLf
strCode = strCode & "End Sub" & vbCrLf
strCode = strCode & vbCrLf
strCode = strCode & "Public Sub MoveNext()" & vbCrLf
strCode = strCode & " rst.MoveNext" & vbCrLf
strCode = strCode & "End Sub" & vbCrLf
strCode = strCode & vbCrLf
strCode = strCode & "Public Sub MovePrevious()" & vbCrLf
strCode = strCode & " rst.MovePrevious" & vbCrLf
strCode = strCode & "End Sub" & vbCrLf
strCode = strCode & vbCrLf
strCode = strCode & "Public Sub MoveFirst()" & vbCrLf
strCode = strCode & " rst.MoveFirst" & vbCrLf
strCode = strCode & "End Sub" & vbCrLf
strCode = strCode & vbCrLf
strCode = strCode & "Public Sub MoveLast()" & vbCrLf
strCode = strCode & " rst.MoveLast" & vbCrLf
strCode = strCode & "End Sub" & vbCrLf
strCode = strCode & vbCrLf
strCode = strCode & "Public Function EOF() As Boolean" & vbCrLf
strCode = strCode & " EOF = rst.EOF" & vbCrLf
strCode = strCode & "End Function" & vbCrLf
strCode = strCode & vbCrLf
strCode = strCode & "Public Function BOF() As Boolean" & vbCrLf
strCode = strCode & " BOF = rst.BOF" & vbCrLf
strCode = strCode & "End Function" & vbCrLf
strCode = strCode & vbCrLf
For Each fld In tdf.Fields
strCode = strCode & "Public Property Get " & fld.Name & "()" & vbCrLf
strCode = strCode & " " & fld.Name & " = rst!" & fld.Name & vbCrLf
strCode = strCode & "End Property" & vbCrLf
Next fld
objVBComponent.CodeModule.AddFromString strCode
objVBComponent.Export CurrentProject.Path & "" & objVBComponent.Name
objVBProject.VBComponents.Remove objVBComponent
Open CurrentProject.Path & "" & tdf.Name For Input As #1
strKlasse = Input$(LOF(1), #1)
Close #1
strKlasse = Replace(strKlasse, "Attribute VB_PredeclaredId = False", _
"Attribute VB_PredeclaredId = True")
Open CurrentProject.Path & "" & tdf.Name For Output As #1
Print #1, strKlasse
Close #1
objVBProject.VBComponents.Import CurrentProject.Path & "" & tdf.Name
DoCmd.Save acModule, tdf.Name
End If
Next tdf
Set db = Nothing
End Sub

Probieren Sie es einfach einmal aus!

Dies war die Leseprobe dieses Artikels.
Melden Sie sich an, um auf den vollständigen Artikel zuzugreifen.

Bitte geben Sie die Zeichenfolge in das nachfolgende Textfeld ein

Die mit einem * markierten Felder sind Pflichtfelder.

  • das ist Ober-Klasse!

    als reiner VBA´ler ist es nicht leicht, darauf zu kommen....

  • Das ist echt innovativ

    Hallo Herr Minhorst,

    das nenne ich mal wirklich innovativ. Wirklich eine sehr schöne Lösung.

    Danke !!!

  • Sehr hilfreich

    Hallo Herr Minhorst ,
    so weit so gut aber eine Erweiterung schlage ich vor:
    Wenn reservierte Begriffe als Feldnamen genutz werden führt dies zu Laufzeitfehlern.
    Beispiel: Feldname FUNCTION oder TYPE:
    ....
    Dim xProperty
    ....
    For Each fld In tdf.Fields
    'Reservierte Begriffe abwandeln, führt zu Laufzeitfehlern
    If fld.Name = "FUNCTION" Or fld.Name = "TYPE" Then
    xProperty = fld.Name & "x"
    Else
    xProperty = fld.Name
    End If
    strCode = strCode & "Public Property Get " & xProperty & "()" & vbCrLf
    strCode = strCode & " " & xProperty & " = rst!" & fld.Name & vbCrLf
    strCode = strCode & "End Property" & vbCrLf
    Next fld
    mfg
    Michael Goos

  • Reservierte Begriffe ...

    Ich würde gegebenenfalls eine Fehlermeldung beim Erstellen der Klassen ausgeben, wenn der Entwickler reservierte Begriffe als Feldnamen verwendet. Aber Profis machen so etwas ja sowieso nicht ... ;-)

Passende Artikel
Access im Unternehmen Access im Unternehmen
124,00 € * 159,00 € *
Neues aus unseren Magazinen
Listenfeld: Reihenfolge mehrerer Einträge...

Wir haben bereits in mehreren Beiträgen beschrieben, wie Sie die individuelle Reihenfolge von Elementen einer Tabelle über den Inhalt eines Feldes etwa namens »ReihenfolgeID« einstellen können –... [mehr]

Diagramme mit gefilterten Daten

In Ausgabe 2/2019 haben wir in zwei Artikeln die modernen Diagramme von Access vorgestellt. Im vorliegenen Beitrag zeigen wir Ihnen, wie Sie diese abhängig von den in einem Formular angezeigten... [mehr]

Benutzerverwaltung mit verschlüsselten...

Wenn Sie in einer Access-Anwendung Benutzer verwalten wollen, die sich per Benutzername und Kennwort an die Anwendung anmelden, sollten Sie sehr sensibel mit den in der Anwendung gespeicherten... [mehr]

HTML-Tabellen mit fester Kopfzeile

In den vorherigen Ausgaben von Access im Unternehmen und in der aktuellen Ausgabe arbeiten wir in einigen Beiträgen mit dem Webbrowser-Steuerelement und stellen Daten, die wir mit den Bordmitteln... [mehr]

Flexible HTML-Tabellen mit fester Kopfzeile

Im Beitrag »HTML-Tabellen mit fester Kopfzeile« haben wir gezeigt, wie Sie Daten aus einer bestimmten Abfrage in einem Webbrowser-Steuerelement so anzeigen, dass die Spaltenköpfe oben fixiert... [mehr]

Berechtigungen per HTML verwalten

Im Beitrag »Benutzerverwaltung mit verschlüsselten Kennwörtern« stellen wir eine Lösung vor, in der wir die Berechtigungen von Benutzergruppen an Datenbankobjekten definieren. Dort benötigen wir... [mehr]

Benutzer und Berechtigungen ermitteln

In den Beiträgen »Benutzerverwaltung mit verschlüsselten Kennwörtern« und »Berechtigungen per HTML verwalten« haben wir die Voraussetzungen für eine Benutzerverwaltung geschaffen. Im vorliegenden... [mehr]

Zugriffsrechte mit Datenmakros

Es gibt verschiedene Möglichkeiten, auf Basis des aktuell angemeldeten Benutzers sicherzustellen, dass dieser nur die für ihn vorgesehenen Aktionen mit Daten durchführen darf – beispielsweise durch... [mehr]

Kennwörter generieren

Für den einen oder anderen Zweck möchten Sie vielleicht Kennwörter generieren oder in einer Benutzeroberfläche die Möglichkeit zum Generieren von Kennwörtern anbieten. Wenn Sie etwa Benutzer zu... [mehr]

Neuer Datensatz von Frontend zu Backend

Für manche Themen gibt es keine kurze, prägnante Überschrift. In diesem Fall wollen wir zeigen, wie Sie einen neuen Datensatz anlegen, der in einer temporären Tabelle im Frontend gespeichert wird,... [mehr]