Zum Inhalt dieser Seite

Access-Grundlagen #6 - VBA

6.1 Anlegen von Ereignisprozeduren 6.2 Ermittlung von Konstantenwerten
6.3 Ermittlung von Eigenschaftsnamen 6.4 Verweisprobleme
6.5 Fehler beheben 6.6 Ersatz für Domänenaggregat-Funktionen

Nach oben 6.1 Anlegen von Ereignisprozeduren

Um einem Formular,Textfeld, Bereich (Seitenkopf etc.) o.ä eine Ereignisprozedur zuzuweisen, muss man folgendermassen vorgehen (Beispiel anhand eines Textfeldes in einem Formular).

1. Im Formularentwurf wählt man das Textfeld aus und geht auf Ansicht/Eigenschaften ( ALT + ENTER ). Anschliessend öffnet sich ein Dialog, in dem man dann das Register "Ereignis" (s. oberste Markierung) auswählt. Jetzt bewegt man sich zu dem gewünschten Ereignis und klickt auf die rechts erscheinenden 3 Punkte (s. rote Markierung). In dem Beispiel wird dem Textfeld "KundenVorname" eine Prozedur bei dem Ereignis "Nach Aktualisierung" zugeordnet.

Textfeldeigenschaften - Ereignis - Nach Aktualisierung

2. Nach Anklicken der 3 Punkte wählt man "Code-Generator" aus und bestätigt mit "OK".

Auswahl nach Anklicken der 3 Punkte

3. Danach sollte sich der VBA-Editor öffnen, wo man dann endlich den vorgeschlagenen Code eintragen kann. Dabei müssten die Zeilen Private Sub TextFeldName_Ereignis() und End Sub automatisch angelegt worden sein.

Gerüst der Prozedur

Einzelansicht

Nach oben 6.2 Ermittlung von Konstantenwerten

Wie bereits unter 2.3 erwähnt, gibt es eine schnelle Möglichkeit, den Wert einer VBA-Konstanten o.ä. ohne Nachschlagen in der Online-Hilfe zu ermitteln.

Den VBA-Editor öffnen (z.B. durch Entwurfsansicht eines Standardmoduls). Dort auf Ansicht/Direktfenster ( STRG + G ) gehen und danach in dem Direktfenster ?NameDerKonstanten eingeben. Die Zeile mit ENTER bestätigen. Anschliessend erhält man den Wert. In dem Beispiel wurde der Wert der Konstanten "vbProperCase" abgefragt.

Beispiel der Eingabe im Direktfenster

Einzelansicht

Nach oben 6.3 Ermittlung von Eigenschaftsnamen

Entgegen der landläufigen Meinung besitzt Access eine gute und bisweilen tatsächlich kontextsensitive Online-Hilfe. Wenn man per VBA eine Objekteigenschaft ändern möchte und den Eigenschaftsnamen, den man unter VBA verwenden müsste, nicht kennt, dann hilft es meistens, im (Formular- bzw. Berichts-) Entwurf diese Eigenschaft erst einmal anzuwählen. Anschliessend drückt man F1 und erhält Hilfe zu der Eigenschaft. Ganz oben sollte auch der (englische) Name dieser Eigenschaft angezeigt werden. Meistens gelangt man aber so nur in die Access-Hilfe, in der nichts näheres zur VBA-Syntax steht.

Wenn man anschliessend im VBA-Editor (z.B. aus dem Entwurf eines Moduls heraus) die VBA-Hilfe aufruft und dort nach der vorher gefundenen Bezeichnung sucht, sollte man die möglichen Einstellungen etc. finden.

Einzelansicht

Nach oben 6.4 Verweisprobleme

Nun steht die Lösung dieser Probleme eigentlich schon unter FAQ 7.1 (s. dort auch für weitere Hinweise):

Zitat

Irgendein Modul öffnen, Menü: Extras/Verweise wählen.

aber es wird immer noch übersehen, dass man nur im VBA-Editor über den Menüpunkt Extras die Verweise setzen kann ( ALT + X , danach V ). Das folgende Bild sollte endlich Abhilfe schaffen:

VBA-Editor-Extras/Verweise

Wenn das Setzen der Verweise nicht möglich ist, dann befindet man sich höchstwahrscheinlich im Debug-Modus, der zunächst beendet werden muss (d.h. einfach das Fenster mit der Code-Ansicht wieder schliessen). Der Debug-Modus wird gestartet, wenn die Prozedur auf einen unbehandelten Laufzeitfehler trifft und man beim daraufhin erscheinenden Meldefenster auf "Debuggen" klickt.

Einzelansicht

Nach oben 6.5 Fehler beheben

Sehr viele Fehler können durch Beachtung der folgenden Punkte vermieden bzw. behoben werden.

Nach oben 6.5.1 Code kompilieren

Nach jeder Code-Änderung sollte vor der nächsten Ausführung der Code kompiliert werden, um evtl. versteckte Fehler ausfindig zu machen. Dazu muss man im VBA-Editor Debuggen/Kompilieren von Datenbankname ( ALT + G , anschliessend K ) aufrufen.

Nach oben 6.5.2 Explizites Deklarieren von Variablen durch Option Explicit

In jedem Deklarationsteil eines Moduls sollte als 2. Zeile die Anweisung Option Explicit zu finden sein (s. auch Screenshot unter 6.4 ). Dadurch wird man gezwungen, die verwendeten Variablen vorher zu deklarieren und erspart sich so manche Fehlersuche wg. Buchstabendrehern in Variablennamen. Man kann diese Zeile in jedem neuen Modul erzwingen, indem man im VBA-Editor Extras/Optionen ( ALT + X , dann O ) aufruft und dort im Register Editor einen Haken bei Variablendeklaration erforderlich setzt. Bereits bestehende Module müssen manuell ergänzt werden.

Nach oben 6.5.3 Richtiges Deklarieren der Variablen

Werden Variablen innerhalb einer Zeile deklariert, so muss der Typ hinter jede Variable gesetzt werden. Das folgende Beispiel ist falsch , da so nur die letzte Variable als String und die ersten zwei Variablen als Variant deklariert werden:

Dim strVariable1, strVariable2, strVariable3 As String

So ist es richtig :

Dim strVariable1 As String, strVariable2 As String, strVariable3 As String

Zudem sollte bei möglichen Überschneidungen von Variablentypen zwischen unterschiedlichen Verweisen explizit die betreffende Bibliothek davor gesetzt werden. Der letztgenannte Punkt trifft vor allem ab Access 2000 und dort speziell bei der Deklaration von Recordsets zu, da der Typ Recordset in ADO und DAO vorhanden ist (s. auch FAQ 7.11 ):

Dim rs As DAO.Recordset
'ist besser als nur:
'Dim rs As Recordset
 

Nach oben 6.5.4 Verwenden von Debug.Print

Immer wieder kommt man in die Verlegenheit, Datenherkünfte oder dgl. dynamisch zu erstellen. Wenn es nicht wie erwartet funktioniert, ist es ratsam, den SQL-String oder andere Werte zur Kontrolle mit Debug.Print auszugeben. Danach taucht der zugewiesene Wert im Direktfenster auf und lässt sich somit besser als mit einer Msgbox oder mittels Haltepunkt kontrollieren. Man muss also folgendermassen vorgehen: Bei der Code-Erstellung lässt man sich im VBA-Editor das Direktfenster über Ansicht/Direktfenster anzeigen (oder durch den Tastenschlüssel STRG + G ). Der zuzuweisende String (oder was auch immer) wird in einer Variablen gespeichert und vor der Zuweisung zur Eigenschaft mittels Debug.Print ausgegeben. Startet man nun die Prozedur und es treten Fehler auf, wechselt man in den VBA-Editor und schaut sich das Ergebnis im Direktfenster an. Speziell SQL-Anweisungen lassen sich durch Kopieren des im Direktfenster erhaltenen Strings und Einfügen in die SQL-Ansicht einer neuen Abfrage sehr gut kontrollieren. Weiteres zum Direktfenster findet man auch auf DBWiki . Hier nun endlich ein Beispiel:

Private Sub lstZeitraeume_AfterUpdate()
 
'Dieser Beispiel-Code wertet den Wert eines
'Listenfeldes aus und ändert die Datenherkunft
'eines Formulares zur Laufzeit.
 
Dim strSQL As String   'nimmt den SQL-String der neuen Datenherkunft auf
 
strSQL = "SELECT TagDerVerbindung, PrK_IDdesTages, FrK_AbrechnungsID" _
         & " FROM tblTage WHERE FrK_Abrechnungsid=" & Me!lstZeitraeume _
         & " ORDER BY TagDerVerbindung;"
 
'Ausgabe der zuzuweisenden Datenherkunft im Direktfenster.
'Wenn alles funktioniert, sollte die Zeile wieder entfernt
'werden:
Debug.Print strSQL
 
'Zuweisen der neuen Datenherkunft:
Me.Recordsource = strSQL
 
End Sub
 

Nach oben 6.5.5 Einzelschrittmodus und Haltepunkte

Um den Ablauf in einer Prozedur zu verfolgen, kann man Haltepunkte festlegen. Dazu geht man im VBA-Editor in die gewünschte Zeile, in der das möglich ist (im Deklarationsteil z.B. ist diese Option nicht gegeben) und legt über Debuggen/Haltepunkt ein/aus den Überwachungspunkt an. Schneller geht es sicherlich mit dem Hotkey F9 oder mit einem Klick auf den grauen Bereich neben dem VBA-Code. Wenn die Routine nun gestartet wird, wartet sie dort so lange, bis sie mittels F8 (oder Debuggen/Einzelschritt ) fortgesetzt wird.

Nach oben 6.5.6 Abfangen von Fehlern

Speziell bei Anwendungen, die in einer Runtime-Umgebung laufen sollen, müssen z.T. unvermeidliche Laufzeitfehler abgefangen werden. Dazu kann man sich einfach mal einen durch den Schaltflächen-Assistenten generierten Code anschauen und diesen ggf. anpassen. Wenn beispielsweise ein leerer Bericht nach der Variante #2 unter FAQ 5.8 nicht gedruckt werden soll, müsste der Code ungefähr so ausschauen (bei anderen Anweisungen muss die Fehlernummer sicherlich angepasst werden - 2501 gilt nur für diesen speziellen Fall):

Private Sub repBerichtsNameDrucken_Click()
 
On Error GoTo Err_repBerichtsNameDrucken_Click
 
DoCmd.OpenReport "Berichtsname"
 
Exit_repBerichtsNameDrucken_Click:
   Exit Sub
 
Err_repBerichtsNameDrucken_Click:
   If Err.Number <> 2501 Then
     MsgBox Err.Description, vbCritical, "Fehler# " & Err.Number
   End If
   Resume Exit_repBerichtsNameDrucken_Click
 
End Sub
 

Nach oben 6.5.7 Code-Gestaltung und -Tools

Der Code sollte so gestaltet oder eingerückt werden, dass daraus schon der Ablauf einigermassen ersichtlich wird. Dies erleichtert das Auffinden von fehlenden abschliessenden Zeilen wie beispielsweise End If bei einer IF-Klausel oder Next bei einer For-Next-Schleife. Erweiterungen des Codes sind dann auch nicht mehr so schwierig. Für das Einrücken bietet sich der Smart Indenter an. Ein weiteres praktisches Add-In sind die MZ-Tools , die eine Bibliothek für immer wiederkehrende Code-Fragmente enthalten. Zeilen können nummeriert, Prozedurköpfe in Kommentarform angelegt werden usw. Unbedingt anschauen!

Einzelansicht

Nach oben 6.6 Ersatz für Domänenaggregat-Funktionen

Die sehr einfach einzusetzenden Domänenaggregat-Funktionen wie z.B. DLookup() , DCount() , DSum() o.ä. haben den entscheidenden Nachteil, dass Indizes eher ungünstig eingesetzt werden. D.h., dass die Wertermittlung bei umfangreichen Tabellen länger als nötig dauern kann. Die 3 folgenden beispielhaften Ersatzfunktionen ( DAvg() usw. könnten ähnlich umgesetzt werden) werden in derselben Art eingesetzt und nutzen Indizes, sofern diese in der Tabelle vorhanden sind und Kriterien benötigt werden. Außerdem werden im Fehlerfall etwas genauere Ausgaben generiert (statt z.B. nur #Fehler im Steuerelementinhalt):

Public Function TLookup(Expression As String, Domain As String, _
                Optional Criteria) As Variant
 
'Ersatz für DLookup()
 
On Error GoTo TLookup_Err
 
Dim strSQL As String
strSQL = "SELECT " & Expression & " FROM " & Domain
If Not IsMissing(Criteria) Then strSQL = strSQL & " WHERE " & Criteria
TLookup = DBEngine(0)(0).OpenRecordset(strSQL, 8)(0)
Exit Function
 
TLookup_Err:
  Select Case Err.Number
    Case 3021  'kein Datensatz gefunden
      TLookup = Null
    Case 3061  'einer der Feldnamen (Ausdruck oder Kriterium) stimmt nicht
      TLookup = "#Ausdruck/Kriterium"
    Case 3078  'Name der Tabelle oder Abfrage stimmt nicht
      TLookup = "#Domäne"
    Case 3464  'Datentyp im Kriterium ist falsch
      TLookup = "#Kriterium"
    Case Else  'Sonstige Fehler
      TLookup = "#Fehler"
  End Select
 
End Function
Public Function TCount(Expression As String, Domain As String, _
                       Optional Criteria) As Variant
 
'Ersatz für DCount()
 
On Error GoTo TCount_Err
 
Dim strSQL As String
strSQL = "SELECT COUNT(" & Expression & ") FROM " & Domain
If Not IsMissing(Criteria) Then strSQL = strSQL & " WHERE " & Criteria
TCount = DBEngine(0)(0).OpenRecordset(strSQL, 8)(0)
Exit Function
 
TCount_Err:
  Select Case Err.Number
    Case 3061  'einer der Feldnamen (Ausdruck oder Kriterium) stimmt nicht
      TCount = "#Ausdruck/Kriterium"
    Case 3078  'Name der Tabelle oder Abfrage stimmt nicht
      TCount = "#Domäne"
    Case 3464  'Datentyp im Kriterium ist falsch
      TCount = "#Kriterium"
    Case Else  'Sonstige Fehler
      TCount = "#Fehler"
  End Select
 
End Function
Public Function TSum(Expression As String, Domain As String, _
                Optional Criteria) As Variant
 
'Ersatz für DSum()
 
On Error GoTo TSum_Err
 
Dim strSQL As String
strSQL = "SELECT SUM(" & Expression & ") FROM " & Domain
If Not IsMissing(Criteria) Then strSQL = strSQL & " WHERE " & Criteria
TSum = DBEngine(0)(0).OpenRecordset(strSQL, 8)(0)
Exit Function
 
TSum_Err:
  Select Case Err.Number
    Case 3061  'einer der Feldnamen (Ausdruck oder Kriterium) stimmt nicht
      TSum = "#Ausdruck/Kriterium"
    Case 3075  'Summierender Ausdruck ist falsch (z.B. "*" ist n. mgl.)
      TSum = "#Ausdruck"
    Case 3078  'Name der Tabelle oder Abfrage stimmt nicht
      TSum = "#Domäne"
    Case 3464  'Datentyp im Kriterium ist falsch
      TSum = "#Kriterium"
    Case Else  'Sonstige Fehler
      TSum = "#Fehler"
  End Select
 
End Function
Einzelansicht