Access-FAQ: Rund um Formulare

Karl Donaubauer, Wien

In der Access-FAQ von Karl Donaubauer (www.donkarl.com) finden Sie die meistgestellten Fragen und Anworten zum Thema Microsoft Access. In dieser Beitragsreihe stellt Karl Donaubauer die wichtigsten Einträge im Detail vor und zeigt Ihnen entsprechende Lösungen anhand praxisnaher Beispiele. Im siebten Teil lernen Sie die Lösungen zu den meistgenannten Problemen der Teilnehmer der deutschsprachigen Access-Newsgroups im Zusammenhang mit Formularen und Unterformularen kennen.

In vielen Situationen ist es notwendig zu wissen, ob ein bestimmtes Formular geöffnet ist, zum Beispiel damit ein Requery-Befehl oder ein anderer Bezug auf das Formular nicht zu einem Fehler führen kann. Es gibt etliche Methoden, um den Zustand eines Formulars zu prüfen. Eine ist das Durchlaufen der Forms-Auflistung, in der ja immer nur alle aktuell geöffneten Formulare stehen:

Dim frm As Form
For Each frm In Application.Forms
    If frm.Name = "FormularName" Then
        ''Aktionen durchführen
    End If
Next frm

Der einzige kleine Nachteil dieser Methode, die in allen Access-Versionen funktioniert, ist eine in der Praxis kaum jemals merkbare Verzögerung, weil alle geöffneten Formulare durchlaufen werden müssen.

Eine andere Variante ist die Verwendung der Funktion SysCmd, die bis Access 2.0 noch undokumentiert war und seit Access 95 offiziell zur Verfügung steht. Am besten verwenden Sie dazu die folgende kleine Funktion in einem Standardmodul:

Public Function fctIsFormOpen( _    StrFrmName As String) As Boolean
    fctIsFormOpen =        (SysCmd(acSysCmdGetObjectState, _        acForm, StrFrmName) > 0)
End Function

Hinweis

Auf der Begleit-CD finden Sie Beispiel-Datenbanken im Format von Access 97 und Access 2000 mit den Quellcodes und Beispielen aus diesem Artikel.

Die Funktion liefert True oder False zurück, je nachdem, ob das Formular geöffnet ist oder nicht. Der typische Aufruf dieser Funktion sieht dann so aus:

If fctIsFormOpen("NameDesFormulares") Then ...

Ab Access 2000 hat Microsoft endlich eine Möglichkeit zur Prüfung in die Access-Bibliothek eingebaut. Es handelt sich um die Eigenschaft IsLoaded. Damit können Sie für alle Access-Objekte, also auch für Tabellen, Abfragen und so weiter, prüfen, ob sie aktuell geöffnet sind. Für ein Formular sieht das so aus (in einer Zeile):

Bild 1: Zwei Instanzen desselben Formulars

Dim myCopy As Form
Private Sub btnNeueInstanz_Click()
    Dim rs As DAO.Recordset
    Set myCopy = New Form_frmInstanzen
    Set rs = myCopy.RecordsetClone
    With myCopy
        .Visible = True
        .Caption = .Caption & " - Kopie"
        DoCmd.MoveSize 2000, 2000
        If Not IsNull(Me!cboAuswahl) Then
            rs.FindFirst "ArtikelId = " & Me!cboAuswahl
            If Not rs.NoMatch Then
              .Bookmark = rs.Bookmark
            End If
        End If
    End With
    Set rs = Nothing
End Sub

Quellcode 1

CurrentProject.AllForms("FormularName").IsLoaded

Im Gegensatz zur oben angeführten Prüfung mit der selbst erstellten Funktion liefert die Eigenschaft IsLoaded drei mögliche Ergebnisse. True, wenn das Objekt geöffnet ist, False, wenn es nicht geöffnet ist, und als dritte Variante den Laufzeitfehler 2467, wenn ein nicht vorhandener Objektname verwendet wurde. Den Fehler können Sie natürlich abfangen, aber das bedeutet immerhin etwas Mehrarbeit.

Ab der Version 95 bietet Access die Möglichkeit, mehrere Instanzen desselben Formulars zu öffnen. Das typische Anwendungsbeispiel ist das Anzeigen mehrerer Datensätze neben- oder übereinander (siehe Bild 1).

In der Beispiel-Datenbank zu diesem Artikel finden Sie das Formular frmInstanzen, das den nötigen Code und die praktische Anwendung demonstriert (s. Quellcode 1).

Im Kombinationsfeld des Beispielformulars können Sie einen anderen Artikel auswählen, der dann beim Klicken der Befehlsschaltfläche in einer neuen Formularinstanz angezeigt wird.

Von “Instanzen” spricht man in diesem Zusammenhang schon deshalb, weil es sich um eine neue Instanz der Klasse des Formulars handelt. Das ist eine der vielen Gelegenheiten, bei denen man Access beziehungsweise VBA die Objektorientierung ansieht.

Die wesentlichen Teile des Codes sind dementsprechend objektorientiert. Zuerst erfolgt die Deklaration eines Formular-Objekts im Deklarationsbereich des Formular-Klassenmoduls mit

Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long) As Long
Const SM_CXSCREEN = 0
Const SM_CYSCREEN = 1
Public Function fctAufloesung()
    fctAufloesung = "Pixel horizontal: " & GetSystemMetrics(SM_CXSCREEN) _
        & "  vertikal: " & GetSystemMetrics(SM_CYSCREEN)
End Function

Quellcode 2

Dim myCopy As Form

Hier könnte man auch den Namen des speziellen Formulars verwenden, also

Dim myCopy As Form_frmInstanzen

Im Ereigniscode Beim Klicken der Schaltfläche wird dann die Klasse instanziiert mit

Set myCopy = New Form_frmInstanzen

Die neue Formularinstanz ist vorläufig noch nicht sichtbar. Das geschieht erst mit der Codezeile

.Visible = True

Der restliche Code sorgt dafür, dass die neue Instanz nicht genau über der ersten geöffnet wird und dass ein anderer Datensatz angezeigt wird, sofern im Kombinationsfeld einer ausgewählt wurde.

Wenn Sie in der neuen Instanz wieder auf die Schaltfläche klicken, wird die nächste Instanz erzeugt. Sie liegt jedoch genau über der aufrufenden. Deshalb müssen Sie das Fenster etwas verschieben, um den neuesten Klon zu sehen.

Eine alte Schwäche von Access ist, dass sich seine Formulare nicht von selbst dynamisch an die Größe des Bildschirmes anpassen. Die meisten Entwickler gehen daher den Weg, eine Standardauflösung für ihre Anwendung vorzusehen, beziehungsweise beim Anwender vorzuschreiben. Dennoch geschieht es in der Praxis oft, dass ein Anwender mit einer anderen Auflösung arbeitet und dann die Formulare entweder zu groß oder zu klein erscheinen.

Möchten Sie Ihre Anwendung an verschiedene Auflösungen anpassen, so ist der erste Schritt das Ermitteln der aktuellen Bildschirmauflösung. Mithilfe der API-Funktion GetSystemMetrics ist das keine große Sache (s. Quellcode 2).

Die kleine Beispielfunktion fctAufloesung in Quellcode 2 zeigt auch gleich beispielhaft den Aufruf der API-Funktion. GetSystemMetrics(SM_CXSCREEN) liefert die Breite des Bildschirmes in Pixeln, GetSystemMetrics(SM_CYSCREEN) liefert die Höhe.

Der schwierigere Teil der Arbeit ist die Größenanpassung der Formulare. Falls sie eine unbedingte Vorgabe ist und Sie nicht viel Zeit oder Mühe investieren möchten, gibt es einen einfachen Workaround, um zumindest zwei oder drei Standardauflösungen zu bedienen: Fertigen Sie für jede zu unterstützende Auflösung eine angepasste Kopie der Formulare an.

Ermitteln Sie beim Programmstart die Werte für die Breite und Höhe des Bildschirms und schreiben Sie sie in eine Variable. In der Anwendung rufen Sie dann die jeweils passende Kopie der Formulare auf. Ein Nachteil dieser Methode ist natürlich, dass bei Entwurfsänderungen auch die Kopien gepflegt werden müssen.

Type Rect
    x1 As Long
    y1 As Long
    x2 As Long
    y2 As Long
End Type
Declare Function GetWindowRect Lib "user32" (ByVal hWnd As Long, lpRect As Rect) _    As Long
Declare Function IsZoomed Lib "user32" (ByVal hWnd As Long) As Long
Declare Function ShowWindow Lib "user32" (ByVal hWnd As Long, _    ByVal nCmdShow As Long) As Long
Declare Function MoveWindow Lib "user32" (ByVal hWnd As Long, ByVal X As Long, _    ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, _    ByVal bRepaint As Long) As Long
Declare Function GetParent Lib "user32" (ByVal hWnd As Long) As Long
Declare Function GetClientRect Lib "user32" (ByVal hWnd As Long, _    lpRect As Rect) As Long
Public Const SW_MAXIMIZE = 3
Public Const SW_SHOWNORMAL = 1
Sub MaximizeRestoredForm(F As Form)
    Dim MDIRect As Rect
    If IsZoomed(F.hWnd) <> 0 Then
        ShowWindow F.hWnd, SW_SHOWNORMAL
    End If
    GetClientRect GetParent(F.hWnd), MDIRect
    MoveWindow F.hWnd, 0, 0, MDIRect.x2 - MDIRect.x1, MDIRect.y2 - MDIRect.y1, True
End Sub

Quellcode 3

Die wirklich dynamische Anpassung der Formulare an die Bildschirmauflösung ist hingegen ein technisch sehr aufwändiges Unterfangen und erfordert viel Programmcode. Es müssen ja alle Steuerelemente, Beschriftungen, Schriftgrößen, grafischen Elemente und so weiter an die jeweilige Auflösung angepasst werden.

Der Code hierfür würde den Rahmen dieses Artikels sprengen. Es gibt entsprechenden Code in Büchern und auf Webseiten, sowohl gratis als auch käuflich, die sich über eine Websuche finden lassen. Sogar bei kommerziellen Tools kommt es aber in manchen Details bei den Proportionen oder Schriftgrößen zu Problemen.

Der Code, um ein Formular zu maximieren, wiederherzustellen oder zu minimieren ist einfach und weithin bekannt:

DoCmd.Maximize
DoCmd.Restore
DoCmd.Minimize

Dieser Code, vor allem jener zum Maximieren, steht in sehr vielen Lade- oder öffnen-Ereignissen von Access-Anwendungen.

Nun sind Formulare so genannte Kind-Fenster des Access-Hauptfensters. Das hat zur Folge, dass, wenn Sie ein Formular maximieren, wiederherstellen oder minimieren, diese Einstellung auch für alle anderen Formulare gilt, genauso wie bei Dokumenten in Word oder Arbeitsmappen in Excel.

Bild 2: Maximiertes und nicht maximiertes Formular

Bei Access-Anwendungen ist das oft unerwünscht, insbesondere das Maximieren aller Fenster. Es gibt jedoch keine eingebaute Möglichkeit, um Formular-Fenster individuell zu maximieren. Hier hilft nur API-Code, wie er etwa von Microsoft im Knowledgebase-Artikel 210299 angeboten wird (s. Quellcode 3).

Ich habe den Code des Knowledgebase-Artikels in ein paar wenigen Punkten verändert. Die Hinweise dazu kamen vom englischen Programmierer Terry Kreft.

Sie finden Quellcode 3 auch im Modul basMaxRestoredForm der Beispiel-Datenbank zu diesem Artikel. Ebenfalls enthalten ist das Formular frmMaxRestored, das den praktischen Einsatz zeigt (siehe Bild 2).

Im Ereignis Beim Laden dieses Formulars wird die Prozedur aus Quellcode 3 aufgerufen, um das Formular an die Größe des Access-Fensters anzupassen. Der Aufruf lautet:

MaximizeRestoredForm Me

Das Formular sieht damit genauso aus, als sei es maximiert.

Der große Unterschied und Vorteil ist jedoch, dass weitere Formulare über diesem Formular geöffnet werden können, ohne maximiert zu sein.

Falls Sie Access 97 verwenden, hat diese Methode noch einen positiven Effekt. Access 97 hat einen Bug, der dafür sorgt, dass die Schließen-Schaltfläche eines Formulars im maximierten Zustand erscheint und aktiv ist, auch wenn die Eigenschaft Schließen Schaltfläche auf Nein eingestellt wurde. Diesen Fehler können Sie mit der vorgestellten Methode umgehen.

Folgendes Verhalten von Access hat schon für so manche Verwunderung und Schrecksekunde gesorgt:

Sie entwerfen ein Formular mit verschiedenen Steuerelementen nur im Detailbereich. Wenn Sie das Formular in der Formularansicht öffnen, ist es jedoch völlig leer.

Kein einziges der zuvor erstellten Steuerelemente ist mehr sichtbar, egal um welchen Steuerelementtyp es sich handelt oder ob gebunden oder nicht. Es präsentiert sich nur der leere Detailbereich. Wechseln Sie in die Entwurfsansicht, so sind alle Steuerelemente wieder zu sehen.

Dieses Verhalten ist “by Design”. Die Ursache liegt darin, dass die zugrunde liegende Tabelle oder Abfrage keine Datensätze liefert und die Einstellungen in der Abfrage oder im Formular noch dazu verhindern, dass neue Datensätze angelegt werden können – zum Beispiel, wenn die Eigenschaft Anfügen zulassen des Formulars auf Nein steht.

Bild 3: Die Steuerelemente sind nur in der Entwursansicht vorhanden.

Bild 4: Dialogfenster der bedingten Formatierung

Die Lösung des Problems ist einfach: Sorgen Sie dafür, dass die Datenquelle Datensätze liefert oder dass das Anlegen neuer Datensätze erlaubt ist.

Wenn das nicht möglich oder erwünscht ist, so können Sie sich zumindest damit behelfen, dass Sie eine Schaltfläche zum Schließen oder ein anderes Steuerelement in den Formularkopf oder -fuß platzieren. Steuerelemente in diesen Bereichen werden auf jeden Fall angezeigt.

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