Access ist bekannt dafür, dass man Daten schnell in gewünschter Weise zur Bearbeitung in Formularen anzeigen kann – zumindest für übliche Szenarien. Entwickler schätzen Access aber auch deshalb, weil sich auch außergewöhnliche Anforderungen damit realisieren lassen. In diesem Beitrag geht es um die Synchronisierung zweier Unterformulare.
Die Anforderung für diesen Beitrag stammt aus einem anderen Beitrag, nämlich Mehrsprachige Anwendungen (Shortlink 611). Dort sollen Benutzer in einem Formular verschiedene Übersetzungen der Beschriftungen von Steuerelementen eintragen, um die Anwendung mehrsprachig zu machen.
Die Daten stammen aus einer einzigen Tabelle, die unter anderem die ID der Sprache und des Textes sowie die verschiedenen Übersetzungen beinhaltet. Wie immer unter Access gibt es verschiedene Ansätze, um den Originaltext und dessen Übersetzung so anzuzeigen, dass der Benutzer diese möglichst komfortabel editieren kann. So könnte man etwa die Originaltexte in einem Listenfeld anzeigen, aus dem der Benutzer den Text auswählt, den er übersetzen möchte, und diesen Text in einem Textfeld zur Bearbeitung bereitstellen. Diese Lösung programmiert ein durchschnittlich begabter Access-Programmierer wohl in maximal einer halben Stunde.
Manchmal juckt dem Entwickler aber das Fell und er wählt einen Weg, der auf den ersten Blick nicht viel aufwendiger erscheint, aber erheblich ergonomischer wirkt. Dabei soll auf der linken Seite des Formulars ein Unterformular in der Datenblattansicht die Originaltexte und ein weiteres auf der rechten Seite die Übersetzungen in der jeweils ausgewählten Sprache anzeigen.
Das ist gegenüber dem ersten Ansatz sicher ergonomischer, weil der Benutzer nicht für jede neue Übersetzung erst den Originaltext markieren muss, um den zu übersetzenden Text angezeigt zu bekommen. Stattdessen sieht er links eine Reihe Originaltexte und rechts die Übersetzungen, wobei er die Übersetzungen einfach von oben nach unten oder auch willkürlich abarbeiten kann. So weit, so gut: Zwei Unterformulare in einem Formular anzulegen, die jeweils andere Daten der gleichen Tabelle anzeigen (je einmal für jede Sprache), stellt keine echte Herausforderung dar. Dafür brauchen Sie sogar nur ein einziges Formular, das Sie zweimal als Unterformular in ein Hauptformular integrieren.
Unpraktisch ist nur, dass man irgendwann alle aktuell angezeigten Texte übersetzt hat und die bis dahin nicht sichtbaren Texte nach oben scrollen muss – und das immer gleichzeitig für zwei Unterformulare, damit die entsprechenden Texte auch nebeneinanderliegen. Hier setzt die Lösung dieses Beitrags an: Sie zeigt, wie Sie die beiden Unterformulare synchron scrollen, egal, ob dies mit der Tastatur, dem Mausrad oder mit den Bildlaufleisten geschieht.
Versuchsaufbau
Als Grundlage dient eine Beispieltabelle namens tblBeispiele mit den zwei Feldern BeispielID und Beispiel. Ein Hauptformular namens frmHauptformular besitzt nur zwei Unterformularsteuerelemente sfm1 und sfm2, die jeweils das Unterformular sfmUnterformular enthalten (siehe Bild 1).
Bild 1: Hauptformular mit zwei synchronisierten Unterformularen
Dieses Unterformular statten Sie mit der Tabelle tblBeispiele als Datenherkunft aus, wobei es beide Felder dieser Tabelle in der Datenblattansicht anzeigen soll (zur Vereinfachung sollen links und rechts jeweils die gleichen Datensätze ein und derselben Tabelle angezeigt werden).
Das Unterformular ziehen Sie einfach zweimal vom Datenbankfenster in das im Entwurf geöffnete Hauptformular und nennen dann das erste Unterformularsteuerelement sfm1 und das zweite sfm2. Achtung, es gibt einen Unterschied zwischen Unterformularsteuerelement und dem darin enthaltenen Formular! Selbstverständlich müssen die Unterformularsteuerelemente verschiedene Namen haben, können aber das gleiche Unterformular enthalten (nachzuprüfen in der Eigenschaft Herkunftsobjekt auf der Registerseite Daten der Steuerelementeigenschaften).
Gegenseitige Abhängigkeit
Die besondere Würze dieses Beispiels ist die Verwendung des gleichen Formulars in beiden Unterformularsteuerelementen. Mal ganz ohne den zugehörigen Code zu beschreiben soll ja bereits beim Wechseln des Datensatzes Folgendes passieren: Unterformular 1 wechselt vom untersten Datensatz zum nächsten Datensatz, der ja gleichzeitig der erste nicht mehr sichtbare Datensatz ist. In diesem Fall soll das Gleiche in Unterformular 2 geschehen – die Datensätze sollen um eine Position nach oben verschoben werden. Dazu sind verschiedene Aktionen notwendig, die unter anderem das Beim Anzeigen-Ereignis von Formular 2 auslösen. Auch das Scrollen von Formular 1 kann prinzipiell (außer per Timer) nur durch das Beim Anzeigen-Ereignis abgefangen werden. Wenn man nun nicht prüft, ob das Beim Anzeigen-Ereignis nicht gerade durch das Scrollen des anderen Formulars ausgelöst wurde, und ein endloses, wechselseitiges Aufrufen vermeiden möchte, muss man dies abfangen.
Wenn nun das erste Unterformular dem zweiten auf irgendeine Weise mitteilen möchte, dass es gerade durch einen Scrollvorgang einen Datensatzwechsel ausgelöst hat, muss das erste Unterformular ja erstmal wissen, dass es überhaupt das erste Unterformular ist – immerhin unterscheiden sich die beiden Unterformulare ja nur dadurch, dass das erste in einem Unterformularsteuerelement etwa namens sfm1 und das zweite in einem anderen namens sfm2 untergebracht ist. Dummerweise kann man aber nicht durch einen Verweis wie Me.Parent auf das Unterformularsteuerelement zugreifen, in dem ein Unterformular steckt, sondern erhält mit Me.Parent direkt einen Verweis auf das Formular, in dem sich das Unterformular befindet.
Selbstidentifikation
Will ein Unterformular nun erfahren, ob es das erste oder zweite Unterformular ist, benötigt es eine kleine Hilfsfunktion:
Private Function GetMe() As String Dim ctl As Control For Each ctl In Parent.Controls If ctl.ControlType = acSubform Then If ctl.Form.hWnd = Me.hWnd Then GetMe = ctl.Name Exit For End If End If Next ctl End Function
Diese durchläuft alle Steuerelemente des übergeordneten Formulars und prüft zunächst, ob es sich dabei um ein Unterformularsteuerelement handelt. Falls ja, zieht es die Eigenschaft hWnd heran, um zu ermitteln, ob es selbst oder ein anderes Unterformular in diesem Unterformular steckt. hWnd liefert ein eindeutiges Fenster-Handle beispielsweise für Formulare. GetMe liefert schließlich den Namen des Unterformularsteuerelements zurück, in dem sich das Unterformular befindet.
Eine zweite Funktion namens GetYou sieht ähnlich aus, liefert aber den Namen des ersten Unterformularsteuerelements, das nicht dem Unterformularsteuerelement entspricht, in dem das aufrufende Unterformular steckt:
Private Function GetYou() As String Dim ctl As Control For Each ctl In Parent.Controls If ctl.ControlType = acSubform Then If ctl.Form.Name = Me.Name And _ ctl.Form.hWnd <> Me.hWnd Then GetYou = ctl.Name Exit For End If End If Next ctl End Function
Diese beiden Funktionen arbeiten nicht wie erwartet, wenn das Hauptformular mehr als die zwei betroffenen Unterformulare enthält. In diesem Fall müssten Sie noch eine weitere Prüfung etwa des Namens der Unterformularsteuerelemente hinzufügen.
Scrollen mit Cursor-, Tab- und Eingabetaste
Die Navigation in den Datensätzen ist durch verschiedene Tasten möglich. Das Abfragen der entsprechenden Tastenanschläge ist nicht notwendig, da ein ändern der Position ohnehin nur beim Wechseln des Datensatzes erfolgt, den Sie mit dem Ereignis Beim Anzeigen des Unterformulars abfangen können. Listing 1 zeigt den Code, den das Unterformular enthalten muss, damit es beim Wechseln des Datensatzes und damit verbundener Scroll-Vorgänge ein Mitscrollen des jeweils anderen Unterformulars bewirkt.
Listing 1: Die Ereignisprozedur, die beim Anzeigen eines Datensatzes und somit bei jedem Datensatzwechsel ausgelöst wird, sorgt für das gegenseitige Aktualisieren der Scrollposition der Unterformulare.
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