Steuerelemente ausrichten per VBA

Access bietet verschiedene Möglichkeiten, um Steuerelemente auszurichten. Diese stellen wir im Beitrag Steuerelemente ausrichten (www.access-im-unternehmen.de/1431) vor. All diese Methoden haben jedoch Vor- und Nachteile. Der größte Nachteil ist, dass sie Zeit kosten – Zeit, die man in viel schönere Arbeiten investieren könnte. Deshalb schauen wir uns in diesem Beitrag an, wie wir in der Entwurfsansicht selektierte Steuerelemente einfach per VBA ausrichten können. Dabei ist nicht die Technik die entscheidende Frage, sondern die Vorgabe, nach welchen Regeln die Ausrichtung genau erfolgen soll. In diesem Beitrag beschreiben wir, wie man per VBA solche Steuerelemente wie Textfeld, Kombinationsfeld, Listenfeld und Kontrollkästchen und ihre Bezeichnungsfelder, die untereinander angeordnet sind, ausrichten kann.

Ausgangssituation im Formular

Wir gehen davon aus, dass Sie als Entwickler auch mit dem Zusammenstellen von Formularen und dem Layout der enthaltenen Steuerelemente zu tun haben.

In vielen Fällen kann man sich die Aufgabe, Steuerelemente sauber ausgerichtet anzulegen, erleichtern – zum Beispiel durch das Ziehen der gebundenen Felder aus der Feldliste in das Formular.

Aber sobald man der Datensatzquelle des Formulars ein weiteres Feld hinzugefügt hat, das nun auch im Formularentwurf landen soll, muss man seine Position an die der anderen anpassen.

Da ist man mit den im oben genannten Beitrag vorgestellten Methoden durchaus ein paar Sekunden beschäftigt – und dabei handelt es sich um eine Tätigkeit, die man während eines Access-Entwicklerlebens sehr oft wiederholt.

Wir wollen die Ausgangssituation noch ein wenig chaotischer darstellen und gehen während der Entwicklung von Steuerelementen aus, die wie in Bild 1 angeordnet sind.

Ausgangssituation im Formular

Bild 1: Ausgangssituation im Formular

Vorgaben zur Anordnung von Steuerelementen

Wenn man diese nun manuell anordnen würde, wäre das Markieren aller Beschriftungsfelder durch Ausziehen eines geeigneten Rahmens wohl der erste Schritt. Dann würde man aus dem Kontextmenü den Befehl Ausrichten|Linksbündig wählen. Als Nächstes wären dann die Textfelder an der Reihe, die man auf die gleiche Weise linksbündig anordnet.

Dann bringt man diese vielleicht noch auf eine Breite und verschiebt sie so nah an die Beschriftungsfelder heran, dass der Abstand zwischen dem breitesten Beschriftungsfeld und den Textfeldern etwa so groß ausfällt wie der vertikale Abstand zwischen den Textfeldern. Das Ergebnis sieht dann beispielsweise wie in Bild 2 aus.

Fertig ausgerichtete Steuerelemente

Bild 2: Fertig ausgerichtete Steuerelemente

Vorgang per VBA abbilden

Nun wollen wir diese ganzen Schritte per VBA erledigen. Dazu können wir grundsätzlich ähnlich vorgehen. Aber welche Rahmenbedingungen dazu wollen wir festhalten? Als Erstes benötigen wir die Position unseres Blocks vom linken und vom oberen Rand aus betrachtet. Deshalb ermitteln wir die Y-Position des obersten Steuerelements und die X-Position des am weitesten links befindlichen Steuerelements.

Danach würden wir die Position aller Bezeichnungsfelder links an der soeben ermittelten Position ausrichten.

Danach wird es schon spannend – nämlich bei der Positionierung der Steuerelemente von oben nach unten. Die erste Idee war, einfach alle markierten Steuerelemente von oben nach unten zu durchlaufen und dabei dem obersten Steuerelement den soeben ermittelten Abstand vom oberen Rand zu vergeben. Die nachfolgenden Steuerelemente würden wir dann jeweils unter dem zuvor ausgerichteten Steuerelement positionieren – mit einem kleinen zusätzlichen Abstand.

Allerdings durchläuft Access die Steuerelemente über die Controls-Auflistung in der Reihenfolge, die durch die Aktivierreihenfolge vorgegeben wurde. Hier stellt sich die Frage, ob wir die Steuerelemente nach der Aktivierreihenfolge ausrichten wollen oder nach der aktuellen vertikalen Anordnung. Intuitiver scheint es, die Steuerelemente nach der aktuellen Anordnung auszurichten.

Diese müssten wir dann erst einmal ermitteln – und das ist nicht gerade trivial. Unsere Lösung für diese Frage ist, den Steuerelementnamen und die Y-Position in einem zweidimensionalen Array zu speichern und die Daten in diesem Array nach der Y-Position zu sortieren, bevor wir sie in der so festgelegten Reihenfolge ausrichten.

Des Weiteren stellt sich die Frage, ob wir uns bei der Höhe für ein Bezeichnungsfeld-Steuerelement-Paar nach der Höhe des Bezeichnungsfeldes oder des Steuerelements richten. Es kann sowohl vorkommen, dass das Bezeichnungsfeld höher ist als das Steuerelement (bei einer längeren Bezeichnung), aber auch Textfelder kommen gelegentlich mit einer größeren Höhe, beispielsweise um mehrere Zeilen anzuzeigen.

Spätestens wenn Listenfelder ins Spiel kommen, sind diese meist höher als das jeweilige Bezeichnungsfeld.

Die einfachste Lösung für diesen Fall scheint zu sein, das jeweils höhere Element zu berücksichtigen.

Bei den Bezeichnungsfeldern stellt sich die Frage, ob diese bereits die richtige Breite aufweisen. Sicherheitshalber wollen wir die Breite auf den tatsächlich enthaltenen Bezeichnungstext anpassen. Damit ermitteln wir nun die maximale Breite der Bezeichnungsfelder und stellen alle Bezeichnungsfelder auf diese Breite ein. Dies ist eine Vorbereitung auf den Fall, dass der Benutzer seine Bezeichnungsfelder rechtsbündig ausrichten möchte.

Danach richten wir die eigentlichen Steuerelemente, in unserem Beispiel also die Textfelder, entsprechend aus. Dabei nutzen wir die X-Position der Bezeichnungsfelder plus ihre Breite plus wiederum einem kleinen Abstand, wie wir ihn auch bereits für die vertikale Anordnung genutzt haben.

Sonderfall Kontrollkästchen

Während die Höhe von Textfeldern in der Regel an die jeweiligen Bezeichnungsfelder angepasst ist, sind Kontrollkästchen etwas weniger hoch und ihr Abstand vom oberen Rand des Formulars stimmt nicht mit dem des dazugehörigen Bezeichnungsfelds überein. Hier müssen wir eine Spezialbehandlung vornehmen. Wir können das Kontrollkästchen nicht auf der gleichen Y-Position wie das Bezeichnungsfeld platzieren, da es sonst optisch etwas zu weit oben angezeigt wird.

Also bauen wir beim Ausrichten des Kontrollkästchens einen zusätzlichen Abstand von oben ein. Da es normalerweise auch noch weniger hoch als ein Textfeld oder Bezeichnungsfeld ist, müssen wir in diesem Fall die Höhe des Bezeichnungsfeldes als Maßstab für die Ausrichtung des darunter liegenden Steuerelements nutzen.

Zusätzliche Einstellungen

Neben den bereits erwähnten Vorgaben wollen wir dem Benutzer noch die Möglichkeit geben, weitere Einstellungen vorzunehmen. Dazu stellen wir der Hauptprozedur einige Parameter bereit. Damit wollen wir die folgenden Einstellungen vornehmen:

  • Abstand zwischen Bezeichnungsfeld und Steuerelement in Pixeln
  • Vertikaler Abstand zwischen zwei Steuerelementen
  • Ausrichtung der Bezeichnungsfelder – wahlweise Standard, links, mittig oder rechts zentriert oder verteilt
  • Optionales Hinzufügen von Doppelpunkten rechts von den Beschriftungen, sofern noch nicht vorhanden

Doppelpunkte hinter Beschriftungen setzen

Zum Aufwärmen wollen wir die Prozedur schreiben, mit der wir den aktuell markierten Bezeichnungsfeldern einen Doppelpunkt hinzufügen, sofern dieser noch nicht vorhanden ist. Diese Prozedur heißt AddColonToLabels und ist in Listing 1 abgebildet.

Public Sub AddColonToLabels()
     Dim frm As Form
     Dim ctl As Control
     Set frm = Screen.ActiveForm
     For Each ctl In frm.Controls
         If ctl.InSelection Then
             Select Case ctl.ControlType
                 Case acLabel
                     If Not Right(ctl.Caption, 1) = ":" Then
                         ctl.Caption = ctl.Caption & ":"
                     End If
             End Select
         End If
     Next ctl
End Sub

Listing 1: Hinzufügen von Doppelpunkten zu den Bezeichnungsfeldern

Die Prozedur können wir auch allein aufrufen. Sie deklariert eine Form– und eine Control-Variable. Mit der Form-Variable frm referenziert sie über die Eigenschaft Screen.ActiveForm das aktuell markierte Formular. Die Variable ctl kann alle Steuerelemente referenzieren. Damit durchlaufen wir in einer For Each-Schleife alle Steuerelemente des referenzierten Formulars.

In der folgenden If…Then-Bedingung prüfen wir mit der Eigenschaft InSelection, ob das aktuelle mit ctl über die Schleife referenzierte Steuerelement derzeit markiert ist. Ist das der Fall, untersuchen wir in einer Select Case-Bedingung durch einen Vergleich der Eigenschaft ControlType mit der Konstanten acLabel, ob es sich um ein Bezeichnungsfeld handelt. Falls ja, prüfen wir schließlich noch, ob das letzte Zeichen ein Doppelpunkt ist. Ist das nicht der Fall, fügen wir der Eigenschaft Caption am Ende einen Doppelpunkt hinzu.

Diese Prozedur können Sie nun bereits ausprobieren. Dazu fügen Sie einem Formular in der Entwurfsansicht ein beliebiges Steuerelement mit einem Bezeichnungsfeld hinzu oder ein einzelnes Bezeichnungsfeld. Dieses markieren Sie nun. Anschließend rufen Sie die Prozedur AddColonToLabels auf. Sollte das Bezeichnungsfeld zu diesem Zeitpunkt noch keinen abschließenden Doppelpunkt haben, wird dieser nun hinzugefügt.

Prozedur zum Ausrichten der Steuerelemente

Nach dem Aufwärmen schauen wir uns gleich die Hauptprozedur der Lösung aus diesem Beitrag an. Diese heißt AlignControls und erwartet vier Parameter:

  • lngDistanceLabelControl: Abstand zwischen Beschriftungsfeld und Steuerelement in Punkten
  • lngVerticalDistance: Vertikaler Abstand zwischen Steuerelementen
  • varAlignment: Ausrichtung des Textes in den Bezeichnungsfeldern (0: Standard, 1: Links, 2: Zentriert, 3: Rechts, 4: Verteilen)
  • bolColons: Gibt an, ob Doppelpunkte hinzugefügt werden sollen.

Die Prozedur finden Sie in Listing 2. Die von dieser Prozedur aufgerufenen weiteren Funktionen und Prozeduren bilden wir im Verlauf ab. Die Prozedur deklariert zunächst einige Variablen.

Public Sub AlignControls(Optional lngDistanceLabelControl As Long = 100, Optional lngVerticalDistance As Long = 100, _
         Optional varAlignment As Variant = 0, Optional bolColons As Boolean = False)
     Dim frm As Form, ctl As Control
     Dim lngLabelLeft As Long, lngLabelWidth As Long, lngControlLeft As Long, lngTop As Long, lngMaxHeight As Long
     Dim lblControl As Label, varControls() As Variant, i As Integer
     If Not GetFormInDesignView(frm) Then
         MsgBox "Es ist kein Formular in der Entwurfsansicht geöffnet.", vbOKOnly + vbExclamation
         Exit Sub
     End If
     If GetSelectedControlCount(frm) < 2 Then
         MsgBox "Es müssen mindestens zwei Steuerelemente ausgewählt sein.", vbOKOnly + vbExclamation
         Exit Sub
     End If
     DoCmd.Echo False
     If bolColons Then
         AddColonToLabels
     End If
     OptimizeLabelSizes frm
     lngLabelLeft = GetMostLeftLabel(frm)
     lngLabelWidth = GetMaxLabelWidth(frm)
     lngControlLeft = lngLabelWidth + lngDistanceLabelControl + lngLabelLeft
     varControls = GetControlArray(frm)
     SortArray varControls
     lngTop = varControls(0, 0)
     For i = 0 To UBound(varControls, 2)
         Set ctl = frm.Controls(varControls(1, i))
         ctl.Left = lngControlLeft
         If Not lngTop = 0 Then
             ctl.Top = lngTop
             If ctl.ControlType = acCheckBox Then ''Korrektur für Kontrollkästchen
                 ctl.Top = ctl.Top + 30
             End If
             Set lblControl = ctl.Controls(0)
             lngMaxHeight = ctl.Height
             If lblControl.Height > lngMaxHeight Then
                 lngMaxHeight = lblControl.Height
             End If
             lblControl.Top = lngTop
             lblControl.Left = lngLabelLeft
             lblControl.Width = lngLabelWidth
             If Not IsMissing(varAlignment) Then
                 lblControl.TextAlign = varAlignment
             End If
         End If
         lngTop = lngTop + lngMaxHeight + lngVerticalDistance
     Next i
     DoCmd.Echo True
End Sub

[

Listing 2: Hauptprozedur zum Ausrichten von Steuerelementen

Aktuelles Formular in der Entwurfsansicht holen

Direkt die erste Anweisung ruft eine Hilfsfunktion namens GetFormInDesignView auf, welche eine leere Rückgabevariable namens frm als Parameter erhält. Die Funktion, die wir in Listing 3 abgebildet haben, hat die Aufgabe, das aktuelle Formular zu ermitteln und gleich noch zu prüfen, ob dieses in der Entwurfsansicht geöffnet ist. Anderenfalls können wir nämlich nicht auf die aktuell markierten Steuerelemente zugreifen.

Public Function GetFormInDesignView(frm As Form) As Boolean
     On Error Resume Next
     Set frm = Screen.ActiveForm
     If Not frm Is Nothing Then
         If frm.CurrentView = 0 Then
             GetFormInDesignView = True
         End If
     End If
     On Error GoTo 0
End Function

Listing 3: Funktion zum Holen des aktuellen Formulars in der Entwurfsansicht

Die Funktion holt mit Screen.ActiveForm das Formular, welches aktuell den Fokus hat. Das kann bereits schiefgehen, wenn gerade kein Formular den Fokus hat – in diesem Fall ist frm danach leer und die Funktion wird mit dem Wert False verlassen.

Enthält frm danach jedoch ein Formular, prüfen wir seine Ansicht mit der CurrentView-Eigenschaft. Hat diese den Wert 0, ist das aktuelle Objekt ein Formular in der Entwurfsansicht und wir können den Wert True als Funktionswert zurückliefern – die Rückgabevariable frm ist ja ohnehin bereits mit einem Verweis auf das betroffene Formular gefüllt.

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