Wenn man mit VBA programmiert, kommt man nicht um das Referenzieren von Formularen und Steuerelementen herum. Dabei gibt es verschiedene Schreibweisen, verschiedene Ziele und auch noch verschiedene Ausgangspunkte. Bei den Schreibweisen scheiden sich die Geister, ob man Punkte oder Ausrufezeichen verwendet und wie detailliert oder kurz es sein soll. Die Ziele sind Formulare, Unterformulare und die darin enthaltenen Steuerelemente mit oder ohne Datenbindung. Und warum gibt es verschiedene Ausgangspunkte? Weil man einerseits vom Klassenmodul eines Formulars selbst auf die enthaltenen Steuerelemente zugreifen möchte, andererseits aber auch von Formularen auf die Elemente anderer Formulare oder auch von Prozeduren aus Standardmodulen. Wie all dies zusammenhängt und wie die Ausdrücke für die verschiedenen Konstellationen formuliert werden müssen, zeigen wir in diesem Beitrag.
Beispieldatenbank
Als Beispiel verwenden wir eine Datenbank, die verschiedene Tabellen zum Speichern von Kunden, Projekten und Mitarbeitern enthält.
Hier nutzen wir zunächst das Formular frmKundenUndProjekte mit dem Unterformular sfmKundenUndProjekte, wobei das Unterformular-Steuerelement zur besseren Unterscheidung sfm genannt wurde (siehe Bild 1).
Bild 1: Formular zum Anzeigen von Kunden und Projekten
Verschiedene Ausgangspunkte
Je nachdem, von wo aus wir auf Formulare, Unterformulare und Steuerelemente zugreifen wollen, können wir unterschiedliche Ausdrücke zum Referenzieren des jeweiligen Elements verwenden.
Dabei gibt es allgemeine Ausdrücke, die wir grundsätzlich von überall aus nutzen können, aber auch solche, die nur von speziellen Orten aus funktionieren.
Wenn wir also beispielsweise ein Steuerelement in einem Formular von einem Standardmodul aus oder von einem Klassenmodul eines anderen Moduls aus referenzieren wollen, werden wir üblicherweise die Forms-Auflistung nutzen, darüber den Formularnamen angeben und schließlich über die Steuerelemente-Auflistung das Steuerelement referenzieren.
Einen solchen Ausdruck können wir beispielsweise auch vom Direktbereich aus nutzen. Dieser sieht wie folgt aus:
Debug.Print Forms.Item("frmKundenUndProjekte"). Controls.Item("Vorname").Value
Wir nutzen also zunächst die Forms-Auflistung, greifen über die Items-Auflistung auf das Formular frmKundenUndProjekte zu und über dessen Controls-Auflistung auf das Steuerelement namens Vorname.
Diesen Ausdruck können wir grundsätzlich von überall aus verwenden, denn er ist absolut und nicht relativ. Einen relativen Ausdruck könnten wir innerhalb des Klassenmoduls des Formulars platzieren.
Innerhalb des Klassenmoduls können wir auf das Formular mit dem Schlüsselwort Me zugreifen. Dabei ersetzen wir den Bezug zum Formular, nämlich Forms.Item(„frmKundenUndProjekte“) durch Me. Der Rest bleibt gleich:
Debug.Print Me.Controls.Item("Vorname").Value
Ähnlich verhält es sich, wenn wir auf ein Element im Unterformular zugreifen wollen. Um die Bezeichnung des aktuell markierten Projekts zu ermitteln, können wir von überall aus diesen Ausdruck verwenden:
Debug.Print Forms.Item("frmKundenUndProjekte"). Controls("sfm").Form.Controls("Projektbezeichnung")
Vom Hauptformular aus kürzen wir den Ausdruck wie zuvor ab:
Debug.Print Me.Controls("sfm").Form.Controls ("Projektbezeichnung").Value
Vom Unterformular aus hingegen reicht dieser Ausdruck:
Debug.Print Me.Controls("Projektbezeichnung").Value
Lang und kurz
In den ersten Beispielen haben wir die jeweils längste Form des benötigten Ausdrucks verwendet. Es gäbe nur noch eine Steigerung, denn die Forms-Auflistung ist eigentlich eine Eigenschaft der Application-Klasse von Access. Das können wir wie folgt im Direktbereich herausfinden:
Debug.Print TypeName(Forms.Parent)
Application
Es ist aber erst dann zwingend notwendig, einen Verweis auf das Application-Objekt von Access anzugeben, wenn wir von einer anderen Anwendung aus eine Access-Instanz fernsteuern wollen.
Wir schauen uns nun die möglichen Kurzformen an und beginnen mit dem ersten Ausdruck, diesmal ohne Debug.Print:
Forms.Item("frmKundenUndProjekte"). Controls.Item("Vorname").Value
Hier können wir als Erstes auf die Value-Eigenschaft verzichten. Value ist nämlich die Standardeigenschaft des Textbox-Steuerelements und vieler weiterer Steuerelemente. Das heißt, wenn wir keine Eigenschaft angeben, wird in der Regel Value als Eigenschaft verwendet.
Die Standardeigenschaft einer Klasse
Warum „in der Regel“? Weil es auch hier eine Ausnahme gibt. Wenn wir nämlich wie folgt eine Objektvariable des Typs TextBox definieren und das Textfeld zuweisen, wird logischerweise nicht der Inhalt der Standardeigenschaft zugewiesen, sondern ein Verweis auf das TextBox-Element.
Wenn wir jedoch eine Operation durchführen wie Debug.Print oder den Wert einer String-Variablen zuweisen, dann liefert der Ausdruck den Wert der Standardeigenschaft.
Woher wissen wir das? Dazu öffnen wir den Objektkatalog des VBA-Editors, suchen nach TextBox, wählen das Ergebnis aus und suchen unter Elemente von “TextBox“ nach einem Element mit einem abweichenden Icon. Dieses sieht wie in Bild 2 aus.
Bild 2: Standardeigenschaft einer Klasse im Objektkatalog
Damit landen wir bei der ersten Verkürzung, denn den Wert unseres Textfeldes können wir nun auch wie folgt ausgeben:
Forms.Item("frmKundenUndProjekte"). Controls.Item("Vorname")
Item ist ebenfalls eine Standardeigenschaft
Und auf der gleichen Basis können wir noch zwei weitere Verkürzungen vornehmen. Genau wie Value für das TextBox-Element ist Item die Standardeigenschaft sowohl der Forms– als auch der Controls-Auflistung. Auch davon kann man sich im Objektkatalog überzeugen.
Damit landen wir direkt bei einer sehr viel kürzeren Version des vorherigen Ausdrucks:
Debug.Print Forms("frmKundenUndProjekte"). Controls("Vorname")
Dieser Ausdruck ist bereits wesentlich übersichtlicher als die erste und die zweite Variante.
Doch es geht noch weiter. Mit Forms(„frmKundenUndProjekte“) referenzieren wir nämlich ein Formular, also ein Element des Typs Form.
Und welche Standardeigenschaft hat das Form-Element? Richtig: die Controls-Auflistung!
Damit können wir auch diese weglassen und erhalten diesen noch kürzeren Ausdruck:
Debug.Print Forms("frmKundenUndProjekte")("Vorname")
Und es geht noch weiter, denn es gibt sogar noch eine Abkürzung für das Referenzieren des gewünschten Auflistungselements. Dabei lassen wir die öffnende und schließenden Klammern und Anführungszeichen weg. Stattdessen stellen wir ein Ausrufezeichen voran. Das machen wir der Übersicht halber in zwei Schritten, als Erstes für das Steuerelement:
Debug.Print Forms("frmKundenUndProjekte")!Vorname
Und schließlich können wir die gleiche Vereinfachung auch noch für das gewünschte Element der Forms-Auflistung erledigen:
Debug.Print Forms!frmKundenUndProjekte!Vorname
Damit wäre alles gut und schön. Wir hätten mit dem Ausrufezeichen ein eindeutiges Zeichen, um eine Abkürzung für Auflistungen zu schreiben. Allerdings können wir auch die folgenden Notationen nutzen, um den Inhalt des Feldes Vorname auszugeben:
Debug.Print Forms.frmKundenUndProjekte.Vorname
Allerdings funktioniert die Eingabe der Punkte nicht für IntelliSense. Der Grund ist ganz einfach: Die Forms-Auflistung weiß während des Entwurfs nicht, welche Elemente sie beinhaltet. Und auch die Controls-Auflistung weiß das nicht. Wie aber können wir dennoch unter bestimmten Umständen per IntelliSense auf Steuerelemente zugreifen? Das schauen wir uns im folgenden Abschnitt an.
Klassen als Eigenschaften
Wie erwähnt, ist es manchmal möglich, über den Punkt per IntelliSense auf Steuerelemente zuzugreifen.
Das gelingt allerdings nur in den folgenden beiden Situationen. Die erste dürfte die weitaus bekanntere sein. Hier verwenden wir das Me-Schlüsselwort gefolgt von dem Punkt. Dies liefert wie in Bild 3 beispielsweise die Steuerelemente KundeID und Land.
Bild 3: Steuerelemente als Eigenschaften
Lassen wir uns hier den Vornamen ausgeben, um beim bisherigen Beispiel zu bleiben, verwenden wir also den folgenden Ausdruck:
Debug.Print Me.Vorname
Auch hier funktioniert übrigens die Notation mit dem Ausrufezeichen:
Debug.Print Me!Vorname
Diese Ausdrücke sind übrigens wieder identisch mit den folgenden:
Debug.Print Me.Vorname.Value Debug.Print Me.Controls("Vorname").Value
Zugriff auf Steuerelemente als Eigenschaften von außerhalb des Klassenmoduls
Bevor wir zu den Unterschieden zwischen der Punkt- und der Ausrufezeichen-Notation kommen, schauen wir uns noch den zweiten und weniger häufigen Fall an, bei dem wir über IntelliSense auf die Steuerelemente zugreifen.
Diese Methode unterscheidet sich vor allem dadurch, dass wir nicht nur vom Klassenmodul des jeweiligen Formulars per IntelliSense auf die Steuerelemente zugreifen können, sondern auch vom Direktbereich, von Standardmodulen oder von anderen Klassenmodulen aus.
Dazu müssen wir den Namen des Klassenmoduls des jeweiligen Formulars kennen. Dieser setzt sich aus der Zeichenkette Form_ und dem Formularnamen zusammen. Im Falle des Formulars frmKundenUndProjekte heißt das Klassenmodul folglich Form_frmKundenUndProjekte.
Damit können wir sogar vom Direktbereich per IntelliSense auf die Steuerelemente zugreifen und beispielsweise ihren Inhalt ausgeben:
In Bild 4 sehen wir ein Beispiel dafür. Warum werden die Steuerelemente aber überhaupt per IntelliSense angezeigt?
Bild 4: Zugriff per IntelliSense auf ein Steuerelement
Der Grund ist: In dem Moment, wo wir einem Formular ein Steuerelement hinzufügen, wird die Formularklasse um dieses Element erweitert. Und damit erhält die Formularklasse eine Eigenschaft, die den gleichen Namen wie das Steuerelement trägt.
Das lässt sich einfach reproduzieren, indem man beispielsweise ein neues Steuerelement namens cmdTest hinzufügt. Sobald man über die Eingabe von Me oder Form_frmKundenUndProjekte plus Eingabe des Punktes IntelliSense aktiviert, wird das neue Steuerelement dort aufgelistet.
Einsatz von Me vs. Name des Formularklassenmoduls
Der Einsatz des Schlüsselwortes Me wirkt sich immer nur auf das Klassenmodul aus, in dem es verwendet wird. Das ist logisch – wo sollte man es nutzen können? Letztlich ist es eine Abkürzung für Form_ plus Formularname. Wir können auch innerhalb des Klassenmoduls eines Formulars zum Beispiel Form_frmKundenUndProjekte nutzen. Das hat allerdings folgenden Nachteil: Wenn man einmal den Namen des Formulars ändert, läuft diese Referenz ins Leere und löst einen Fehler aus.
Außerhalb von Formularklassenmodulen können wir Me logischerweise nicht einsetzen. Wenn wir hier per IntelliSense auf die Steuerelemente des Formulars zugreifen wollen, müssen wir also beispielsweise Form_frmKundenUndProjekte referenzieren und können dann nach Eingabe des Punktes IntelliSense nutzen.
Es gibt allerdings einen entscheidenden Unterschied: Sowohl Me als auch Form_frmKundenUndProjekte greifen auf eine Instanz der Klasse Form_frmKundenUndProjekte zu. Dafür muss aber auch eine solche Klasse vorhanden sein! Davon können wir bei Verwendung von Me ausgehen, denn Me kann nur vom Klassenmodul selbst auf die Eigenschaften zugreifen. Wenn wir jedoch über Form_frmKundenUndProjekte von außerhalb des Klassenmoduls auf seine Elemente zugreifen wollen, und es ist noch keine Instanz vorhanden, dann wird nun eine neue Instanz erstellt beziehungsweise das Formular geöffnet.
Forms!frmKundenUndProjekte vs. Form_frmKundenUndProjekte
Damit haben wir nun zwei Methoden, um von außerhalb eines Formulars auf ein Formular zuzugreifen. Die erste verwendet die Forms-Auflistung und kann so aussehen, wobei wir hier den Namen des Formulars mit der Name-Eigenschaft ausgeben:
Debug.Print Forms!frmKundenUndProjekte.Name Debug.Print Forms("frmKundenUndProjekte").Name
Mit Form_ sieht der Zugriff wie folgt aus:
Debug.Print Form_frmKundenUndProjekte.Name
Hier sehen wir einen weiteren Unterschied:
- Wenn wir über die Forms-Auflistung auf ein Formular zugreifen und es ist nicht geöffnet, erhalten wir einen Fehler.
- Wenn wir über die Form_-Notation darauf zugreifen und es ist nicht geöffnet, wird die Instanz automatisch erstellt.
Beides kann zu unterwarteten Ergebnissen führen. Deshalb sollten wir vorher prüfen, ob das jeweilige Formular bereits geöffnet ist und erst dann auf die Inhalte zugreifen. Anderenfalls erhalten wir im besten Fall einen Fehler, wenn das Formular noch nicht geöffnet ist und wir über die Auflistung darauf zugreifen wollen.
Im schlechtesten Fall greifen wir über den Namen des Klassenmoduls des Formulars darauf zu und erstellen das Formular neu, obwohl dieses eigentlich gerade gar nicht geöffnet sein sollte.
Wie wir herausfinden, ob ein Formular gerade geöffnet ist, schauen wir uns im Beitrag Prüfen, ob ein Formular geöffnet ist (www.access-im-unternehmen.de/****) an.
Kurz gefasst: Bevor wir über den Namen des Klassenmoduls eines Formulars auf dieses zugreifen wollen, könnten wir in einer einfachen If…Then-Bedingung und einer Hilfsfunktion prüfen, ob dieses überhaupt geöffnet ist:
If IstFormularGeoeffnet("frmKundenUndProjekte") = True Then Debug.Print Form_frmKundenUndProjekte.Name Else Debug.Print "Formular nicht geöffnet" End If
Nur, wenn das Formulare geöffnet ist, sollten wir auch mit dem Klassennamen darauf zugreifen, weil wir es sonst neu erstellen.
Interessanterweise liefern der Zugriff über die Auflistung Forms als auch der über den Klassennamen beide ein Element des Typs der Formularklasse zurück – hier zunächst für die Forms-Auflistung:
TypeName(Forms!frmKundenUndProjekte) Form_frmKundenUndProjekte
Wenn wir direkt auf das Klassenmodul zugreifen, erhalten wir das gleich Ergebnis:
TypeName(Form_frmKundenUndProjekte) Form_frmKundenUndProjekte
IntelliSense für Steuerelemente bei Unterformularen
Wenn wir die Auflistungen verwenden wollen, können wir wie folgt auf die Steuerelemente eines Unterformulars zugreifen – hier in der vereinfachten Form mit Ausrufezeichen:
Debug.Print Forms!frmKundenUndProjekte!sfm.Form! Projektbezeichnung
Hier ist zu sehen, dass wir diesmal das Unterformular-Steuerelement namens sfm als Steuerelement der Steuerelementliste des Hauptformulars nutzen. Um das darin enthaltene Formular zu referenzieren, verwenden wir die Form-Eigenschaft. Dies liefert zwar einen Verweis auf das Klassenmodul, aber dennoch können wir nicht mit IntelliSense auf die enthaltenen Steuerelemente zugreifen.
Stattdessen müssen wir diese wieder erst nachschauen oder aus dem Gedächtnis eingeben.
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