Eine immer wiederkehrende Aufgabe in meinem Programmieralltag ist das Erstellen von Formularen, die ein Unterformular mit Daten in der Datenblattansicht enthalten. Das umfasst einige Schritte, die ich immer wieder manuell durchgeführt habe. Bis ich die Lösung für diesen Beitrag programmiert habe. Einen Assistenten, den ich starte, statt im Ribbon den Befehl zum Erstellen eines neuen, leeren Formulars in der Entwurfsansicht aufzurufen. Und der lediglich eine Information benötigt: Welche Tabelle oder Abfrage soll die Daten für das Unterformular bereitstellen? Das Ganze in einem Assistenten verpackt, der in jeder geöffneten Access-Datenbank per Mausklick gestartet werden kann. Nachfolgend finden Sie die Anleitung, wie ich diesen Assistenten erstellt habe und wie er funktioniert.
Die Anzeige der Daten einer Tabelle oder Abfrage in einem Unterformular wird oft benötigt. Das Unterformular zeigt die Daten in der Datenblattansicht an, im Hauptformular kann man beliebige Steuerelemente zum Bearbeiten oder Hinzufügen von Datensätzen, zum Anzeigen eines Detailformulars zum ausgewählten Datensatz, zum Löschen von Datensätzen oder zum Filtern oder Sortieren der Datensätze anzeigen.
Die Anzeige der Daten in der Datenblattansicht ermöglicht außerdem die Nutzung der Filter- und Sortiermöglichkeiten dieser Ansicht. Zudem können wir Spalten anordnen oder ein- und ausblenden.
Das Anlegen einer solchen Kombination aus Haupt- und Unterformular erfordert einige Schritte, die wir nachfolgend auflisten:
- Anlegen des Unterformulars
- Zuweisen der Datensatzquelle für das Formular
- Hinzufügen der anzuzeigenden Felder
- Einstellen der Eigenschaft Standardansicht auf Datenblatt
- Speichern des Unterformulars unter dem gewünschten Namen
- Anlegen des Hauptformulars
- Einfügen des Unterformulars
- Einstellen weiterer Eigenschaften des Hauptformulars, zum Beispiel Automatisch zentrieren auf Ja, Bildlaufleisten auf Keine, Datensatzmarkierer und Navigationsschaltflächen auf Nein.
- Einstellen der Verankerung des Unterformular-Steuerelements, sodass sich seine Größe beim Vergrößern des Hauptformulars ebenfalls ändert
- Speichern des Hauptformulars unter dem gewünschten Namen
Diese Schritte habe ich in der Vergangenheit hunderte, wenn nicht tausende Male durchgeführt. Also beschloss ich, einen Wizard zu programmieren, der mit diese immer wiederkehrenden Schritte abnimmt und ein Formular wie das aus Bild 1 schnell erstellt.

Bild 1: Formular mit Unterformular in der Datenblattansicht
Damit ist eine Basis geschaffen, der ich dann die weiteren Steuerelemente hinzufügen kann.
Funktionsweise des Wizards
Der Wizard wird einfach über den Eintrag Formular-Assistent im Ribbon aufgerufen. Dies zeigt alle verfügbaren Assistenten an (siehe Bild 2).

Bild 2: Uhser Assistent in der Liste der Formular-Assistenten
Hier wählen wir unseren Assistenten aus und selektieren die Datensatzquelle für das zu erstellende Formular. Diese ist bereits voreingestellt, wenn beim Aufrufen des Assistenten schon eine Tabelle oder Abfrage im Navigationsbereich ausgewählt war.
Danach erscheint unser selbst programmiertes Formular, mit dem wir weitere Details festlegen können (siehe Bild 3).

Bild 3: Der Wizard zum Erstellen von Unterformularen in der Datenblattansicht
Dabei handelt es sich um die folgenden Informationen:
- Anzuzeigende Felder: Hier können wir die Felder festlegen, die in das Unterformular in der Datenblattansicht übernommen werden sollen.
- Name des Hauptformulars: Hier geben wir den Namen des zu erstellenden Hauptformulars ein, für den bereits ein Vorschlag vorgegeben wird.
- Name des Unterformulars: Hier stellen wir den Namen des Unterformulars ein. Auch hier finden wir einen Vorschlag.
- Vorhandene Formulare überschreiben: Legt fest, dass eventuell vorhandene Formulare gleichen Namens überschrieben werden sollen.
- Schaltfläche Formular erstellen: Startet das Erstellen des Haupt- und Unterformulars.
- Schaltfläche Abbrechen: Schließt das Formular, ohne neue Formulare zu erstellen.
Wenn wir nun auf Formular erstellen klicken, arbeitet Access kurz und präsentiert anschließend das neue Formular (siehe Bild 4).

Bild 4: Das neu erstellte Formular mit Unterformular
Starten wir den Assistenten danach erneut für die gleiche Datensatzquelle, zeigt dieser direkt an, dass die automatisch vorgeschlagenen Formularnamen bereits existieren (siehe Bild 5).

Bild 5: Hinweis auf bereits vorhandene Formulare
In diesem Fall können wir diese entweder überschreiben oder wir aktivieren die Option Vorhandene Formulare überschreiben.
Erst wenn eines von beiden erfolgt ist, wird die Schaltfläche zum Anlegen der Formulare aktiviert.
Programmierung des Assistenten
Damit die Datenbank mit unserer Lösung als Add-In installiert werden kann, benötigen wir eine Tabelle namens USysRegInfo, die wir wie in Bild 6 füllen.

Bild 6: Die Tabelle USysRegInfo
Diese enthält die Informationen, die beim Installieren in die Registry eingetragen werden.
Wichtig ist der Name der aufzurufenden Funktion beim Starten des Assistenten, hier Autostart_SubformWithDatasheetWizard.
Sie hat einen Parameter namens strRecordsource, der automatisch mit der Tabelle oder Abfrage gefüllt wird, die wir im Dialog zur Auswahl des Assistenten auswählen.
Diese Funktion sehen wir in Listing 1. Sie ruft lediglich eine weitere Prozedur namens CreateSubformWithDatasheet auf, der sie den Namen der Datensatzquelle übergibt.
Public Function Autostart_SubformWithDatasheetWizard(Optional strRecordsource As String) As Variant On Error GoTo Fehler Call CreateSubformWithDatasheet(strRecordsource) Exit Function Fehler: MsgBox Err.Number & " " & Err.Description End Function
Listing 1: Funktion, die beim Start des Wizards aufgerufen wird
Hauptfunktion des Wizards
Die Hauptfunktion CreateSubformWithDatasheet finden wir in Listing 2. Sie schreibt den Namen des Wizard-Formulars in die Variable strWizForm und schließt dieses zunächst, falls es noch geöffnet ist.
Private Sub CreateSubformWithDatasheet(strRecordsource As String) Dim db As DAO.Database Dim strNewForm As String Dim strNewSubform As String Dim bolOverwriteForms As Boolean Dim strWizForm As String Dim strTempForm As String Dim strFields As String strWizForm = "frmUnterformularMitDatenblatt" On Error Resume Next DoCmd.Close acForm, strWizForm On Error GoTo Fehler DoCmd.OpenForm strWizForm, WindowMode:=acDialog, OpenArgs:=strRecordsource If IstFormularGeoeffnet(strWizForm) Then strNewForm = Forms(strWizForm)!txtForm strNewSubform = Forms(strWizForm)!txtSubform strFields = GetSelectedFields(Forms(strWizForm)!lstFields) bolOverwriteForms = Forms(strWizForm)!chkOverwriteForms DoCmd.Close acForm, strWizForm Else Exit Sub End If On Error GoTo 0 Set db = CurrentDb strTempForm = CreateSubform(db, strRecordsource, strFields) If RenameForm(strNewSubform, strTempForm, bolOverwriteForms) = True Then strTempForm = CreateForm(strNewSubform) If RenameForm(strNewForm, strTempForm, bolOverwriteForms) = False Then MsgBox "Formular ''" & strNewForm & "'' konnte nicht erstellt werden.", vbExclamation + vbOKOnly, _ "Fehler bei Erstellung" DoCmd.DeleteObject acForm, strTempForm Exit Sub End If Else MsgBox "Unterformular ''" & strNewSubform & "'' konnte nicht erstellt werden.", vbExclamation + vbOKOnly, _ "Fehler bei Erstellung" DoCmd.DeleteObject acForm, strTempForm Exit Sub End If DoCmd.OpenForm strNewForm Exit Sub Fehler: Select Case Err.Number Case 0, 2501 Case Else MsgBox "Fehler " & Err.Number & vbCrLf & Err.Description End Select End Sub
Listing 2: Hauptfunktion des Wizards
Dann öffnet sie das Formular als modalen Dialog und übergibt die zu verwendende Datensatzquelle mit dem Parameter OpenArgs. Im Formular gibt der Benutzer nun die gewünschten Daten ein und macht dieses entweder mit der Schaltfläche Formular erstellen unsichtbar oder schließt es mit der Abbrechen-Schaltfläche.
Deshalb prüfen wir im folgenden Schritt mit der Hilfsfunktion IstFormularGeoeffnet, ob das Formular ausgeblendet oder geschlossen wurde.
Falls das Formular noch geöffnet, aber unsichtbar ist, liest die Prozedur den Namen des zu erstellenden Formulars in strNewForm und den des Unterformulars in strNewSubform ein. Außerdem liest sie die im Listenfeld lstFields mit der Funktion GetSelectedFields in die Variable strFields ein.
Diese Funktion durchläuft alle selektierten Einträge aus der Eigenschaft ItemsSelected und fügt den jeweiligen Eintrag zur Variablen strFields hinzu. Die Einträge haben jeweils ein vorangestelltes Semikolon. Nach dem Durchlaufen aller Felder wird noch ein abschließendes Semikolon hinzugefügt und das Ergebnis zurückgegeben:
Public Function GetSelectedFields(lst As ListBox) As String Dim var As Variant Dim i As Integer Dim strFields As String For Each var In lst.ItemsSelected strFields = strFields & ";" & lst.ItemData(var) i = i + 1 Next var strFields = strFields & ";" GetSelectedFields = strFields End Function
Das Ergebnis lautet beispielsweise wie folgt:
;KundeID;Firma;Vorname;AnredeID;Aktiv;
Außerdem lesen wir den Wert des Kontrollkästchens chkOverwriteForms in die Variable bolOverwriteForms ein. Damit können wir das Formular nun endgültig schließen.
Nun erstellen wir mit der Funktion CreateSubForm zunächst das Unterformular und fügen diesem die ausgewählten Felder als gebundene Steuerelemente mit Beschriftungsfeld hinzu.
Diese Funktion sehen wir uns im Anschluss an.
Unterformular umbenennen
Die Funktion liefert den Namen des neu erstellten Unterformulars zurück und wir speichern es in strTempForm.
Dann benennen wir diese mit der Funktion RenameForm in den durch den Benutzer eingegebenen Namen um.
Diese Funktion nimmt den neuen und den alten Namen entgegen und außerdem den Boolean-Wert, der angibt, ob bestehende Formulare gelöscht werden sollen.
Wenn ein bestehendes Formular überschrieben werden soll, schließen wir dieses, falls es noch geöffnet ist, und löschen es anschließend mit DoCmd.Delete.
Anschließend benennen wir das neu erstellte Formular auf den angegebenen Namen um und liefern den Wert True zurück:
Public Function RenameForm(strNew As String, strOld As _ String, bolOverwriteForms As Boolean) As Boolean On Error GoTo Fehler If bolOverwriteForms = True Then On Error Resume Next DoCmd.Close acForm, strNew On Error GoTo Fehler DoCmd.DeleteObject acForm, strNew End If DoCmd.Rename strNew, acForm, strOld RenameForm = True Exit Function Fehler: MsgBox "Fehler " & Err.Number & vbCrLf & Err.Description End Function
War dies nicht erfolgreich, löschen wir das angelegte Formular wieder und verlassen die Prozedur.
Hauptformular anlegen
War dies erfolgreich, erstellen wir das Hauptformular mit der Funktion CreateForm und übergeben dieser den Namen des zu erstellenden Formulars.
Auch diese Funktion beschreiben wir im Anschluss.
Für das neu erstellte Hauptformular rufen wir wiederum die Funktion RenameForm auf und übergeben den neuen und den alten Namen, damit das Formular den gewünschten Namen erhält. Wenn dies fehlschlägt, gibt die Funktion einen entsprechenden Hinweis auf, das Formular wird gelöscht und die Prozedur beendet.
Schließlich öffnen wir das neu erstellte Formular.
In der Fehlerbehandlung geben wir keine Meldung für den Fehler mit der Nummer 2501 aus. Dieser wird ausgelöst, wenn wir das Formular öffnen, aber diesem keine Datenastzquelle übergeben wurde.
In diesem Fall wird das Öffnen des Formulars bereits im Ereignis Form_Open abgebrochen, was die Anweisung DoCmd.OpenForm mit dem Fehler 2501 quittiert. Alle anderen Fehler werden per Meldungsfenster angezeigt.
Funktion zum Erstellen des Unterformulars
Die Funktion zum Erstellen des Unterformulars heißt CreateSubform und nimmt einen Verweis auf die aktuell geöffnete Datenbank, die Datensatzquelle und die Liste der zu erstellenden Felder entgegen (siehe Listing 3).
Public Function CreateSubform(db As DAO.Database, strRecordsource As String, strFields As String) As String Dim rst As DAO.Recordset, fld As DAO.Field Dim sfm As Form Dim strFormTemp As String Dim i As Integer Dim txt As Access.TextBox, cbo As Access.ComboBox, chk As Access.CheckBox, lbl As Access.Label Dim strField As String Dim strControlName As String Set rst = db.OpenRecordset("SELECT * FROM " & strRecordsource & " WHERE 1=2", dbOpenSnapshot) Set sfm = Application.CreateForm() strFormTemp = sfm.Name With sfm .RecordSource = strRecordsource .DefaultView = 2 For Each fld In rst.Fields strField = fld.Name If Not InStr(1, strFields, ";" & strField & ";") = 0 Then Select Case GetControlType(fld) Case acTextBox Set txt = Application.CreateControl(strFormTemp, acTextBox, acDetail, , strField, 1100, _ 100 + i * 400, 1000, 400) strControlName = "txt" & strField txt.Name = "txt" & strField Case acComboBox Set cbo = Application.CreateControl(strFormTemp, acComboBox, acDetail, , strField, 1100, _ 100 + i * 400, 1000, 400) strControlName = "cbo" & strField cbo.Name = "cbo" & strField Case acCheckBox Set chk = Application.CreateControl(strFormTemp, acCheckBox, acDetail, , strField, 1100, _ 100 + i * 400, 1000, 400) strControlName = "chk" & strField chk.Name = "chk" & strField End Select Set lbl = Application.CreateControl(sfm.Name, acLabel, acDetail, strControlName, strField, 100, _ 100 + i * 400, 1000, 400) With lbl .Name = "lbl" & strField End With End If i = i + 1 Next fld End With DoCmd.Close acForm, strFormTemp, acSaveYes CreateSubform = strFormTemp End Function
Listing 3: Funktion zum Erstellen des Unterformulars
Sie erstellt ein Recordset auf Basis der übergebenen Datensatzquelle.
Dann erstellt sie mit der Methode CreateForm ein neues Formular und speichert einen Verweis darauf in der Variablen sfm. Außerdem schreibt sie den automatisch vergebenen Namen für dieses Formular, zum Beispiel Formular1, in die Variable strFormTemp.
Sie stellt die Eigenschaft RecordSource auf die übergebene Datensatzquelle aus strRecordsource ein und legt für die Eigenschaft Standardansicht (DefaultView) den Wert Datenblatt fest.
Dann durchläuft sie die Fields-Auflistung des Recordsets und schreibt den Namen des aktuellen Felds in die Variable strField.
Dieser Variablen fügt sie vorn und hinten ein Semikolon hinzu. Damit können wir per InStr-Funktion prüfen, ob das Feld in der Liste aus strFields vorkommt:
;KundeID;Firma;Vorname;AnredeID;Aktiv;
Steuerelementtyp für das Feld ermitteln
Nur wenn das Feld dort enthalten ist, folgt der Aufruf einer Select Case-Bedingung. Diese ermittelt mit GetControlType für das Feld aus fld den Steuerelementtyp des Feldes. Dieser kann TextBox, ComboBox oder CheckBox lauten. Die Funktion sieht so aus:
Public Function GetControlType(fld As DAO.Field) Dim prp As DAO.Property Dim lngDisplayControl As Long On Error Resume Next Set prp = fld.Properties("DisplayControl") lngDisplayControl = prp.Value On Error GoTo 0 If lngDisplayControl = 0 Then lngDisplayControl = 109 End If GetControlType = lngDisplayControl End Function
Sie prüft, ob das Feld eine Eigenschaft namens DisplayControl aufweist. Diese enthält für Textfelder den Wert 109, für Kombinationsfelder den Wert 111 und für Kontrollkästchen den Wert 106.
Steuerelemente zum Unterformular hinzufügen
Je nach Steuerelementtyp wird einer der folgenden Case-Zweige angesteuert. Im Falle eines Textfeldes erstellt die Prozedur mit der Funktion CreateControl ein Steuerelement des Typs acTextBox und übergibt weitere Informationen wie das Feld, an welches das Steuerelement gebunden werden soll, den Zielbereich (acDetail für den Detailbereich) und die Position und die Größe.
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
