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

Unser Assistent in der Liste der Formular-Assistenten

Bild 2: Unser 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 den Namen entweder anpassen 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 ausgewählten Felder 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 Datensatzquelle ü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" & strControlName
                    Case acComboBox
                        Set cbo = Application.CreateControl(strFormTemp, acComboBox, acDetail, , strField, 1100, _
                            100 + i * 400, 1000, 400)
                        strControlName = "cbo" & strField
                        cbo.Name = "cbo" & strControlName
                    Case acCheckBox
                        Set chk = Application.CreateControl(strFormTemp, acCheckBox, acDetail, , strField, 1100, _
                            100 + i * 400, 1000, 400)
                        strControlName = "chk" & strField
                        chk.Name = "chk" & strControlName
                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

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