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/1537) 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.
Wenn wir auch für das Unterformular IntelliSense für den Zugriff auf die enthaltenen Steuerelemente nutzen wollen, müssen wir dieses zuerst mit einer Variablen referenzieren, die den Typ der Formularklasse hat:
Dim objSfmKundenUndProjekte As Form_sfmKundenUndProjekte
Dann setzen wir einen Verweis auf das Form-Element in dem in der Steuerelement-Auflistung enthaltene Unterformular-Steuerelement:
Set objSfmKundenUndProjekte = Form_frmKundenUndProjekte.sfm.Form
Danach können wir auch auf die Steuerelemente im Unterformular per IntelliSense zugreifen (siehe Bild 5).
Bild 5: IntelliSense für Unterformulare
Steuerelement oder Feld der Datensatzquelle?
Bisher haben wir die ganze Zeit mit gebundenen Steuerelementen gearbeitet, die genauso heißen, wie die Felder, an die sie gebunden sind. Das ist der Standard, wenn man die Felder nach dem Festlegen der Datensatzquelle aus der Feldliste in das Formular zieht. Access vergibt automatisch die Feldnamen als Namen für die Steuerelemente.
Aus unserer Sicht ist es wesentlich einfacher, Steuerelemente so umzubenennen, dass sie zwar noch den Namen des gebundenen Feldes enthalten, aber ein Präfix aufweisen, das dem Typ des Steuerelements entspricht. Die wichtigsten sind dabei:
- txt: Textfeld
- cbo: Kombinationsfeld
- chk: Kontrollkästchen
- lst: Listenfeld
Sie können natürlich auch andere Präfixe verwenden – wichtig ist, dass sie konsistent sind. Der Grund ist, dass bei Verwendung von IntelliSense natürlich auch alle Textfelder, Kombinationsfelder, Kontrollkästchen, Listenfelder und so weiter immer an der gleichen Stelle gefunden werden sollen.
Aber können wir eigentlich gezielt entweder auf das Steuerelement oder das gebundene Feld zugreifen, wenn beide den gleichen Namen haben?
Wenn wir beispielsweise den Typ des Textfeldes Vorname ausgeben, erhalten wir folgendes Ergebnis:
Debug.Print TypeName(Forms!frmKundenUndProjekte!Vorname) TextBox
Aber können wir überhaupt auf das Feld selbst zugreifen, statt auf das daran gebundene Steuerelement? Und welchen Objekttyp hat ein solches Feld in einem Formular eigentlich? Das finden wir heraus, indem wir das Steuerelement Vorname einmal in txtVorname umbenennen. Danach erhalten wir beim Zugriff auf das Element Vorname den folgenden Datentyp:
Debug.Print TypeName(Forms!frmkundenundprojekte!Vorname)
AccessField
Hier erhalten wir beispielsweise den Datentyp AccessField. Was ist nun genau der Unterschied zwischen den beiden? Dieser ist riesig: Während die Steuerelementtypen TextBox, ComboBox, CheckBox, ListBox et cetera einige Eigenschaften aufweisen, mit denen sich alles programmieren lässt, was wir benötigen, weist der Datentyp AccessField lediglich eine einzige Eigenschaft auf!
Schauen wir uns das einmal im Objektkatalog an, finden wir diesen Datentyp über die Suche zunächst einmal gar nicht (siehe Bild 6).
Bild 6: Der Datentyp AccessField existiert anscheinend gar nicht.
Der Grund ist, dass es sich hier um einen verborgenen Objekttyp handelt. Diesen decken wir auf, indem wir im Objektkatalog mit der rechten Maustaste das Kontextmenü öffnen und dort den Befehl Verborgene Elemente anzeigen betätigen. Danach finden wir dort den Eintrag AccessField. Markieren wir diesen und schauen uns die Liste der Elemente an, finden wir lediglich den Eintrag Value (siehe Bild 7).
Bild 7: Der Datentyp taucht erst auf, wenn wir die verborgenen Elemente einblenden.
Warum müssen wir das überhaupt wissen? Das ist zum Beispiel dann wichtig, wenn Sie nicht gleich zu Beginn der Entwicklung Wert darauf legen, Präfixe für die Steuerelemente festzulegen und dies später nachholen wollen.
Wenn man nämlich bis dahin zum Beispiel die folgende Zeile verwendet, um den Standardwert des Steuerelements namens Vorname zu ermitteln, gelingt dies zunächst problemlos:
Debug.Print Forms!frmKundenUndProjekte!Vorname.DefaultValue
Wenn wir nun den Namen des Steuerelements von Vorname zu txtVorname ändern und diese Zeile erneut aufrufen, erhalten wir einen Fehler:
Debug.Print Forms!frmKundenUndProjekte!Vorname.DefaultValue
Die entsprechende Fehlermeldung ist in Bild 8 abgebildet.
Bild 8: Fehler beim Versuch, nicht vorhandene Eigenschaften eines AccessField-Elements zu lesen
Wozu eigentlich die ausführliche Form?
Weiter oben sind wir mit der folgenden langen Form zum Referenzieren eines Steuerelements gestartet:
Debug.Print Forms.Item("frmKundenUndProjekte").Controls.Item("Vorname").Value
Nachdem wir gesehen haben, dass wir es auch so kurz schreiben können:
Debug.Print Forms!frmKundenUndProjekte!Vorname
Oder dass wir auch direkt über Form_frmKundenUndProjekte sogar mit IntelliSense auf die Steuerelemente zugreifen können.
Wieso sollten wir dann überhaupt die Variante mit der Auflistung mit Klammern und Anführungszeichen verwenden?
Der erste Grund ist, dass wir vielleicht nicht den Namen des Formulars oder Steuerelements angeben wollen, sondern diesen in einer Variablen gespeichert haben.
Wenn wir beispielsweise eine Variable wie die folgende deklarieren:
Dim strFormularname As String
Und dann den Namen zuweisen:
strFormularname = "frmKundenUndProjekte"
Dann können wir diese Variable in der Anweisung zum Referenzieren des Formulars einbauen:
Debug.Print Forms(strFormularname)!Vorname
Warum sollte man das tun? Zum Beispiel, weil man in mehreren Anweisungen hintereinander auf das Formular zugreift und bei einer Änderung des Formularnamens die Änderung nur an einer Stelle in der Prozedur durchführen möchte.
Es gibt noch andere Anwendungsfälle, die wir nur über die Auflistungen lösen können. Wenn wir beispielsweise viele gleichartige Steuerelemente in einem Formular platzieren, beispielsweise um einen Kalender abzubilden, dann werden wir diese vermutlich mit Namen nach einem bestimmten Schema mit numerischen Anteilen versehen, zum Beispiel txt001, txt002, txt003 und so weiter.
Wenn wir diese nun durchlaufen wollen, um ihre Eigenschaften entsprechend des darzustellenden Kalenders anzupassen, können wir die Controls-Auflistung nutzen und dabei dynamisch zusammengesetzte Bezeichnungen nutzen:
Dim lngControl As Long For lngControl = 1 To 42 Me.Controls("txt" & Format(lngControl, "000")).Value = lngControl Next lngControl
Was nun?
Nach den vielen verschiedenen Beispielen für den Zugriff auf Formulare und die enthaltenen Unterformulare und Steuerelemente stellt sich nun noch die Frage, wann nun welche Option am sinnvollsten ist. Auch dazu gibt es ein paar Richtlinien, aber leider keine eindeutigen.
Me im eigenen Klassenmodul ist gesetzt
Ganz klar: Wenn Sie auf Elemente in dem Formular zugreifen wollen, in dessen Formularklassenmodul Sie gerade programmieren, dann verwenden wir immer das Schlüsselwort Me für den Zugriff auf Elemente des aktuellen Formulars.
Punkt oder Ausrufezeichen?
Aber referenzieren wir die Steuerelemente dann über den Punkt oder das Ausrufezeichen?
Ganz ehrlich: Bisher habe immer das Ausrufezeichen verwendet, einfach um benutzerdefinierte Elemente von Eigenschaften unterscheiden zu können.
Aber im Rahmen der Recherche zu diesem Beitrag ist deutlich geworden, dass dies Nachteile haben kann. Auch wenn sich diese Nachteile in meinem Programmieralltag sehr selten bemerkbar gemacht haben.
Der Nachteil entsteht, wenn man ein Steuerelement nachträglich umbenennt, das bereits anders heißt als das gebundene Feld (also zum Beispiel beim Feld Vorname, das im Textfeld txtVorname angezeigt wird). Wir schauen uns ein Beispiel an, bei dem wir die folgende Prozedur für die Schaltfläche cmdVornameAusgeben hinterlegt haben:
Private Sub cmdVornameAusgeben_Click() Debug.Print Me!txtVorname Debug.Print Me.txtVorname End Sub
Solange das Steuerelement txtVorname heißt, liefern beide Zeilen das korrekte Ergebnis.
Wenn wir jedoch den Namen des Textfeldes im Formularentwurf auf txtFirstname ändern, wird es interessant. Wir bekommen dann nämlich zwei unterschiedliche Fehler. Wir schauen uns erst einmal den ersten Fehler an. Dabei handelt es sich gleich um einen Kompilierfehler, der in der Zeile auftaucht, wo wir die Punktsyntax verwendet haben (siehe Bild 9). Warum ein Kompilierfehler? Weil wir hier über die Punktsyntax auf die Eigenschaft zugreifen und nicht auf ein Element einer Auflistung. Die Eigenschaft ist nicht vorhanden und deshalb kann die Prozedur nicht kompiliert werden.
Bild 9: Kompilierfehler
Auf diesen Fehler würden wir auch stoßen, wenn wir das VBA-Projekt mit Debuggen|Kompilieren von Datenbankname kompilieren würden oder wenn wir versuchen würden, eine .accde-Datei aus der Datenbank zu erstellen.
Wenn wir diesen Fehler behoben haben und die Prozedur erneut ausführen, sehen wir den nächsten Fehler bei der Zeile mit der Ausrufezeichen-Syntax. Hier handelt es sich allerdings um einen Laufzeitfehler (siehe Bild 10).
Bild 10: Laufzeitfehler
Warum ist es in diesem Fall nun sinnvoller, die Punkt-Syntax zu verwenden? Weil wir so bereits beim Kompilieren erkennen können, ob es noch Fehler durch falsch benannte Elemente gibt.
Wenn wir die Ausrufezeichen-Syntax verwendet hätten, wäre dieser Fehler erst beim Testen oder, noch schlimmer, bei Kunden aufgefallen.
Forms-Auflistung oder Formularklassenname?
Damit kommen wir zur Situation, dass wir nicht von innerhalb des gleichen Formularklassenmoduls aus programmieren. Auch hier sollte nun das Ziel sein, auf die Steuerelemente als Eigenschaften zuzugreifen, um eventuelle Probleme mit falsch benannten Referenzen direkt als Kompilierfehler zu erkennen.
Dies ist allerdings nur möglich, wenn wir bereits bei den Formularen nicht wie bei Form!frmKundenUndProjekte die Auflistung verwenden, sondern direkt mit dem Klassennamen die Formularklasse referenzieren, also Form_frmKundenUndProjekte.
Bei einem Ausdruck wie Form_frmKundenUndProjekte.txtVorname können wir durch Kompilieren gleich zwei Fehlern vorbeugen – sowohl einem geänderten Formular- als auch einem geänderten Steuerelementnamen.
Performancefrage
Wenn wir die Unterschiede schon so genau unter die Lupe nehmen, sollten wir auch noch einen Blick auf die Performance werfen.
Hier wollen wir die folgenden Funktionen untersuchen, in denen wir einerseits über die Klasseninstanz auf das Objekt zugreifen und andererseits über die Auflistung:
Public Function Objekt() Dim strVorname As String strVorname = Form_frmKundenUndProjekte.txtFirstname End Function Public Function Liste() Dim strVorname As String strVorname = Forms!frmKundenUndProjekte!txtFirstname End Function
Bild 11 zeigt das Ergebnis. Die Variante mit der Objektinstanz ist zwischen 15% und 20% schneller als die andere Variante.
Bild 11: Performancevergleich
Wir haben auch noch getestet, ob die lange oder die kurze Schreibweise über die Auflistungen schneller ist:
Public Function Lang() Dim strVorname As String strVorname = Forms.Item("frmKundenUndProjekte").Controls("txtFirstname").Value End Function Public Function Kurz() Dim strVorname As String strVorname = Forms!frmKundenUndProjekte!txtFirstname End Function
Das Ergebnis: Beide sind ungefähr gleich schnell.
Zusammenfassung und Ausblick
Dieser Beitrag liefert eine Übersicht über die verschiedenen Möglichkeiten, Formulare und Steuerelemente zu referenzieren.
Dabei untersuchen wir auch, welche Methode welche Vor- und Nachteile aufweist.
Schließlich haben wir uns noch angesehen, wie sich verschiedene Maßnahmen auf die Performance auswirken.
Downloads zu diesem Beitrag
Enthaltene Beispieldateien:
Mitarbeiterverwaltung.accdb