Form und Subform in der Datenblattansicht per Wizard

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.

Formular mit Unterformular in der Datenblattansicht

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).

Uhser Assistent in der Liste der Formular-Assistenten

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).

Der Wizard zum Erstellen von Unterformularen in der Datenblattansicht

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).

Das neu erstellte Formular mit Unterformular

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).

Hinweis auf bereits vorhandene Formulare

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.

Die Tabelle USysRegInfo

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

Schreibe einen Kommentar