Formulare generieren

Lies diesen Artikel und viele weitere mit einem kostenlosen, einwöchigen Testzugang.

Für Einsteiger sind die Formular-Assistenten von Access sicher eine hilfreiche Einrichtung. Fortgeschrittene Entwickler verwenden diese jedoch eher selten – sie haben eine individuell verschiedene Vorgehensweise, diese zu erstellen, mit Datenherkunft und Steuerelementen zu befüllen und Eigenschaften und Ereignisse festzulegen. Wenn man aber immer wieder ähnliche Formulare benötigt, ist das ständige Neuerstellen vergeudete Zeit: Per Copy and Paste oder auch mit ein paar Zeilen Code gelingt dies doch deutlich schneller.

Die schnellste Möglichkeit, ein Formular zu erstellen, dass man bereits in ganz ähnlicher Weise produziert hat, ist das Kopieren dieses Formulars – zumindest, wenn es sich in der gleichen Datenbank befindet. Sollten Sie es für eine andere Anwendung erstellt haben, können Sie es über die Import-Funktion von Access ebenfalls schnell hinzufügen.

Es gibt aber noch eine Alternative: Warum programmieren Sie sich nicht eine kleine Funktion, die Ihnen immer wiederkehrende Schritte beim Erstellen von Formularen abnimmt In diesem Beitrag stellen wir Ihnen die notwendigen Grundlagen für das Erstellen von Formularen und Steuerelementen samt Eigenschaften vor.

Ein anderer Grund für das dynamische Erstellen eines Formulars wäre ein Unterformular, dessen Felder erst zur Laufzeit bekannt sind. Ein gutes Beispiel dafür ist die Lösung zum Beitrag Seriendruck mit Access und Word (www.access-im-unternehmen.de/). Hier können wir zwar direkt eine Tabelle oder Abfrage als Datenherkunft zuweisen (unter VBA etwa mit dem Ausdruck Me.sfm.SourceObject = "Table.tblBeispiel" beziehungsweise "Tabelle.tblBeispiel" in älteren Access-Versionen). Wenn das Unterformular aber auch noch die für ein Formular üblichen Ereignisse bereitstellen soll, um etwa auf änderungen der enthaltenen Daten reagieren zu können, ist das Erstellen eines Formulars zur Laufzeit eine Alternative.

Das Anlegen von Ereignisprozeduren für zur Laufzeit erstellte Formulare demonstrieren wir in einem späteren Beitrag.

Formulare erstellen

Das reine Erstellen eines Formulars geht ganz einfach von der Hand und kann gleich im Direktfenster des VBA-Editors geschehen: Die Application-Methode CreateForm erzeugt im Handumdrehen ein Formular, ganz so als ob Sie es über den entsprechenden Menüeintrag erledigt hätten.

Unter Access 2003 oder unter der Dokumentfensteroption namens Überlappende Fenster unter Access 2007 erscheint das Formular allerdings lediglich in Form seiner Titelleiste, was dem Modus Minimiert entspricht. Das lässt sich allerdings leicht mit folgender Anweisung ändern:

DoCmd.Restore

Anschließend können wir das Formular nach Bedarf speichern. Dies gelingt nur unter Angabe des Formularnamens, der allerdings beim Aufruf der CreateForm-Methode dynamisch erzeugt wird (in der deutschen Version nacheinander Formular1, Formular2, …).

Um wirklich das richtige Formular zu speichern, speichern wir gleich beim Anlegen einen Verweis auf das neue Formular in einer Objektvariablen namens frm. Dies alles geschieht bereits in einer kleinen Prozedur, die wir anschließend Schritt für Schritt erweitern:

Public Sub FormularErstellen()
Dim frm As Form
Set frm = CreateForm
DoCmd.Restore
DoCmd.Save acForm, frm.Name
End Sub

Wenn Sie das so gespeicherte Formular umbenennen möchten, verwenden Sie die Anweisung DoCmd.Rename. Diese setzt allerdings voraus, dass das zu ändernde Formular geschlossen ist.Wenn das Formular geschlossen ist, haben wir aber keinen Zugriff mehr auf die Objektvariable frm, um den aktuellen Namen zu ermitteln. Also speichern wir diesen zuvor in der Variablen strForm zwischen, schließen das Formular und benennen es erst dann um:

Dim frm As Form
Dim strFormTemp As String
Set frm = CreateForm
strFormTemp = frm.Name
DoCmd.Restore
DoCmd.Save acForm, strFormTemp
DoCmd.Close acForm, strFormTemp
DoCmd.Rename "frmBeispiel", acForm, strFormTemp

Das Ergebnis dieser acht Zeilen ist ein neues Formular mit dem gewünschten Namen. Um die Prozedur dynamischer zu gestalten, übergeben Sie den gewünschten Namen (hier noch frmBeispiel) mit einem Parameter:

Public Sub FormularErstellen(strFormname As String)
...
DoCmd.Rename strFormName, acForm, strFormTemp
End Sub

Steuerelemente zum Formular hinzufügen

Steuerelemente fügen Sie einem Formular in der Entwurfsansicht mit der CreateControl-Methode des Application-Objekts hinzu.

Diese erwartet die folgenden Parameter:

  • Formname: Name des Formulars, in dem das Steuerelement erstellt werden soll
  • ControlType: Konstante, die den Steuerelementtyp angibt, beispielsweise acTextBox, acComboBox oder acCheckBox
  • Section: Bereich des Formulars, in dem das Steuerelement angelegt werden soll, beispielsweise acDetail für den Detailbereich
  • Parent: Übergeordnetes Steuerelement. Eigentlich nur wichtig, wenn man ein Bezeichnungsfeld an ein anderes Steuerelement wie etwa ein Textfeld binden will (optional).
  • Column: Name des Feldes der Datenherkunft, an welches das Steuerelement gebunden werden soll (optional)
  • Left: Abstand vom linken Formularrand in Twips (optional)
  • Top: Abstand vom oberen Rand des Zielbereichs in Twips (optional)
  • Width: Breite in Twips (optional)
  • Height: Höhe in Twips (optional)

Wenn Sie ein Steuerelement mit der CreateControl-Methode anlegen, können Sie einen Verweis darauf in einer entsprechenden Variablen speichern:

Dim ctl As Control
Set ctl = Application.CreateControl("frmTest", acTextBox, acDetail, ...)

Der Hintergrund ist, dass Sie so noch weitere Einstellungen vornehmen können – etwa um bei Kombinationsfeldern die Nachschlagefeld-Eigenschaften aus der Felddefinition auszulesen und abzubilden.

Formular mit Daten füllen und in der Datenblattansicht anzeigen

Manche Entwickler arbeiten in Unterformularen sehr viel mit der Datenblattansicht. Sie zeigt die Daten übersichtlich an, ermöglicht das Bearbeiten und auch das Sortieren oder Filtern.

Bauen wir also eine Prozedur, die ein mit der vorherigen Prozedur erstelltes Formular so umwandelt, dass es die gewünschte Datenherkunft erhält und die Felder der Datenherkunft anzeigt.

Diese Prozedur verwendet zwei Parameter: den Namen des Formulars und den Namen der zu verwendenden Datenherkunft beziehungsweise den entsprechenden SQL-Ausdruck. Vor dem Aufruf dieser Funktion sollten Sie bereits ein leeres Formular mit der Prozedur FormularErstellen erzeugt haben.

Die folgenden Zeilen sorgen außerdem dafür, dass ein eventuell bereits bestehendes Formular gelöscht und die richtigen Parameter übergeben werden:

Dim strFormname As String
Dim strRecordsource As String
strFormname = "frmBeispiel"
strRecordsource = "SELECT * FROM tblAdressen"
On Error Resume Next
DoCmd.DeleteObject acForm, strFormname
On Error GoTo 0
FormularErstellen strFormname
DatenblattEinrichten strFormname, strRecordsource

Die Prozedur DatenblattEinrichten öffnet das mit dem Parameter strFormname benannte Formular in der Entwurfsansicht und füllt die Objektvariable frm mit einem Verweis darauf, um später komfortabel auf seine Eigenschaften zugreifen zu können (s. Listing 1).

Listing 1: Füllen eines leeren Formulars mit den Steuerelementen für die Anzeige der Datenblattansicht

Public Sub DatenblattEinrichten(strFormname As String, strRecordsource As String)
    ''...Deklaration
    DoCmd.OpenForm strFormname, acDesign
    Set frm = Forms(strFormname)
    Set db = CurrentDb
    Set rst = db.OpenRecordset(strRecordsource, dbOpenDynaset)
    For Each fld In rst.Fields
         intControlType = acTextBox
        On Error Resume Next
        intControlType = fld.Properties("DisplayControl")
        On Error GoTo 0
        Set ctl = Application.CreateControl(strFormname, intControlType, acDetail)
        ctl.ControlSource = fld.SourceField
        Select Case intControlType
            Case acComboBox
                Set cbo = ctl
                With cbo
                    .RowSource = fld.Properties("RowSource")
                    .ColumnCount = fld.Properties("ColumnCount")
                    .ColumnWidths = fld.Properties("ColumnWidths")
                End With
        End Select
        Set lbl = Application.CreateControl(strFormname, acLabel, acDetail, ctl.Name)
        lbl.Caption = fld.Name
        On Error Resume Next
        lbl.Caption = fld.Properties("Caption")
        On Error GoTo 0
    Next fld
    frm.RecordSource = strRecordsource
    frm.DefaultView = acDefViewDatasheet
    DoCmd.Close acForm, frm.Name, acSaveYes
End Sub

Danach wird ein Recordset-Objekt mit den Datensätzen der mit dem Parameter strRecordsource angegebenen Tabelle oder Abfrage gefüllt. Dieses benötigen wir, um die Felder der Datenherkunft zu durchlaufen und die entsprechenden Steuerelemente im Formular anzulegen.

Man könnte die Felder zwar auch über ein TableDef– beziehungsweise ein QueryDef-Objekt auslesen, aber dazu müsste man noch prüfen, ob es sich bei dem in strRecordsource angegebenen Objekt um eine Tabelle oder Abfrage handelt.

Dies sparen wir uns aus Bequemlichkeit und weil wir wissen, dass das Erstellen von Formularen wohl nicht zur Laufzeit geschehen wird und somit nicht wichtig für die Performance ist (TableDef und QueryDef enthalten nur die Definition, Recordset enthält auch Daten und könnte ein wenig mehr Zeit zum Füllen beanspruchen).

Beim Durchlaufen der einzelnen Felder der Datenherkunft über die Fields-Auflistung geschehen folgende Schritte:

  • Ein Tabellenfeld kann unter anderem als Textfeld, als Kombinationsfeld oder als Kontrollkästchen angezeigt werden. Die Variable intControlType speichert den Typ, wobei diese zuerst mit dem Standardwert acTextBox gefüllt wird. Wenn das Feld nicht als Textfeld angezeigt wird, enthält die als Property gespeicherte Eigenschaft DisplayControl die Konstante für das entsprechende Steuerelement, beispielsweise acComboBox oder acCheckBox. Dieser Wert landet dann gegebenenfalls statt acTextBox in der Variablen intControlType.
  • Die CreateControl-Methode erzeugt im Detailbereich des angegebenen Formulars ein Steuerelement des soeben ermittelten Typs und speichert einen Verweis darauf in der Variablen ctl.
  • Die Eigenschaft ControlSource (im Eigenschaftsfenster Steuerelementinhalt) erhält als Wert den Inhalt der Eigenschaft SourceField des Field-Objekts.
  • Anschließend untersucht ein Select Case-Konstrukt, ob es sich bei dem Steuerelement um ein Kombinationsfeld handelt, und stellt weitere Eigenschaften auf die im Originalfeld voreingestellten Werte ein. Dabei handelt es sich um die Datensatzherkunft (RowSource), die Spaltenanzahl (ColumnCount) und die Spaltenbreiten (ColumnWidths).
  • Schließlich wird ein passendes Bezeichnungsfeld für jedes Steuerelement erzeugt. Dieses erhält als Beschriftung zunächst den Namen des Feldes. Falls der Entwickler mit der Eigenschaft Beschriftung eine alternative Beschriftung hinterlegt hat, besitzt das Feld eine Property namens Caption. Ist diese vorhanden, wird das Bezeichnungsfeld damit betitelt.
  • Schließlich wird die Datenherkunft des Formulars auf den mit strRecordsource übergebenen Ausdruck und die Standardansicht auf Datenblatt eingestellt.
  • Im letzten Schritt wird das Formular geschlossen und gespeichert.

Wenn Sie das Formular auf Basis der Tabelle tblAdressen der Beispieldatenbank erstellt haben, sieht das Formular nach dem Öffnen wie in Bild 1 aus. Ein Wechsel in die Entwurfsansicht liefert allerdings ein überraschendes Bild: Dort befindet sich offensichtlich nur ein einziges Steuerelement (siehe Bild 2)! Doch das ist nicht der Fall, denn die Prozedur hat lediglich alle Steuerelemente übereinander angelegt beziehungsweise Größe und Position nach dem Anlegen nicht mehr geändert.

pic001.png

Bild 1: Ein per VBA-Code erstelltes Formular in der Datenblattansicht

pic003.png

Bild 2: Die Entwurfsansicht ist nur schwer bearbeitbar …

Das ist für die Datenblattansicht eigentlich nicht relevant, aber für die spätere Bearbeitung eher unpraktisch – wozu erstellen wir vollautomatisch ein Formular, wenn wir anschließend doch wieder mehr Handgriffe ausführen müssen, um das Formular unseren Wünschen anzupassen

Also erweitern wir die Prozedur noch um einige Zeilen, welche die Steuerelemente übersichtlich anordnen – etwa so wie in Bild 3. Diese Abbildung berücksichtigt gleichzeitig eine Einstellung der Größe entsprechend des für die angezeigten Daten benötigten Platzes. Außerdem sollen auch die Spaltenbreiten der Datenblattansicht entsprechend den bereits in der Datenherkunft enthaltenen Daten optimiert werden.

pic002.png

Bild 3: So sollte das Formular im Entwurf aussehen.

Formulare für die Formularansicht erstellen

Nachdem wir die Datenblattansicht abgedeckt haben (wenn auch mit einer etwas komprimierten Entwurfsansicht), widmen wir uns einer etwas komplizierteren Aufgabe: Dem Erstellen eines Formulars, das auch in der Entwurfs- und somit in der Formularansicht ein akzeptables Bild abgibt. Dabei wollen wir folgende Voraussetzungen erfüllen:

  • Jedes Steuerelement soll jeweils ein Bezeichnungsfeld besitzen.
  • Die Steuerelemente sollen mit ein paar Pixeln Abstand untereinander angeordnet werden.
  • Die Breite der Bezeichnungsfelder soll sich nach dem breitesten Bezeichnungsfeld richten.
  • Die zu den Bezeichnungsfeldern gehörenden Steuerelemente sollen jeweils rechts daneben angeordnet werden.
  • Die Breite von Textfeldern, Kombinationsfeldern und Listenfeldern wird dem breitesten enthaltenen Text angepasst (plus ein paar Prozent für folgende Einträge).
  • Wenn ein Feld noch keine Daten enthält, soll eine per Parameter zu definierende Mindestbreite verwendet werden.
  • Die Vorgaben für Memofelder sind schwer zu definieren, weil diese ja auch mehrzeilig sein können. Eine Idee wäre, die Breite dem breitesten der übrigen Text-Steuerelemente anzupassen und eine Standardhöhe von vier Zeilen festzulegen.
  • Kombinationsfelder sollen gegebenenfalls mit den in der Felddefinition festgelegten Nachschlagefeld-Eigenschaften ausgestattet werden.
  • Die Steuerelemente sollen ordnungsgemäß mit Präfixen versehen werden, also beispielsweise txtText für ein Textfeld!

Prozedur zum Erstellen von Formularen mit passenden Steuerelementbreiten

Die Prozedur FormularansichtEinrichten (s. Listing 2) hat viel mehr zu erledigen als die Prozedur zum Erstellen eines Formulars in der Datenblattansicht. Nach dem Deklarationsteil öffnet diese zunächst das leere, mit der weiter oben vorgestellten Prozedur FormularErstellen erstellte Formular in der Entwurfsansicht und verweist mit der Form-Objektvariablen frm darauf. Dann füllt es die Variable db mit einem Verweis auf das aktuelle Database-Objekt und erzeugt eine Datensatzgruppe mit den im Formular anzuzeigenden Daten.

Listing 2: Anlegen eines neuen Formulars in der Formularansicht

Public Sub FormularansichtEinrichten(strFormname As String, strRecordsource As String)
    ''... Deklaration
    DoCmd.OpenForm strFormname, acDesign
    Set frm = Forms(strFormname)
    Set db = CurrentDb
    Set rst = db.OpenRecordset(strRecordsource, dbOpenDynaset)
    Call GetSizeAllLabels(rst, frm.DefaultControl(acTextBox), lngLabelX, lngLabelY)
    sngTop = 100
    For Each fld In rst.Fields
         intControlType = acTextBox
        On Error Resume Next
        intControlType = fld.Properties("DisplayControl")
        On Error GoTo 0
        Set ctl = Application.CreateControl(strFormname, intControlType, acDetail, , , lngLabelX _
        + 300, sngTop)
        Call GetMaximalSizeControl(db, strRecordsource, ctl, fld, lngControlX, lngControlY)
        intFieldsize = 0
        On Error Resume Next
        intFieldsize = fld.FieldSize
        If Err.Number = 0 Then ''Memo
            lngControlX = lngControlX / 4
            lngControlY = lngControlY * 4
        End If
        On Error GoTo 0
        ctl.Width = lngControlX
        ctl.Height = lngControlY
        ctl.ControlSource = fld.SourceField
        Select Case intControlType
            Case acComboBox
                ctl.RowSource = fld.Properties("RowSource")
                ctl.ColumnCount = fld.Properties("ColumnCount")
                ctl.ColumnWidths = fld.Properties("ColumnWidths")
                ctl.Name = "cbo" & fld.Name
            Case acListBox
                ctl.RowSource = fld.Properties("RowSource")
                ctl.ColumnCount = fld.Properties("ColumnCount")
                ctl.ColumnWidths = fld.Properties("ColumnWidths")
                ctl.Name = "lst" & fld.Name
            Case acCheckBox
                ctl.Name = "chk" & fld.Name
            Case acTextBox
                ctl.Name = "txt" & fld.Name
        End Select
        Call BuildLabel(frm, fld, ctl, intControlType, lngLabelX, lngLabelY, sngTop)
        sngTop = sngTop + ctl.Height + 40 ''für Abstand
    Next fld
    frm.RecordSource = strRecordsource
    frm.DefaultView = acDefViewSingle
    DoCmd.Close acForm, frm.Name, acSaveYes
End Sub

Bevor auch nur ein einziges Steuerelement angelegt werden kann, brauchen wir den Abstand der gebundenen Steuerelemente vom rechten Rand. Dieser hängt maßgeblich von der Breite der Beschriftungsfelder ab, die sich wiederum am enthaltenen Text in der verwendeten Schriftart, Schriftgröße und weiteren Schrifteigenschaften orientiert. Die maximale Breite der Beschriftungstexte liefert die Funktion GetSizeAllLabels, die wir weiter unten beschreiben, mit dem Parameter lngLabelX.

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

Testzugang

eine Woche kostenlosen Zugriff auf diesen und mehr als 1.000 weitere Artikel

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar