Home > Artikel > Ausgabe 9/2016 > Steuerelemente im Eigenbau: Range Control

Steuerelemente im Eigenbau: Range Control

Achtung: Sie sind nicht angemeldet. Wenn Sie Abonnent sind und sich anmelden, lesen Sie den kompletten Artikel, laden das PDF herunter oder probieren die Beispieldatenbank aus (sofern vorhanden).

Der Fundus an Access-Steuerelementen fällt aus heutiger Sicht, gerade im Vergleich zu anderen visuellen Entwicklungsumgebungen, etwas bescheiden aus. Zwar lassen sich allerlei ActiveX-Steuerelemente in Formulare integrieren, doch beliebt ist dies bei vielen Entwicklern wegen der externen, unter Admin-Rechten zu registrierenden, Komponenten nicht besonders. Abhilfe schaffen können aber Steuerelemente, die Sie mit Bordmitteln einfach selbst erstellen. Wir stellen hier ein Beispiel vor: das Range Control.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 1609_RangeCtl.accdb.

Was ist denn ein Range Control?

Eine Übersetzung des Begriffs wäre Bereichssteuerelement. Sie kennen solche Regler etwa von Webshops oder Shopping-Portalen, wo Ihnen angeboten wird, die angezeigten Produkte über eine Preisspanne zu filtern. Dabei können Unter- und Obergrenzen mit der Maus und den Ziehpunkten des Elements eingestellt werden. Bild 1 zeigt unsere Umsetzung in einem Testformular. Mit vier Range Controls stellen sie dort visuell Wertebereiche ein.

Unterschiedliche Wertebereiche können im Testformular mithilfe der vier Range Controls mit der Maus über Ziehpunkte eingestellt werden

Bild 1: Unterschiedliche Wertebereiche können im Testformular mithilfe der vier Range Controls mit der Maus über Ziehpunkte eingestellt werden

Ein Anwendungsfall für derartige Steuerelemente wäre etwa die Filterung von Datensätzen. Im Datenblatt von Access können Sie bereits, je nach Felddatentyp, Wertebereiche zur Filterung der Anzeige bestimmen. Klicken Sie etwa, wie im Datenblatt zur Tabelle tblArtikel in Bild 2, auf den Dropdown-Pfeil des Spaltenkopfs zu Einzelpreisen. Es öffnet sich das Filtermenü mit diversen Möglichkeiten. Um einen Zahlenbereich festzulegen navigieren Sie über Zahlenfilter auf den Untermenüeintrag Zwischen.... Es erscheint ein einfaches Dialogfenster zur manuellen Eingabe der Bereichsgrenzen für die Preise, nach dessen Bestätigung sich die Datensätze entsprechend filtern. Während dieses Feature im Datenblatt noch ganz praktisch ist, verhielte es sich in Formularen wegen der Menüverschachtelung etwas undurchsichtig. Hier käme man mit einem Range Control schneller zum Ziel.

Menü und Dialog für das Filtern nach Wertebereich in einem Datenblatt

Bild 2: Menü und Dialog für das Filtern nach Wertebereich in einem Datenblatt

Formular als Steuerelement

Wenn Sie ein neues virtuelles Steuerelement entwickeln, werden Sie dieses in der Regel aus mehreren verfügbaren Access-Steuerelementen zusammensetzen wollen. Das könnten Sie direkt in jenem Formular tun, in dem dieses zum Einsatz kommen soll. Sobald dies jedoch in mehreren Formularen geschehen soll oder Sie das neue Element in andere Datenbanken importieren möchten, wird der Aufwand zu groß.

Die beteiligten Steuerelemente müssten alle kopiert werden und die zugehörigen Code-Abschnitte ebenso. Bequemer kommen Sie weg, wenn Sie die Steuerelemente und den Code in einem eigenen Formular anlegen, welches Sie dann schließlich nur als Unterformular in andere einsetzen. Zudem lässt sich dieses dann als Objekt auch in andere Datenbanken exportieren.

Eigenes Range Control

In der Beispieldatenbank finden Sie das neue Steuerelement in Gestalt des Formulars sfrmRanger. Sein Entwurf zeigt sich in Bild 3. Sie können es auch unmittelbar öffnen und bedienen, wie in Bild 4 dargestellt. Die Breite des virtuellen Steuerelements passt sich dabei der Größe des Formulars an, dessen Rahmen auf Veränderbar eingestellt ist, wodurch es mit der Maus dimensioniert werden kann. Dies wirkt sich später auch aus, wenn Sie es als Unterformular dimensionieren.

Entwurfsansicht des Range Control-Formulars

Bild 3: Entwurfsansicht des Range Control-Formulars

Das Range Control-Formular zur Laufzeit nach dem Öffnen

Bild 4: Das Range Control-Formular zur Laufzeit nach dem Öffnen

Die weiteren relevanten Einstellungen des Formulars: Der Datenbankmarkierer ist ausgeschaltet und ebenso die Navigationsschaltflächen. Die Standardansicht ist Einzelnes Formular. Gleichzeitig ist dies die einzige zugelassene Ansicht. Es gibt auch keine Trennlinien oder Bildlaufleisten.

Sieben Steuerelemente ergeben zusammen das Range Control. Die beiden Ziehpunkte sind Schaltflächen, die über deren Design-Eigenschaften auf blaue und violette Hintergrundfarbe mit Verlauf eingestellt sind (Bild 5). Außerdem ist ihr Rahmen rund (Bild 6). Sie erreichen diese Layout-Steuerung bei markiertem Button über den Ribbon Format | Steuerelementformatierung und das Menü zu Fülleffekt, sowie die Schaltfläche Form ändern.

Farbverlauf für die Buttons

Bild 5: Farbverlauf für die Buttons

Form für die Buttons

Bild 6: Form für die Buttons

Die beiden Schaltflächen heißen cmd1 und cmd2. Für den linken und rechten Rand des Elements kommen ebenfalls Schaltflächen zum Einsatz, die sich cmdL und cmdR nennen. Sie sind auf ähnliche Weise formatiert, wie die Ziehpunkte.

Unter diesen vier Schaltflächen liegen die zwei Rechteck-Steuerelemente r0 und r1. r0 ist grau gefüllt und stellt den Hintergrundbalken dar, r1 ist blau und symbolisiert den gewählten Bereich. Damit diese sechs Elemente korrekt angezeigt werden, muss eine bestimmte Z-Order eingehalten werden, weil sie ja übereinander liegen. Die Z-Order ist jene Reihenfolge, in der Steuerelemente übereinander zu liegen kommen, also deren Tiefeneigenschaft. Unter Access lässt sich auf diese Z-Order direkt leider weder über die Oberfläche beim Entwurf, noch per VBA zugreifen. Stattdessen bemühen Sie die beiden Ribbon-Schaltflächen In den Vordergrund und In den Hintergrund im Ribbon-Tab Anordnen ganz rechts. Für unser Beispiel gehen Sie dabei so vor:

Markieren Sie r0 und klicken In den Hintergrund. Markieren Sie nun r1 und klicken In den Vordergrund. Schließlich markieren Sie cmdL, cmd1, cmd2 und cmdR und klicken abermals In den Vordergrund. Damit ist gewährleistet, dass die Buttons nie hinter die beiden Rechteck-Steuerelemente geraten.

Ein Bezeichnungsfeld LblVal am rechten Rand des Formulars dient zur Anzeige des eingestellten Wertebereichs.

Da das Range Control in der Breite variabel ausfallen soll, müssen sich die enthaltenen Steuerelemente entsprechend der Größenänderung des Formulars in Position und Breite anpassen (s. Bild 7). Der Versuch, dies über das Verankern-Feature der Elemente zu bewerkstelligen lief indessen ins Leere. Zwar lassen sich die Anpassungen damit visuell korrekt steuern, doch die aus den Button-Positionen abgeleiteten Werte können damit leider nicht mehr ermittelt werden. Stelen Sie etwa den Hintergrundbalken r0 auf Quer nach oben dehnen ein, so macht er die Größenänderungen des Formular anstandslos mit. Möchten Sie nun dessen Breite ermitteln, die ja die Basis für den Wertebereich darstellt, so würden Sie dafür die VBA-Eigenschaft Width des Steuerelements verwenden. Die jedoch bleibt auf dem Wert stehen, der im Entwurf angelegt wurde. Das Dehnen ändert also Width nicht! Gleiches gilt etwa für Left bei Steuerelementen, die auf Oben rechts eingestellt sind. Die linke Position wandert dann zwar im Formular bei Größenänderung, doch Left bleibt konstant auf dem im Entwurf festgelegten Wert.

Bei Größenänderung des Formulars positionieren und dimensionieren sich die integrierten Elemente passend

Bild 7: Bei Größenänderung des Formulars positionieren und dimensionieren sich die integrierten Elemente passend

Deshalb kommt man nicht umhin, die Positionierung stattdessen per VBA im Ereignis Bei Größenänderung (Form_Resize) des Formulars vorzunehmen. Listing 1 zeigt den überschaubaren Code hierfür. Der linke Rand der rechten Bereichsschaltfläche cmdR ergibt sich aus der Breite des Formulars zur Laufzeit (InsideWidth, nicht Width!) minus der Breite des Labels LblVal minus seiner eigenen Breite minus 300 Twips. Die Position des Labels LblVal wiederum errechnet sich ähnlich. Die Breite des Hintergrundbalkens r0 ermittelt sich aus der Formularbreite minus Label LblVal minus 150 Twips. Die linke Bereichsschaltfläche cmdL hingegen bleibt an Ort und Stelle. Die beiden Ziehpunkt-Buttons cmd1 und cmd2, sowie der Bereichsbalken r1, aber werden ja nicht in dieser Prozedur eingestellt, sondern beim Ziehen mit der Maus; und wie das geht erfahren Sie im Folgenden.

Private Sub Form_Resize()

     On Error Resume Next

     cmdR.Left = Me.InsideWidth - LblVal.Width - cmdR.Width - 300

     LblVal.Left = Me.InsideWidth - LblVal.Width - 150

     r0.Width = cmdR.Left - cmdL.Left

End Sub

Listing 1: Positionierung der Steuerelemente des Formulars bei Größenänderung in der Resize-Ereignisprozedur

Bewegen von Steuerelementen mit der Maus

Mit wenig Code kann die Mausbewegung auf einem Steuerelement dafür verwendet werden, dieses im Formular zu verschieben. Die dafür geeigneten Ereignisprozeduren sind Bei Maustaste Ab (MouseDown), Bei Maustaste Auf (MouseUp) und Bei Mausbewegung (MouseMove). Exemplarisch zeigt Listing 2, wie dies für den linken Ziehpunkt cmd1 realisiert ist.

Option Explicit

Private x1 As Long

Public Event ValuesUpdated()

Private Sub cmd1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

     If Button = 1 Then

         x1 = X

     End If

End Sub

Private Sub cmd1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)

     If Button = 1 Then

         Dim xx As Long

         xx = cmd1.Left + (X - x1)

         If xx < cmdL.Left Then xx = cmdL.Left

         If xx > cmdR.Left Then xx = cmdR.Left

         If xx > cmd2.Left Then xx = cmd2.Left

         cmd1.Left = xx

         AdjustValueBar

     End If

End Sub

Private Sub cmd1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)

     If Button = 1 Then RaiseEvent ValuesUpdated

End Sub

Listing 2: Prozeduren zum Bewegen des linken Ziehpunkts mit der Maus

Wird eine Maustaste gedrückt, während sich der Cursor über dieser Schaltfläche befindet, so löst dies das Ereignis MouseDown aus. Die zugehörige Prozedur übergibt einige Parameter. Button ist eine Zahl, die sich auf die Taste bezieht. Bei 1 handelt es sich um die linke, bei 2 um die rechte Maustaste. Shift ist ein Wert, der die Kombination aus STRG- SHIFT- und ALT-Taste wiedergibt. Wir lassen diesen hier unberücksichtigt. x und y sind die Koordinaten, bei denen der Cursor quasi auf das Element traf. Uns interessiert natürlich nur die horizontale Position x bei linker Maustaste. Sie wird nun in der im Modulkopf deklarierten Variablen x1 zwischengespeichert, die damit auch weiteren Prozeduren zur Verfügung steht.

Beim Bewegen der Maus tritt nun fortwährend das Ereignis MouseMove auf. Die Parameter sind dieselben, wie bei MouseDown. Ist dabei die linke Maustaste (Button = 1) gedrückt, so errechnet die Routine in der Folge die neue Position des Ziehpunkts cmd1 in der Hilfsvariablen xx.

Zur linken Position des Steuerelements (cmd1.Left) wird die Differenz aus neuer Mausposition X und zwischengespeichertem alten Wert in x1 hinzuaddiert. Damit befindet sich das Steuerelement wieder genau unter dem Mauszeiger!

Allerdings soll sich der Ziehpunkt nicht beliebig im Formular bewegen lassen, weshalb noch drei Bedingungen ins Spiel kommen. Ist die neu errechnete Position xx weiter links, als der Rand-Button cmdL, so wird sie einfach auf dessen Position gesetzt, damit der Ziehpunkt ihn niemals überschreitet. Das gleiche gilt für den rechten Rand-Button cmdR.

Hier darf die neue Position nicht größer sein, als dessen linke Position. Und schließlich ergibt es auch keinen Sinn, wenn der linke Ziehpunkt sich über den rechten (cmd2) bewegen ließe. Darum ist auch hier eine entsprechend Einschränkung eingebaut. Der nunmehr gültige Wert in xx wird der Left-Eigenschaft von cmd1 zugewiesen. Erst hier bewegt sich die Schaltfläche im Formular tatsächlich.

Die Routinen für den rechten Ziehpunkt cmd2 sind absolut analog aufgebaut. Der einzige Unterschied besteht in der Bedingungszeile, dass dieser sich nicht nach links jenseits des ersten Ziehpunkts cmd1 bewegen darf.

Beim Bewegen der Ziehpunkte soll sich außerdem der blaue Bereichsbalken unter ihnen ändern. Das übernimmt die Prozedur AdjustValueBar, die von beiden MouseMove-Routinen abschließend aufgerufen wird. Wir kommen gleich noch auf deren Code zu sprechen.

Das MouseUp-Ereignis (Bei Maustaste Auf) hätte man im Prinzip auch unberücksichtigt lassen können. Es wird für das Bewegen der Steuerelemente nicht benötigt. Hier aber wird es verwendet, um ein neues Ereignis hervorzurufen.

Beim Loslassen der Maustaste nämlich soll das Unterformular dem Hauptformular melden, dass sich der Wertebereich des Range Controls geändert hat. Und das lässt sich am Einfachsten über ein Klassenereignis erreichen.

Das Modul eines Formulars ist ein Klassenmodul. In Klassenmodulen gibt es, im Unterschied zu normalen Modulen, die Möglichkeit, Ereignisse zu deklarieren. Ein Klassenmodul, welches diese Klasse selbst verwendet – hier wäre es das des Hauptformulars – kann dann auf diese Ereignisse reagieren. Im Kopf des Ranger-Formulars ist das neue Ereignis nämlich dergestalt deklariert:

Public Event ValuesUpdated()

Das Unterformular löst somit gegebenenfalls das Ereignis ValuesUpdated aus. Wie aber kann dies in den Formularroutinen erreicht werden? RaiseEvent lautet hier das Zauberwort. Im MouseUp-Ereignis in Listing 2 wird genau diese Anweisung abgesetzt:

RaiseEvent ValuesUpdated

Wie darauf reagiert wird, erläutern wir später noch.

Abschließend der Code zum Anpassen des Bereichsbalkens in AdjustValueBar (Listing 3), welcher jeweils nach Neujustierung der Ziehpunkte aufgerufen wird. Bei Ziehen der Punkte soll sich dessen linker Rand unter dem Ziehpunkt links (cmd1) befinden und seine Breite sich zwischen beiden Punkten ausdehnen. Die drei ersten Zeilen benötigen demnach wohl keiner Erläuterung. In die Routine zusätzlich eingebaut ist die numerische Ausgabe des Wertebereichs im Label LblVal. Es bezieht die Angaben aus den zwei Hilfsfunktionen LeftVal und RightVal, die als Properties daherkommen.

Private Sub AdjustValueBar()

     On Error Resume Next

     r1.Left = cmd1.Left + cmd1.Width \ 2

     r1.Width = cmd2.Left - cmd1.Left

     LblVal.Caption = LeftVal & " - " & RightVal

     RaiseEvent Changed

End Sub

Property Get LeftVal() As Long

     LeftVal = m_maxval * (cmd1.Left - cmdL.Left) / (cmdR.Left - cmdL.Left)

End Property

Sie haben das Ende des frei verfügbaren Teil dieses Artikels erreicht!

Wenn Sie mehr lesen und auf viele weitere Artikel zugreifen möchten, melden Sie sich als Abonnent unter Login an. Falls nicht, bestellen Sie doch einfach ein Jahresabonnement!