Datenblatt: Reihenfolge mehrerer Einträge ändern

Wir haben bereits in mehreren Beiträgen beschrieben, wie Sie die individuelle Reihenfolge von Elementen einer Tabelle über den Inhalt eines Feldes etwa namens „ReihenfolgeID“ einstellen können – zum Beispiel in Listenfeldern oder Unterformularen in der Datenblattansicht. Dort haben wir die Reihenfolge dann durch Markieren der Einträge und anschließendes Betätigen etwa von Schaltflächen mit Beschriftungen wie „Ganz nach oben“, „Nach oben“, „Nach unten“ oder „Ganz nach unten“ geändert. Im vorliegenden Beitrag schauen wir uns nun an, wie wir im Unterformular in der Datenblattansicht die Reihenfolge für mehrere Einträge gleichzeitig ändern können.

Mehrfach-Reihenfolge im Datenblatt

Im Beitrag Listenfeld: Reihenfolge mehrerer Einträge ändern (www.access-im-unternehmen.de/1197) zeigen wir, wie das Ändern der Reihenfolge mehrerer markierter Einträge gleichzeitig im Listenfeld funktioniert. Aber oft sollen Daten auch in einem Unterformular in der Datenblattansicht dargestellt werden, was Vorteile gegenüber einem Listenfeld bietet – zum Beispiel das schnelle Sortieren, Filtern oder das Ändern der Spaltenanordnung oder -breite.

Außerdem kann der Benutzer in einem Unterformular in der Datenblattansicht auch direkt Daten ändern, was im Listenfeld nicht möglich ist. Also zeigen wir auch für das Unterformular in der Datenblattansicht, wie Sie die Reihenfolge der markierten Datensätze ändern können.

Beispielformular

Die Beispielkonstellation sieht wie in Bild 1 aus. Hier haben wir ein Unterformular namens sfmMehrfachreihenfolgeDatenblatt erstellt, dass an die Tabelle tblKategorien gebunden ist – und zwar über die folgende SQL-Abfrage:

Entwurf des Formulars frmMehrreihenfolgeDatenblatt

Bild 1: Entwurf des Formulars frmMehrreihenfolgeDatenblatt

SELECT tblKategorien.KategorieID, tblKategorien.Kategoriename, tblKategorien.ReihenfolgeID FROM tblKategorien ORDER BY tblKategorien.ReihenfolgeID;

Dadurch werden die Datensätze aufsteigend nach der Sortierung des Feldes ReihenfolgeID angezeigt.

Das Unterformular soll die Daten der drei Felder KategorieID, Kategoriename und ReihenfolgeID anzeigen. Für die Eigenschaft Standardansicht haben wir den Wert Datenblatt eingestellt.

Danach haben wir das Unterformular aus dem Navigationsbereich von Access in den Entwurf des Hauptformulars frmMehrfachreihenfolgeDatenblatt gezogen. Das Unterformularsteuerelement, in dem das Unterformular angezeigt wird, haben wir aus Gründen der Übersicht in sfm umbenannt.

Rechts neben dem Unterformular befinden sich die vier Schaltflächen cmdTop, cmdUp, cmdDown und cmdBottom. Die Eigenschaften Navigationsschaltflächen, Datensatzmarkierer, Trennlinien und Bildlaufleisten des Hauptformulars haben wir auf Nein eingestellt, die Eigenschaft Automatisch zentrieren auf Ja.

Beschreibung der Aufgabe

Im Gegensatz zum Listenfeld können wir mit der Datenblattansicht nur zusammenhängende Datensätze markieren. Dafür gibt es beim Datenblatt ganz andere Möglichkeiten, um die jeweils markierten Datensätze zu identifizieren. Diese werden Sie in den folgenden Abschnitten kennenlernen.

Im ersten Schritt wollen wir sicherstellen, dass jeweils nur die aktuell notwendigen Schaltflächen aktiviert sind. Das heißt, dass die beiden Schaltflächen cmdTop und cmdUp deaktiviert werden sollen, wenn der Benutzer den obersten Datensatz im Datenblatt markiert hat, denn dann können die markierten Elemente nicht mehr weiter nach oben verschoben werden. Das gleiche gilt für die Schaltflächen cmdDown und cmdBottom, wenn der letzte Datensatz im Datenblatt markiert ist. Wenn der Benutzer alle Datensätze markiert, sollen dementsprechend alle vier Schaltflächen deaktiviert sein.

Eine besondere Herausforderung besteht darin, dass beim Wechseln der Markierung im Unterformular Steuer-elemente im Hauptformular geändert sollen. Es ist zwar einfach möglich, dies über eine entsprechende Ereignisprozedur im Unterformular zu realisieren. Diese würde dann etwa über Me.Parent.cmdUp auf die Schaltfläche cmdUp zugreifen. Schöner und wartungsfreundlicher ist es allerdings, wenn der Code im Hauptformular versammelt ist. Das heißt, dass wir im Hauptformular eine Objektvariable zum Referenzieren des Unterformulars deklarieren und für diese Objektvariable die Ereignisse implementieren, die normalerweise im Klassenmodul des Unterformulars gelandet wären.

Aktivieren und Deaktivieren der Schaltflächen

Die Schaltflächen sollen in Abhängigkeit von den aktuell markierten Einträgen des Unterformulars aktiviert und deaktiviert werden. Das Problem dabei ist, dass wir erst noch ein Ereignis finden müssen, dass beim Setzen der Markierung im Unterformular ausgelöst wird. Das Ereignis Bei Markierungsänderung hört sich spannend an, aber es kann nur in der veralteten PivotChart– oder PivotTable-Ansicht eingesetzt werden.

Also nutzen wir die Bei Maustaste auf-Ereigniseigenschaft eines der Bereiche des Unterformulars. Wir finden schnell heraus, dass dieses Ereignis für das Formular selbst ausgelöst wird, wenn wir auf einen der Datensatzmarkierer klicken, also auf den in Bild 2 markierten Bereich. Im Unterformular selbst sieht die Ereignisprozedur wie folgt aus:

Bereich, der das Ereignis OnMouseDown auslöst

Bild 2: Bereich, der das Ereignis OnMouseDown auslöst

Private Sub Form_MouseUp(Button As Integer, _
     Shift As Integer, X As Single, Y As Single)
End Sub

Wir wollen diese allerdings im Klassenmodul des Hauptformulars implementieren. Dazu deklarieren wir die Variable sfm wie folgt:

Dim WithEvents m_SubForm As Form

Bei dieser Gelegenheit deklarieren wir direkt drei Konstanten, die später oft verwendete Bezeichnungen enthalten – für die Tabelle, das Primärschlüsselfeld der Tabelle und das Feld, nach dem die Sortierung erfolgt, in diesem Fall ReihenfolgeID:

Const m_Table As String = "tblKategorien"
Const m_PKField As String = "KategorieID"
Const m_Orderfield As String = "ReihenfolgeID"

Auf diese Weise können Sie, wenn Sie den Code dieser Lösung in anderen Formularen einsetzen wollen, schnell die Bezeichnungen der von Ihnen verwendeten Tabelle und Felder angeben.

Danach füllen wir diese Variable in der Prozedur, die durch das Ereignis Beim Laden des Hauptformulars ausgelöst wird, wie folgt:

Private Sub Form_Load()
     Set m_SubForm = Me!sfm.Form
     m_SubForm.OnMouseUp = "[Event Procedure]"
     FillOrderID
End Sub

Die zweite Anweisung stellt per Code die Eigenschaft Bei Maustaste auf des Unterformulars auf den Wert [Ereignisprozedur] ein – diesen Schritt würden Sie normalerweise über das Eigenschaftenblatt erledigen.

Die Prozedur FillOrderID füllt das in m_Orderfield angegebene Feld der Tabelle aus m_Table mit durchlaufenden Werten von 1 bis zur Anzahl der enthaltenen Datensätze.

Diese Prozedur beschreiben wir ausführlich im Beitrag Listenfeld: Reihenfolge mehrerer Einträge ändern (www.access-im-unternehmen.de/1197).

Nun hinterlegen wir eine Ereignisprozedur für das Ereignis Bei Maustaste ab des Unterformulars.

Dazu wählen Sie im Codefenster des Moduls Form_frmMehrfachreihenfolgeDatenblatt aus dem linken Kombinationsfeld den Wert m_SubForm und aus dem rechten Kombinationsfeld den Wert MouseUp aus (siehe Bild 3).

Anlegen des Ereignisses für das Unterformular

Bild 3: Anlegen des Ereignisses für das Unterformular

Damit das Ereignis auch ausgelöst wird, müssen Sie für das Unterformular noch ein Klassenmodul anlegen. Dazu können Sie einfach die Eigenschaft Enthält Modul des als Unterformular verwendeten Formulars auf Ja einstellen.

Markierte Datensätze erkennen

Die so entstandene Prozedur, die wie folgt aussieht, müssen wir nun noch mit dem Code füllen, der die Markierung prüft und die Schaltflächen aktiviert oder deaktiviert. Diesen Code lagern wir in eine neue Prozedur namens ActivateControls aus:

Private Sub m_SubForm_MouseDown(Button As Integer, _
         Shift As Integer, X As Single, Y As Single)
     ActivateControls
End Sub

Hier geben wir testweise erst einmal die Werte der Eigenschaften SelTop und SelHeight im Direktbereich aus, um zu prüfen, ob diese die entsprechenden Werte zur durchgeführten Markierung liefern:

Private Sub ActivateControls()
     Debug.Print m_SubForm.SelTop, m_SubForm.SelHeight
End Sub

Das gelingt auch recht gut. Die Eigenschaft SelTop liefert immer den 1-basierten Index des ersten markierten Eintrags, die Eigenschaft SelHeight die Anzahl der markierten Einträge. Wenn Sie etwa erst auf den Datensatzmarkierer des zweiten Eintrags klicken und dann bei gedrückter Umschalt-Taste auf den fünften Eintrag, werden vier Einträge vom zweiten bis zum fünften Eintrag markiert (siehe Bild 4).

Markieren mehrerer Einträge

Bild 4: Markieren mehrerer Einträge

Die Eigenschaft SelTop liefert dann den Wert 2, die Eigenschaft SelHeight den Wert 4. Sie können auch mehrere Datensätze markieren, indem Sie auf den Datensatzmarkierer des ersten zu markierenden Datensatzes klicken und dann bei gedrückter linker Maustaste den Mauszeiger bis zum Datensatzmarkierer des letzten gewünschten Datensatzes herunterziehen.

Hier gibt es noch ein paar Besonderheiten:

  • Wenn das Unterformular das Hinzufügen von Datensätzen erlaubt, können Sie auch die neue, leere Zeile markieren (siehe Bild 5). Dies liefert dann als Ergebnis für die Eigenschaft SelTop den Wert 9 und für die Eigenschaft SelLength den Wert 1. Wie wir dies behandeln, schauen wir uns weiter unten an.
  • Markieren des letzten Eintrags

    Bild 5: Markieren des letzten Eintrags

  • Sie können auch mit der Maus auf den schmalen Bereich zwischen zwei Datensatzmarkierern klicken. Das ist dann der Fall, wenn sich der Mauszeiger zuvor in das Symbol aus Bild 6 verwandelt hat. Hier liefert SelTop den Index des darunter liegenden Datensatzes, im Beispiel den Wert 6. SelLength liefert aber zum Glück den Wert 0, sodass wir diesen Sonderfall gut behandeln können.
  • Anklicken des Bereichs zwischen zwei Datensätzen

    Bild 6: Anklicken des Bereichs zwischen zwei Datensätzen

  • Es gibt noch einen Sonderfall, nämlich das Markieren aller Datensätze mit der Tastenkombination Strg + A. Das registriert die von uns vorgesehene Ereignisprozedur jedoch nicht, da diese nur auf Mausklicks reagiert. Also müssen wir uns hierfür eine Sonderbehandlung überlegen, die wir ebenfalls weiter unten vorstellen.

Neuen Datensatz berücksichtigen

Um den neuen, leeren Datensatz zu berücksichtigen, müssen wir zunächst einmal herausfinden, ob dieser überhaupt eingeblendet wird. Dies gelingt mit der Eigenschaft AllowEditions.

Diese fragen wir als Eigenschaft des Unterformulars ab, also etwa mit Me!sfm.Form.AllowAdditions und in unserem Fall mit m_SubForm.AllowAdditions. Wenn der Benutzer das Hinzufügen von Datensätzen erlaubt, liefert die Eigenschaft den Wert True, sonst False.

Damit ergeben sich zwei Fälle:

  • Der Benutzer könnte bei AllowEditions = True auch den neuen, leeren Datensatz markieren.
  • Bei AllowEditions = False kann der Benutzer den neuen, leeren Datensatz nicht markieren, da dieser nicht angezeigt wird.

Im zweiten Fall können wir als einfach die von SelLength gelieferte Anzahl verwenden. Im ersten Fall müssen wir jedoch prüfen, ob der Index eines der markierten Einträge der Index des neuen, leeren Datensatzes ist. Und dazu benötigen Sie die Anzahl der Datensätze des Unterformulars.

Anzahl der Datensätze im Unterformular ermitteln

Die Anzahl der Datensätze ermitteln wir ganz einfach mit der Eigenschaft RecordCount des Recordset-Objekts des Unterformulars:

m_SubForm.Recordset.RecordCount

Schaltflächen aktivieren und deaktivieren

Mit diesen Erkenntnissen können wir uns nun um die Prozedur ActivateControls kümmern (siehe Listing 1). In dieser aktivieren wir nun zuerst alle Schaltflächen durch Einstellen der Eigenschaft Enabled auf den Wert True.

Private Sub ActivateControls()
     Dim bolErster As Boolean, bolLetzter As Boolean
     Dim lngAnzahl As Long
     Me!cmdBottom.Enabled = True
     Me!cmdDown.Enabled = True
     Me!cmdTop.Enabled = True
     Me!cmdUp.Enabled = True
     lngAnzahl = m_SubForm.Recordset.RecordCount
     If m_SubForm.SelTop = 1 Then
         bolErster = True
     End If
     If m_SubForm.AllowAdditions = True Then
         If m_SubForm.SelTop + m_SubForm.SelHeight - 1 >= lngAnzahl Then
             bolLetzter = True
         End If
     Else
         If m_SubForm.SelTop + m_SubForm.SelHeight - 1 = lngAnzahl Then
             bolLetzter = True
         End If
     End If
     If m_SubForm.SelHeight = 0 Then
         DisableUp
         DisableDown
     Else
         If bolErster Then
             DisableUp
         End If
         If bolLetzter Then
             DisableDown
         End If
     End If
End Sub

Listing 1: Prozedur zum Aktivieren und Deaktivieren der Schaltflächen

Dann ermitteln wir die Anzahl der enthaltenen Datensätze und speichern sie in der Variablen lngAnzahl. Die beiden Variablen bolErster und bolLetzter verwenden wir, um einzustellen, dass der Benutzer den ersten und/oder letzten Datensatz markiert hat.

Anschließend prüfen wir, ob die Eigenschaft SelTop den Wert 1 aufweist, was schon einmal bedeutet, dass der erste Datensatz markiert ist. Also stellen wir die Variable bolErster auf den Wert True ein.

Dann unterscheiden wir nach dem Wert der Eigenschaft AllowEditions, wie es weitergeht. Hat sie den Wert True, prüfen wir, ob der Ausdruck m_SubForm.SelTop + m_SubForm.SelHeight – 1 >= lngAnzahl wahr ist.

Wir ermitteln im ersten Teil also den Index des letzten markierten Eintrags und prüfen, ob dieser größer oder gleich der Anzahl der Recordsets im Unterformular ist. Ist das der Fall, erhält bolLetzter den Wert True. Im Else-Teil prüfen wir mit einer ähnlichen Bedingung, die sich nur im Vergleichsoperator von der ersten unterscheidet, ob der Index des letzten markierten Eintrags der Anzahl der Recordsets entspricht und legen dann den Wert von bolLetzter auf True fest.

Wenn kein Eintrag markiert ist, SelHeight also den Wert 0 liefert, rufen wir direkt die beiden Prozeduren DisableUp und DisableDown auf, damit die Schaltflächen deaktiviert werden. Anderenfalls rufen wir DisableUp nur auf, wenn bolErster den Wert True hat und DisableDown nur dann, wenn bolLetzter den Wert True hat.

Die beiden Prozeduren DisableUp und DisableDown deaktivieren nur jeweils die entsprechenden Schaltflächen:

Private Sub DisableUp()
     Me!cmdUp.Enabled = False
     Me!cmdTop.Enabled = False
End Sub
Private Sub DisableDown()
     Me!cmdDown.Enabled = False
     Me!cmdBottom.Enabled = False
End Sub

Damit haben wir die Aktivierung und Deaktivierung der Schaltlfächen erledigt. Diese reagieren jetzt wie gewünscht – siehe Bild 7:

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