Übersichtsformular per Mausklick

Manche Aufgaben im Alltag eines Access-Entwicklers wiederholen sich immer wieder und unterscheiden sich nur durch Kleinigkeiten. Zum Beispiel sehen Detailformulare, Übersichtsformulare oder auch Formulare zum Verwalten von Daten in Haupt- und Unterformular immer gleich aus – wenn auch für andere Daten. Im Beitrag „Tabellendaten mit Übersicht und Details anzeigen“ (www.access-im-unternehmen.de/1488) haben wir grundsätzlich gezeigt, wie wir ein Hauptformular mit einer Übersicht in einem Unterformular anlegen und programmieren können. Der Beitrag „Detailformular per Mausklick erstellen“ (www.access-im-unternehmen.de/10) wiederum liefert ein Beispiel dafür, wie wir ein einfaches Detailformular schnell definieren und per Mausklick erstellen können. Im vorliegenden Beitrag wollen wir beides kombinieren – also ein Übersichtsformular mit Haupt- und Unterformular in einem Konfigurationsformular einrichten und dann automatisch erstellen.

Im oben genannten Beitrag Tabellendaten mit Übersicht und Details anzeigen (www.access-im-unternehmen.de/1488) haben wir gezeigt, wie ein einfaches Formular zum Anzeigen der Übersicht über die Daten eines Formulars erstellt werden kann.

Dieses soll folgende Elemente beinhalten und sieht in der Formularansicht wie in Bild 1 aus:

Beispiel für ein Übersichtsformular

Bild 1: Beispiel für ein Übersichtsformular

  • Unterformular mit den anzuzeigenden Daten
  • Schaltfläche zum Anlegen neuer Datensätze
  • Schaltfläche zum Bearbeiten des aktuell markierten Datensatzes
  • Schaltfläche zum Löschen des aktuell markierten Datensatzes
  • Schaltfläche zum Schließen des Formulars
  • Textfeld zum Eingeben von Suchbegriffen zum Suchen nach Datensätzen im Unterformular

Außerdem soll sich das Unterformular an die Größe des Hauptformulars anpassen, wenn der Benutzer dieses vergrößert. Zum Erstellen eines solchen Formulars wollen wir in diesem Beitrag ein Konfigurationsformular erstellen sowie den Code, der nötig ist, um das Formular auf Knopfdruck anhand der vorgenommenen Einstellungen zu erstellen.

Dabei wollen wir auch direkt die Möglichkeit schaffen, das im Beitrag Detailformular per Mausklick erstellen (www.access-im-unternehmen.de/10) erstellte Formular zum Erstellen neuer oder zum Bearbeiten vorhandener Datensätze aufzurufen beziehungsweise den Namen dieses Formulars zu diesem Zweck auszuwählen.

Wir beginnen, indem wir das Konfigurationsformular Schritt für Schritt zusammenstellen und dabei gleichzeitig den Code entwickeln, der zum Erstellen des Formulars dienen soll.

Im gleichen Zuge stellen wir auch die Tabellen zusammen, welche die Konfigurationsdaten erfassen. Wir wollen diese speichern und jederzeit erneut abrufen können.

Die dazu verwendeten Tabellen heißen tblOverviewforms und tblOverviewFields. Die Tabellen sind ähnlich aufgebaut wie in der Lösung zum Erstellen von Detailformularen. Die Tabelle tblOverviewForm sehen wir in der Entwurfsansicht in Bild 2. Sie enthält die wesentlichen Informationen zum Erstellen des Hauptformulars und der Steuerelemente im Hauptformular.

Tabelle für die Daten zum Erstellen von Haupt- und Unterformular

Bild 2: Tabelle für die Daten zum Erstellen von Haupt- und Unterformular

Die Tabelle tblOverviewFields enthält die Daten der Felder, die für die Übersicht im Unterformular angezeigt werden sollen (siehe Bild 3).

Tabelle für die Daten der Felder im Unterformular

Bild 3: Tabelle für die Daten der Felder im Unterformular

Von der einfachen Access-Anwendung zum Add-In

Wir arbeiten wieder in zwei Schritten: Wir werden zunächst das Konfigurationsformular in der gleichen Anwendung entwickeln, in der wir auch testweise die Formulare anlegen und aus der die Tabellen oder Abfragen stammen, die als Grundlage für das Übersichtsformular dienen. Erst wenn dies erledigt ist, wandeln wir die Datenbankdatei in eine Add-In-Datenbank um.

Das Konfigurationsformular sieht in der Formularansicht wie in Bild 4 aus.

Formulare mit den Daten zum Erstellen des Übersichtsformulars

Bild 4: Formulare mit den Daten zum Erstellen des Übersichtsformulars

Es enthält oben links die Basisinformationen des zu erstellenden Formulars:

  • Datensatzquelle: Tabelle oder Abfrage mit den in der Übersicht anzuzeigenden Daten
  • Formularname: Name des zu erstellenden Formulars
  • Formulartitel: Text für die Titelleiste des Formulars
  • Rand/Abstand: Gibt die Abstände der Steuerelemente zu den Rändern und zu den anderen Steuerelementen an.
  • Automatisch zentrieren: Legt den Wert für die Eigenschaft Automatisch zentrieren fest.
  • Bereich Suchen-Textfeld: Hier finden wir Informationen darüber, ob ein Suchfeld angezeigt werden soll, wie die Beschriftung des Suchfeldes heißt und welcher Code ausgelöst werden soll, wenn sich der Wert des Suchen-Feldes ändert.
  • Bereich OK-Button: Enthält Informationen darüber, ob eine OK-Schaltfläche angezeigt werden soll, welchen Text sie enthalten soll, welchen Code sie ausführen und welches Symbol sie enthalten soll.
  • Bereich Anlegen-Button: siehe OK-Button
  • Bereich Bearbeiten-Button: siehe OK-Button
  • Bereich Löschen-Button: siehe OK-Button

Im unteren Bereich sehen wir schließlich noch ein Unterformular, das alle Felder der für die Eigenschaft Datensatzquelle ausgewählten Tabelle oder Abfrage aufnimmt. Hier können wir individuelle Einstellungen für diese Felder vornehmen, zum Beispiel, ob dieses Feld angezeigt oder ignoriert werden soll. Außerdem können wir die Beschriftung für die Spaltenköpfe hier abweichend von den Feldnamen und den im Tabellenentwurf gespeicherten Beschriftungen vornehmen. Mit Fieldindex geben wir die Reihenfolge an und mit FieldWidth die Breite des Feldes. Außerdem finden wir hier noch die typischen Eigenschaften für Felder, die als Kombinationsfelder ausgelegt sind und die wir hier auch nochmal individuell anpassen können.

Tabelle oder Abfrage mit den anzuzeigenden Daten auswählen

Der erste Schritt ist die Auswahl der Tabelle oder Abfrage, deren Daten im Unterformular angezeigt werden soll. Dazu benötigen wir ein Kombinationsfeld, mit dem wir alle Tabellen und Abfragen der aktuellen Datenbank auswählen können.

Dieses Kombinationsfeld wollen wir mit allen Tabellen und Abfragen füllen, die in der Tabelle MSysObjects vorhanden sind. Dabei müssen wir die Elemente erfassen, die im Feld Type einen der Werte 1, 4, 5 oder 6 enthalten und deren Wert im Feld Name nicht mit F_ oder MSys beginnt.

Allerdings können wir dazu später in der Add-In-Datenbank nicht einfach eine Abfrage zuweisen, die auf der Tabelle MSysObjects basiert. Damit würden wir nämlich auf die Tabelle MSysObjects der Add-In-Datenbank zugreifen und nicht auf die der Host-Datenbank, der wir das Formular hinzufügen wollen. Also müssen wir ein Recordset erstellen und dieses mit den entsprechenden Daten der Tabelle MSysObjects der Host-Datenbank füllen.

Das erledigen wir in der Prozedur, die durch das Ereignis Beim Laden des Formulars ausgelöst wird (siehe Listing 1).

Private Sub Form_Load()
     Dim db As DAO.Database
     Dim dbc As DAO.Database
     Dim rst As DAO.Recordset
     Dim rstIcons As DAO.Recordset
     Set db = CurrentDb
     Set dbc = CodeDb
     Set rst = db.OpenRecordset("SELECT Name FROM MSysObjects WHERE Type IN (1, 4, 5, 6) AND Name NOT LIKE ''F_*'' " _
         & "AND Name NOT LIKE ''MSys*'' AND Name NOT LIKE ''USys*'' AND Name NOT LIKE ''~*''", dbOpenDynaset)
     Set Me!cboDatasource.Recordset = rst
     Set rstIcons = dbc.OpenRecordset("SELECT id, [Name] FROM MSysResources WHERE Extension = ''png'' ORDER BY [Name]", _
         dbOpenDynaset)
     Set Me.cboOKIcon.Recordset = rstIcons
     Me!chkOKButton.DefaultValue = True
     Me!txtOKCaption.DefaultValue = """OK"""
     Me!txtOKCode.DefaultValue = """Debug.Print """"OK"""""""
     Me!cboOKIcon.DefaultValue = 13
     Set Me!cboAddNewIcon.Recordset = rstIcons
     Me!chkAddNewButton.DefaultValue = True
     Me!txtAddNewCaption.DefaultValue = """Anlegen"""
     Me!txtAddNewCode.DefaultValue = """Debug.Print """"Anlegen"""""""
     Me!cboAddNewIcon.DefaultValue = 10
     Set Me!cboDeleteIcon.Recordset = rstIcons
     Me!chkDeleteButton.DefaultValue = True
     Me!txtDeleteCaption.DefaultValue = """Löschen"""
     Me!txtDeleteCode.DefaultValue = """Debug.Print """"Löschen"""""""
     Me!cboDeleteIcon.DefaultValue = 12
     Set Me!cboEditIcon.Recordset = rstIcons
     Me!chkEditButton.DefaultValue = True
     Me!txtEditCaption.DefaultValue = """Bearbeiten"""
     Me!txtEditCode.DefaultValue = """Debug.Print """"Bearbeiten"""""""
     Me!cboEditIcon.DefaultValue = 11
     Me!chkAutoCenter.DefaultValue = True
     Me!cboButtonStyleID.DefaultValue = dbc.OpenRecordset("SELECT ButtonStyleID FROM tblButtonStyles", _
         dbOpenDynaset).Fields(0)
     Me!txtSubformHeight.DefaultValue = 5000
     Me!txtSubformWidth.DefaultValue = 10000
     Me!txtMargin.DefaultValue = 50
     Me!chkSearchTextbox.DefaultValue = True
     Me!txtSearchCaption.DefaultValue = """Suchen nach"""
     Me!txtSearchCode.DefaultValue = """Debug.Print """"Suchen"""""""
     If Not Len(Nz(Me.OpenArgs, "")) = 0 Then
         Me!cboDatasource = Me.OpenArgs
         cboDatasource_AfterUpdate
     End If
End Sub

Listing 1: Aktionen beim Starten des Formulars frmOverviewForms

Die Prozedur definiert zwei verschiedene Database-Objekte, von denen das erste mit CurrentDb das Database-Objekt der Host-Datenbank referenziert und das zweite mit CodeDb das Database-Objekt der Add-In-Datenbank. Solange wir noch mit einer Datenbank entwickeln, beziehen sich beide Objektvariablen auf das gleiche Database-Objekt. Mit der Variablen rst referenzieren wir die Datensätze der Tabelle MSysObjects der Host-Datenbank für das Kombinationsfeld der Datensatzquellen. Dieses weisen wir der Recordset-Eigenschaft des Kombinationsfeldes cboDatasource zu.

Das Recordset rstIcons stellen wir auf die Tabelle MSysResources der Add-In-Datenbank ein. Diese Tabelle enthält die Icons, die wir den Schaltflächen der zu erstellenden Formulare zuweisen wollen.

Das Recordset weisen wir den Kombinationsfeldern cboOKIcon, cboAddNewIcon, cboDeleteIcon und cboEditIcon zu.

Außerdem stellt die Prozedur beim Laden Standardwerte für die Kontrollkästchen und Textfelder der Schaltflächen ein.

Auswählen einer Datensatzquelle für das zu erstellende Übersichtsformular

Der erste Schritt beim Erstellen ist die Auswahl einer Datensatzquelle, auf deren Basis das Übersichtsformular erstellt werden soll. Das Auswählen eines der Einträge des Kombinationsfeldes cboDatasource löst das Ereignis Nach Aktualisierung aus, für das wir die Prozedur aus Listing 2 hinterlegen.

Private Sub cboDatasource_AfterUpdate()
     If ReadOverviewFieldsFromTable(Me!cboDatasource) = True Then
         Me!sfmOverviewforms.Form.Requery
     End If
     If Len(Nz(Me!txtFormname, "")) = 0 Then
         Me!txtFormname = Replace(Replace(Me!cboDatasource, "tbl", "frm"), "qry", "frm")
     End If
     If Len(Nz(Me!txtUnterformularname, "")) = 0 Then
         Me!txtUnterformularname = Replace(Me!txtFormname, "frm", "sfm")
     End If
     If Len(Nz(Me!txtFormTitle, "")) = 0 Then
         Me!txtFormTitle = Replace(Replace(Me!cboDatasource, "tbl", ""), "qry", "")
     End If
End Sub

Listing 2: Aktualisieren des Kombinationsfeldes cboDatasource

Die Prozedur ruft eine weitere Funktion namens ReadOverviewFieldsFromTable auf, der wir die gewählte Tabelle oder Abfrage übergeben und die wir gleich beschreiben. Hat diese Funktion die Felder erfolgreich ausgelesen und in die Tabelle tblOverviewFields geschrieben, wird das Unterformular sfmOverviewforms aktualisiert.

Wenn txtFormname noch nicht gefüllt ist, trägt die Prozedur automatisch den Namen der Datenquelle ein, wobei vorher Präfixe wie tbl oder qry durch frm ersetzt werden. Das Unterformular erhält den gleichen Namen, allerdings wird das Präfix frm durch sfm ersetzt.

Für den Titel wird der gleiche Ausdruck eingetragen, jedoch werden die Präfixe tbl und qry ersatzlos gestrichen.

Eintragen der Feldnamen für die Übersicht im Unterformular

Die im Unterformular anzuzeigenden Felder werden erstmalig ausgelesen, sobald der Benutzer die Datensatzquelle selektiert hat. Dies löst die Prozedur cboDatasource_AfterUpdate und in der Folge die Funktion ReadOverviewFieldsFromTable aus. Diese Funktion nimmt den Namen der Tabelle oder Abfrage entgegen, die als Datensatzquelle gewählt wurde (siehe Listing 3). Dann referenziert sie die Host- und die Add-In-Database-Objekte mit den Variablen db und dbc.

Public Function ReadOverviewFieldsFromTable(strDataSource As String) As Boolean
     Dim db As DAO.Database, dbc As DAO.Database
     Dim rst As DAO.Recordset, rstSource As DAO.Recordset
     Dim bolReadFields As Boolean, lngControlType As AcControlType
     Dim varRowSourceType As Variant, varRowSource As Variant, varBoundColumn As Variant
     Dim varCaption As Variant, varColumnCount As Variant, varColumnWidths As Variant
     Dim fld As DAO.Field, i As Long
     Set db = CurrentDb
     Set dbc = CodeDb
     Set rst = dbc.OpenRecordset("SELECT * FROM tblOverviewfields WHERE OverviewformID = " & Me!OverviewformID, _
         dbOpenDynaset)
     If Not rst.EOF Then
         rst.MoveLast
     End If
     bolReadFields = True
     Me.Dirty = False
     If Not rst.RecordCount = 0 Then
         bolReadFields = MsgBox("Sollen die Felder neu eingelesen werden?", vbYesNo + vbExclamation, _
             "Felder neu einlesen?") = vbYes
     End If
     If bolReadFields = True Then
         dbc.Execute "DELETE FROM tblOverviewfields WHERE OverviewformID = " & Me!OverviewformID, dbFailOnError
         Set rstSource = db.OpenRecordset(strDataSource, dbOpenDynaset)
         For Each fld In rstSource.Fields
             Call GetControlProperties(fld, varCaption, intControlType, varBoundColumn, varRowSourceType, _
                 varRowSource, varColumnCount, varColumnWidths)
             rst.AddNew
             rst!OverviewformID = Me!txtOverviewFormID
             rst!FieldName = fld.Name
             rst!Fieldindex = i
             rst!FieldWidth = 2000
             rst!FieldHeight = 300
             rst!FieldColumn = 1
             rst!Fieldlabel = Nz(varCaption, fld.Name)
             rst!ControlType = lngControlType
             rst!RowSourceType = varRowSourceType
             rst!RowSource = varRowSource
             rst!BoundColumn = varBoundColumn
             rst!ColumnCount = varColumnCount
             rst!ColumnWidths = varColumnWidths
             rst.Update
             i = i + 1
         Next fld
         ReadOverviewFieldsFromTable = True
         Me!sfmOverviewforms.Form.Requery
     Else
         ReadOverviewFieldsFromTable = False
     End If
End Function

Listing 3: Aktualisieren der Felder für das Übersichtsformular

Für die Add-In-Datenbank referenziert sie mit der Variablen rst die Datensätze der Tabelle tblOverviewFields, die über das Fremdschlüsselfeld OverviewFormID mit dem aktuellen Primärschlüsselwert des Hauptformulars verbunden sind. Findet die Funktion hier bereits Datensätze, verschiebt sie den Datensatzzeiger auf den letzten Datensatz, um mit rst.RecordCount die Anzahl der Datensätze zu ermitteln. Dann stellt sie die Variable bolReadFields auf True und speichert alle Änderungen im Formular.

Sind nun bereits Datensätze vorhanden, fragt die Funktion den Benutzer, ob diese neu eingelesen werden sollen. Antwortet der Benutzer mit Ja, behält bolReadFields den Wert True und die Felder werden nachfolgend neu ausgelesen. Anderenfalls werden die Felder nicht neu eingelesen.

Im Falle des Neueinlesens löscht die Funktion zunächst alle Einträge der Tabelle tblOverviewfields, die dem aktuell im Formular angezeigten Datensatz der Tabelle tblOverviewForms zugeordnet sind.

Dann stellt sie das Recordset rstSource auf die mit strDataSource übergebene Tabelle oder Abfrage ein. Anschließend durchläuft sie alle Felder dieses Recordsets. Dabei ruft sie für jedes Feld die Funktion GetControlProperties auf und übergibt dieser mit fld einen Verweis auf das Feld sowie die Variablen varCaption, intControlType, varBoundColumn, varRowSourceType, varRowSource, varColumnCount und varColumnWidths. Diese Funktion, die wir weiter unten beschreiben, liest die Werte für die Variant-Parameter aus den Eigenschaften des Field-Objekts aus fld aus.

Die folgenden Anweisungen tragen einige Informationen in das Recordset auf Basis der Tabelle tblOverviewfields ein. Dazu gehört der Primärschlüsselwert des aktuell im Hauptformular angezeigten Datensatzes der Tabelle tblOverviewForms, damit die Daten dem jeweils zu erstellenden Formular zugeordnet werden können.

Außerdem trägt sie den Feldnamen, den Index, den Wert 2000 als Feldbreite, die Beschriftung, den Steuerelementinhalt und einige Eigenschaften bezüglich eventuell in der Tabelle definierter Nachschlagefelder ein. Dazu gehören ControlType, RowSourceType, RowSource, BoundColumn, ColumnCount und ColumnWidths. Damit wird für jedes Feld der Datensatzquelle ein Datensatz in der Tabelle tblOverviewfields gespeichert und das Unterformular zur Anzeige dieser Felder aktualisiert.

Einlesen der Feldeigenschaften

Die soeben angesprochene Funktion GetControlProperties liest die verschiedenen Eigenschaften des mit dem Parameter fld übergebenen Feldes ein und liefert diese in Rückgabeparametern zurück (siehe Listing 4).

Public Function GetControlProperties(fld As DAO.Field, varCaption As Variant, intControlType As AcControlType, _
         varBoundColumn As Variant, varRowSourceType As Variant, varRowSource As Variant, varColumnCount As Variant, _
         varColumnWidths As Variant)
     intControlType = 0
     varCaption = Null
     varBoundColumn = Null
     varRowSourceType = Null
     varRowSource = Null
     varColumnCount = Null
     varColumnWidths = Null
     On Error Resume Next
     varCaption = fld.Properties("Caption")
     intControlType = fld.Properties("DisplayControl")
     Select Case intControlType
         Case acTextBox
             Debug.Print fld.Name, "acTextBox"
         Case acComboBox
             varBoundColumn = fld.Properties("BoundColumn")
             varRowSourceType = fld.Properties("RowSourceType")
             varRowSource = fld.Properties("RowSource")
             varColumnCount = fld.Properties("ColumnCount")
             varColumnWidths = fld.Properties("ColumnWidths")
         Case acCheckBox
             Debug.Print fld.Name, "acCheckBox"
         Case Else
             Debug.Print "anderer Controltype", intControlType
     End Select
     On Error GoTo 0
End Function

Listing 4: Einlesen der Feldeigenschaften

Einige Eigenschaften von Feldern kann man einfach so einlesen, aber bei einigen ist dies nicht so einfach möglich: Es werden nämlich nicht alle Eigenschaften für jedes Feld angelegt. Beispielweise wird die Eigenschaft Caption nur für ein Feld hinterlegt, wenn der Benutzer die Eigenschaft Beschriftung über den Tabellenentwurf eingestellt hat. Anderenfalls ist die Property namens Caption schlicht nicht vorhanden und der Versuch, diese auszulesen, löst einen Fehler aus.

Daher lesen wir diese Werte bei deaktivierter Fehlerbehandlung ein. Das ist der Fall bei der Eigenschaft Caption, aber auch bei DisplayControl. Letztere legt fest, ob ein Feld im Datenblatt als Textfeld, Kontrollkästchen oder Kombinationsfeld angezeigt wird. Im Falle eines Kombinationsfeldes stehen noch einige weitere Eigenschaften zur Verfügung. Um also herauszufinden, ob wir diese auch einlesen müssen, lesen wir zuerst den Wert der Eigenschaft DisplayControl ein. Lautet dieser acComboBox, lesen wir auch noch die nun verfügbaren Eigenschaften BoundColumn, RowSourceType, RowSource, ColumnCount und ColumnWidths in die entsprechenden Rückgabeparameter ein. Im Falle von acTextBox oder acCheckBox müssen keine weiteren Parameter eingelesen werden.

Übersichtsformulare per Code erstellen

Damit haben wir alle Informationen erfasst, die wir zum Erstellen des Übersichtsformulars benötigen und können nun den Code zusammenstellen. Dabei beginnen wir mit dem Unterformular, dass die Daten der Tabelle in der Datenblattansicht anzeigen soll. Wenn wir dieses erstellt haben, fügen wir den Code zum Erstellen des Hauptformulars hinzu. Dieses wird schließlich auch ein Unterformularsteuerelement enthalten, das wir mit dem Unterformular füllen.

Funktion zum Erstellen eines neuen Formulars

Wir verwenden eine einfache Funktion zum Erstellen der beiden Formulare. Diese heißt CreateNewForm und erwartet den Namen des zu erstellenden Formulars als einzigen Parameter. Wie daran gut zu erkennen ist, erledigt die Funktion nichts anderes, als ein neues Formular zu erstellen (siehe Listing 5). Sie prüft mit der Hilfsfunktion ExistsForm, ob bereits ein Formular mit dem angegebenen Namen vorhanden ist:

Public Function CreateNewForm(strName As String) As Boolean
      Dim frm As Form
      Dim strNameTemp As String
      If ExistsForm(strName) Then
          If MsgBox("Formular ''" & strName & "'' ist bereits vorhanden. Überschreiben?", vbYesNo + vbExclamation, _
                  "Formular überschreiben") = vbYes Then
              If DeleteForm(strName) = False Then
                  Exit Function
              End If
          Else
              Exit Function
          End If
      End If
      Set frm = CreateForm()
      strNameTemp = frm.Name
      frm.Visible = True
      DoCmd.Close acForm, frm.Name, acSaveYes
      DoCmd.Rename strName, acForm, strNameTemp
      CreateNewForm = True
End Function

Listing 5: Funktion zum Erstellen von Formularen

Public Function ExistsForm(strForm As String) As Boolean
      Dim objForm As AccessObject
      For Each objForm In CurrentProject.AllForms
          If objForm.Name = strForm Then
              ExistsForm = True
              Exit Function
          End If
      Next objForm
End Function

Falls ja, fragt die Funktion, ob das Formular überschrieben werden soll. Antwortet man mit Ja, versucht die Routine, das Formular mit einer weiteren Funktion namens DeleteForm zu löschen:

Public Function DeleteForm(strName As String) As Boolean
      On Error Resume Next
      DoCmd.DeleteObject acForm, strName
      If Err.Number = 0 Then
          DeleteForm = True
      Else
          MsgBox Err.Description, vbCritical + vbOKOnly, _
              "Löschen fehlgeschlagen"
      End If
End Function

Gelingt dies nicht, wird die Funktion verlassen und liefert den Wert False zurück. Auch wenn man auf die Frage, ob ein bestehendes Formular überschrieben werden soll, mit Nein antwortet, endet die Funktion mit dem Rückgabewert False.

Anderenfalls ruft die Funktion die Anweisung CreateForm auf und speichert den Verweis auf das neu erstellte Formular in der Variablen frm.

Sie trägt den von Access vergebenen Namen in die Variable strNameTemp ein. Das Formular wird dann geschlossen und die Funktion ändert noch den Namen vom temporären Namen in den als Parameter übergebenen Namen.

Starten der Formularerstellung

Die eigentliche Formularerstellung mit dem Aufruf der zuvor beschriebenen Funktionen erfolgt, wenn der Benutzer die Schaltfläche Erstellen anklickt. Für die Ereignisprozedur Beim Klicken hinterlegen wir die Prozedur aus Listing 6.

Private Sub cmdCreate_Click()
     Dim frm As Form
     RunCommand acCmdSaveRecord
     If CreateNewForm(Me!txtUnterformularname) = True Then
         DoCmd.OpenForm Me!txtUnterformularname, acDesign
         Set frm = Forms(Me!txtUnterformularname)
         With frm
             .DefaultView = acDefViewDatasheet
             .RecordSource = Me!cboDatasource
             .HasModule = True
             AddControlsOverviewSubform Me!OverviewformID
         End With
     End If
     ...
End Sub

Listing 6: Prozedur zum Erstellen der Formulare, hier zunächst für das Unterformular

Diese speichert zunächst den aktuell im Formular angezeigten Datensatz. Nach dem Aufruf der weiter oben bereits beschriebenen Funktion CreateForm sollte das angehende Unterformular nun angelegt sein.

Dieses öffnen wir nun mit der DoCmd.OpenForm-Methode und referenzieren es mit der Objektvariablen frm. Nun stellen wir einige Eigenschaften für dieses Formular ein:

  • DefaultView: Legt mit acDefViewDatasheet fest, dass das Formular in der Datenblattansicht angezeigt werden soll.
  • RecordSource: Wird auf die aus Me!cboDatasource ausgelesene Datensatzquelle, also die Tabelle oder Abfrage, eingestellt.
  • HasModule: Legt mit dem Wert True fest, dass das Unterformular ein Klassenmodul enthalten soll.

Außerdem rufen wir für dieses Formular die Prozedur AddControlsOverviewSubform auf. Dieser Prozedur übergeben wir den Primärschlüsselwert des Datensatzes der Tabelle tblOverviewforms, für den wir die Steuerelemente hinzufügen wollen.

Steuerelemente zum Unterformular hinzufügen

Die Prozedur AddControlsOverviewSubform unterstützt uns beim Hinzufügen der Steuerelemente zum Unterformular, die in der Tabelle tblOverviewfields festgelegt wurden (siehe Listing 7).

Public Sub AddControlsOverviewSubform(lngOverviewformID As Long)
     Dim dbc As DAO.Database
     Dim rstControls As DAO.Recordset
     Dim rstForm As DAO.Recordset
     Dim strFormName As String
     Dim ctl As Control
     Dim lngMaxWidth As Long
     Dim lngLabelWidth As Long
     Dim lngTop As Long
     Set dbc = CodeDb
     Set rstForm = dbc.OpenRecordset("SELECT * FROM tblOverviewforms WHERE OverviewformID = " & lngOverviewformID, _
         dbOpenDynaset)
     strFormName = rstForm!Subformname
     Set dbc = CodeDb
     Set rstControls = dbc.OpenRecordset("SELECT * FROM tblOverviewfields WHERE OverviewformID = " _
         & lngOverviewformID, dbOpenDynaset)
     lngLabelWidth = GetMaxLabelWidth(rstControls)
     lngTop = rstForm!Margin
     Do While Not rstControls.EOF
         If rstControls!IgnoreField = False Then
             Select Case rstControls!ControlType
                 Case acTextBox
                     Set ctl = AddTextBox(rstForm, rstControls, strFormName, acDetail, lngLabelWidth, lngTop, _
                         lngMaxWidth)
                 Case acComboBox
                     Set ctl = AddComboBox(rstForm, rstControls, strFormName, acDetail, lngLabelWidth, lngTop, _
                         lngMaxWidth)
                 Case acCheckBox
                     Set ctl = AddCheckBox(rstForm, rstControls, strFormName, acDetail, lngLabelWidth, lngTop, _
                         lngMaxWidth)
                 Case Else
                     Set ctl = AddTextBox(rstForm, rstControls, strFormName, acDetail, lngLabelWidth, lngTop, lngMaxWidth)
             End Select
             lngTop = lngTop + ctl.Height + rstForm!Margin
             ctl.ColumnWidth = rstControls!FieldWidth
         End If
         rstControls.MoveNext
     Loop
End Sub

Listing 7: Prozedur zum Hinzufügen der Steuerelemente zum Unterformular

Sie erwartet den Primärschlüsselwert des Eintrags der Tabelle tblOverviewforms und weitere Parameter. Sie referenziert mit dem Recordset rstForm die Tabelle tblOverviewforms mit dem Hauptdatensatz für das zu erstellende Formular und ermittelt daraus den Namen des zu erstellenden Unterformulars.

Dann referenziert sie die zu diesem Formular gehörenden Datensätze der Tabelle tblOverviewfields mit einem weiteren Recordset namens rstControls.

Auch wenn dies in diesem Unterformular in der Datenblattansicht nicht unbedingt nötig ist, holt die Prozedur mit der Funktion GetMaxLabelWidth die maximale Breite für die hinzuzufügenden Steuerelemente (siehe Listing 8). In dieser Funktion ermitteln wir mithilfe der Wizhook-Klasse die Breiten für die Beschriftungen der abzubildenden Felder. Dabei nehmen wir ein Vorlagenformular zu Hilfe, das ein entsprechendes Label-Feld enthält, von dem wir die Schriftarteigenschaften für die Berechnung der Länge der Texte heranziehen. Die Funktion durchläuft die Beschriftungen und ermittelt jeweils die Breite des Textes für die angegebenen Schrifteigenschaften. Ist die Breite für einen Text breiter als die bisher breiteste, wird dieser als der aktuell breiteste Wert gespeichert. Auf diese Weise erhalten wir am Ende den größten Wert für die Breite der Beschriftungen. Nachdem diese Breite ermittelt wurde, holen wir aus dem Feld Margin den Abstand zum oberen Rand und legen diesen als lngTop fest. Dann durchläuft die Prozedur alle Steuerelemente der Tabelle tblOverviewfields und prüft zuerst anhand der Eigenschaft IgnoreField, ob überhaupt ein Steuerelement für dieses Feld angelegt werden soll. Falls ja, prüfen wir in einer Select Case-Bedingung, welches Steuerelement angelegt werden soll. Auch, wenn es sich um ein Unterformular in der Datenblattansicht handelt, können neben Textfeldern auch Kombinationsfelder und Kontrollkästchen auftauchen.

Public Function GetMaxLabelWidth(rst As DAO.Recordset)
     Dim frm As Form
     Dim lbl As Label
     Dim lngBold As Long
     Dim lngWidthText As Long
     Dim lngHeight As Long
     Dim lngWidthMax As Long
     DoCmd.OpenForm "frmFontStyles"
     Set frm = Forms!frmFontStyles
     Set lbl = frm!lblFontStyle
     WizHook.Key = 51488399
     Do While Not rst.EOF
         With lbl
             If .FontBold Then
                 lngBold = 700
             Else
                 lngBold = 400
             End If
             WizHook.TwipsFromFont .FontName, .FontSize, lngBold, .FontItalic, .FontUnderline, 0, _
                 rst!Fieldlabel & ":", 0, lngWidthText, lngHeight
         End With
         If lngWidthText > lngWidthMax Then
             lngWidthMax = lngWidthText
         End If
         rst.MoveNext
     Loop
     rst.MoveFirst
     GetMaxLabelWidth = lngWidthMax
     DoCmd.Close acForm, "frmFontStyles"
End Function

Listing 8: Funktion zum Ermitteln der Breite der breitesten Beschriftung

Textfelder anlegen

Im Falle von acTextBox rufen wir die Funktion AddTextBox auf (siehe Listing 9). Dieser übergeben wir die notwendigen Informationen – das Recordset rstForm, das Recordset rstControls mit den hinzuzufügenden Feldern, den Namen des Formulars, den Bereich, in dem die Steuerelemente eingefügt werden sollen und so weiter.

Public Function AddTextBox(rstForm As DAO.Recordset, rstControls As DAO.Recordset, strFormName As String, _
         lngSection As AcSection, lngLabelWidth As Long, lngTop As Long, lngMaxWidth As Long) As Control
     Dim ctl As Control
     Dim lbl As Label
     Set ctl = CreateControl(strFormName, acTextBox, lngSection, , rstControls!FieldName, lngLabelWidth + 2 * _
         rstForm!Margin, lngTop, rstControls!FieldWidth, Nz(rstControls!FieldHeight, 300))
     Set lbl = CreateControl(strFormName, acLabel, lngSection, ctl.Name, , rstForm!Margin, lngTop, lngLabelWidth, _
         Nz(rstControls!FieldHeight, 300))
     lbl.Caption = rstControls!Fieldlabel & ":"
     If ctl.Left + ctl.Width > lngMaxWidth Then
         lngMaxWidth = ctl.Left + ctl.Width
     End If
     Set AddTextBox = ctl
End Function

Listing 9: Funktion zum Hinzufügen einer Textbox

Die Routine verwendet die Funktion CreateControl, um dem Formular das Formular mit den entsprechenden Eigenschaften hinzuzufügen. Zusätzlich zum Textbox-Steuerelement wird auch noch ein Label-Steuerelement erstellt, das später in der Datenblattansicht die Spaltenüberschrift beisteuert. Außerdem legt die Routine noch die Beschriftung des Label-Steuerelements fest und stellt die Position der Steuerelemente fest, damit diese untereinander eingefügt werden.

Kontrollkästchen anlegen

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