Reihenfolge mit Klasse

Wann immer Kriterien sich nicht nach dem Alphabet oder nach einem Zahlenwert sortieren lassen und eine individuelle Sortierung gefordert ist, entsteht Programmieraufwand. Sie müssen die betroffene Datenherkunft um ein Sortierfeld erweitern und einige Schaltflächen oder anderweitige Möglichkeiten zum Einstellen der Sortierung bereitstellen. Dieser Beitrag stellt eine Klasse vor, mit der Sie die benötigten Techniken mit wenigen Codezeilen einbinden.

In diesem Beitrag lernen Sie zunächst zwei Anwendungsfälle für den Einsatz der Reihenfolge-Klasse clsOrder kennen:

  • Einsatz in einem Formular mit einem Listenfeld
  • Einsatz in einem Formular mit einem Unterformular in der Datenblattansicht

Anschließend erfahren Sie mehr über die in der Klasse verwendete Technik, um später eigene Anpassungen vornehmen zu können. In beiden Fällen sollen Daten der Tabelle tblKategorien der Südsturm-Datenbank angezeigt und sortiert werden. Damit dies funktioniert, fügen Sie der Tabelle ein Feld namens ReihenfolgeID hinzu (s. Bild 1).

pic001.png

Bild 1: Kategorientabelle mit dem Feld ReihenfolgeID

Reihenfolge in Listenfeldern

Um die Daten dieser Tabelle in einem Listenfeld darzustellen und die Datensätze durch ändern des Wertes des Feldes ReihenfolgeID in der entsprechenden Reihenfolge darzustellen, fügen Sie einem neuen, leeren Formular zunächst ein entsprechendes Listenfeld hinzu. Damit das Listenfeld zu Testzwecken auch den Wert des Feldes ReihenfolgeID anzeigt, stellen Sie zunächst die Eigenschaft Datensatzherkunft auf den folgenden Ausdruck ein – dies sorgt gleichzeitig für die Darstellung in der richtigen Reihenfolge:

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

Außerdem legen Sie für die beiden Eigenschaften Spaltenanzahl und Spaltenbreiten die beiden Werte 3 und 0cm;5cm fest. Auf diese Weise wird der Primärschlüsselwert verborgen, der Kategoriename wird in einer Breite von 5cm angezeigt und der Wert des Feldes ReihenfolgeID nimmt den übrigen Platz ein (das Listenfeld sollte eine entsprechende Breite aufweisen, also etwa 6cm). Das Listenfeld soll den Namen lstKontakte erhalten.

Außerdem fügen Sie dem Formular vier Schaltflächen namens cmdTop, cmdBottom, cmdUp und cmdDown hinzu (s. Bild 2).

pic002.png

Bild 2: Vorbereitung des Listenfeldes mit Schaltflächen zum ändern der Reihenfolge

Nun beginnt der interessante Teil: Das Formular soll mit entsprechender Funktionalität ausgestattet werden. Diese steckt komplett in der Klasse clsOrder.

Sie müssen diese Klasse nun instanzieren, mit einer im Klassenmodul des Formulars deklarierten Objektvariablen referenzieren und einige Eigenschaften für dieses Objekt festlegen. Zu guter Letzt rufen Sie auch noch eine Methode dieser Klasse auf.

Grundvoraussetzung ist also, dass sich die Klasse clsOrder in der gleichen Datenbank befindet.

Sollten Sie die Beispiele in einer neuen Datenbank ausprobieren wollen, importieren Sie also diese Klasse aus der Beispieldatenbank in die Zieldatenbank (Sie können auch den kompletten Quellcode der Klasse kopieren, in ein neues Klassenmodul einfügen und dieses unter dem Namen clsOrder speichern).

Danach weisen Sie der Eigenschaft Beim Laden des Formulars den Wert [Ereignisprozedur] zu und klicken auf die Schaltfläche mit den drei Punkten, um die entsprechende Ereignisprozedur anzulegen. Fügen Sie im Kopf des Moduls die folgende Deklarationszeile ein:

Dim objOrder As clsOrder

Komplettieren Sie dann die Ereignisprozedur Form_Load wie folgt, wobei Sie die Eigenschaften und Methoden ganz einfach wie in Bild 3 per IntelliSense auswählen:

pic005.png

Bild 3: Auswahl von Eigenschaften und Methoden der Klasse clsOrder per IntelliSense

Private Sub Form_Load()
    Set objOrder = New clsOrder
    With objOrder
        Set .Listbox = Me!lstKontakte
        .Table = "tblKategorien"
        .OrderField = "ReihenfolgeID"
        .PrimaryKeyField = "KategorieID"
        Set .Commandbutton_Top = Me!cmdTop
        Set .Commandbutton_Bottom = Me!cmdBottom
        Set .Commandbutton_Up = Me!cmdUp
        Set .Commandbutton_Down = Me!cmdDown
        .FillOrderID
    End With
End Sub

Die erste Zeile erzeugt zunächst eine neue Instanz der Klasse clsOrder und speichert einen Verweis darauf in der Objektvariablen objOrder.

Die zweite Zeile legt fest, dass die Sortierfunktion auf die Daten des Listenfeldes lstKontakte angewendet werden soll. Dazu übergeben Sie einen Verweis auf dieses Steuerelement an die Eigenschaft Listbox. Die Eigenschaft Table legt fest, auf welche Tabelle sich die änderung der Reihenfolge bezieht – in diesem Falle tblKategorien.

Sie können auch eine Abfrage angeben, diese muss jedoch aktualisierbar sein. Der Eigenschaft OrderField übergeben Sie den Namen des Feldes, nach dem die Daten sortiert werden sollen, also ReihenfolgeID. PrimaryKeyField erwartet schließlich den Namen des Primärschlüsselfeldes der Tabelle, hier KontaktID.

Die folgenden vier Anweisungen definieren, welche Schaltflächen mit den Funktionen zum ändern der Reihenfolge versehen werden sollen. Schließlich rufen Sie noch die Methode FillOrderID auf. Diese Methode füllt eventuell noch leere Sortierfelder mit den entsprechenden Zahlenwerten auf.

Wenn Sie nun in die Formularansicht wechseln, können Sie die Schaltflächen direkt einsetzen, um die Reihenfolge des aktuell aktivierten Eintrags im Listenfeld zu ändern – vorausgesetzt, es ist überhaupt ein Eintrag aktiviert.

Wenn das Listenfeld beim Öffnen den Wert Null hat, ist keine der Schaltflächen aktiviert. Klicken Sie auf das oberste Listenelement, werden die beiden Schaltflächen zum Verschieben des aktuellen Eintrags ganz nach oben oder um eine Position nach oben deaktiviert (siehe Bild 4). Ist hingegen der unterste Eintrag ausgewählt, sind die beiden Schaltflächen zum Verschieben des aktuellen Eintrags um eine Position nach unten oder ganz nach unten deaktiviert.

pic003.png

Bild 4: Listenfeld mit aktiven Reihenfolge-Funktionen

Reihenfolge für Unterformulare

Die gleiche Technik funktioniert auch für Formulare, die Daten einer Tabelle in einem Unterformular in der Datenblattansicht anzeigen (Gleiches gilt für Endlosformulare). In der Beispieldatenbank finden Sie zwei Formulare namens frmKategorien und sfmKategorien, wobei Letzteres als Unterformular in der Datenblattansicht definiert ist.

Das Unterformular ist über die Eigenschaft Datenherkunft an die Tabelle tblKategorien gebunden und zeigt die drei Felder KategorieID, Kategoriename und ReihenfolgeID an (s. Bild 5). Auch hier gibt es vier Schaltflächen, die in diesem Fall cmdGanzNachUnten, cmdNachUnten, cmdNachOben und cmdGanzNachOben heißen.

pic004.png

Bild 5: Konstellation mit Haupt- und Unterformular

Der Code zum Einbinden und Aktivieren des Klassenmoduls clsOrder sieht hier ein wenig anders aus:

Dim objOrder As clsOrder
Private Sub Form_Load()
    Set objOrder = New clsOrder
    With objOrder
        Set .Datasheet = Me!sfmKategorien.Form
        .OrderField = "ReihenfolgeID"
        .PrimaryKeyField = "KategorieID"
        .Table = "tblKategorien"
        Set .Commandbutton_Bottom = Me!cmdGanzNachUnten
        Set .Commandbutton_Down = Me!cmdNachUnten
        Set .Commandbutton_Top = Me!cmdGanzNachOben
        Set .Commandbutton_Up = Me!cmdNachOben
        .FillOrderID
    End With
End Sub

Der große Unterschied liegt darin, dass hier nicht die Eigenschaft Listbox, sondern die Eigenschaft Datasheet gefüllt wird – in diesem Fall mit einem Verweis auf das im Unterformularsteuerelement enthaltene Formular.

Datenblatt-Spezialitäten

Wenn Sie die Reihenfolge von Daten im Datenblatt ändern möchten, müssen Sie eigentlich einiges beachten: So kann es ja zum Beispiel vorkommen, dass der Datensatzzeiger auf dem neuen Datensatz landet, dass ein Datensatz gelöscht wird oder dass der Benutzer einen neuen Datensatz anlegt. Die Klasse clsOrder kümmert sich um all diese Fälle: Beim Anlegen eines neuen Datensatzes werden die Schaltflächen zum Verschieben des Datensatzes bis zum Speichern zunächst deaktiviert.

Erst nach dem Speichern des Datensatzes weist clsOrder dem Feld ReihenfolgeID einen Wert zu, der dem größten bisher vergebenen Wert plus eins entspricht. Außerdem werden dann wieder die entsprechenden Schaltflächen zum ändern der Reihenfolge aktiviert beziehungsweise deaktiviert.

Vorstellung der verwendeten Techniken

Für Access-Entwickler, die selbst Hand an die Klasse clsOrder anlegen möchten, um Anpassungen durchzuführen, folgt nun der technisch interessante Teil des Beitrags. Die Klasse verwendet zunächst einmal vier Objektvariablen, welche die Verweise zu den vier Schaltflächen zum Einstellen der Reihenfolge aufnehmen sollen:

Private WithEvents m_Up As CommandButton
Private WithEvents m_Down As CommandButton
Private WithEvents m_Top As CommandButton
Private WithEvents m_Bottom As CommandButton

Diese Objektvariablen sind mit dem Schlüsselwort WithEvents gekennzeichnet, damit wir innerhalb dieser Klasse Ereignisprozeduren für die mit den Variablen referenzierten Objekte definieren können.

Immerhin soll ja die komplette Funktionalität beim Klicken der Schaltflächen in der Klasse clsOrder enthalten sein und nicht im Klassenmodul des Formulars – dieses soll nur die paar Konfigurationsanweisungen aufnehmen.

Wenn Sie per VBA etwa die Eigenschaft CommandButton_Up mit einem Verweis auf die Schaltfläche cmdNachOben füllen, löst dies die folgende Prozedur aus:

Public Property Set Commandbutton_Up(cmd As CommandButton)
    Set m_Up = cmd
    Set m_Form = cmd.Parent
    m_Form.OnCurrent = "[Event Procedure]"
    m_Up.OnClick = "[Event Procedure]"
End Property

Diese versieht die Objektvariable m_Up mit dem Verweis auf die betroffene Schaltfläche. Danach wird die Objektvariable m_Form mit einem Verweis auf das Parent-Objekt der Schaltfläche gefüllt, also dem Formular, in dem diese sich befindet.

Der Eigenschaft OnCurrent der Objektvariable m_Form wird der Wert [Event Procedure] zugewiesen, was gleichbedeutend mit dem Füllen der Eigenschaft Beim Anzeigen mit [Ereignisprozedur] im Eigenschaftsfenster ist.

Nur dass Access in diesem Fall nicht im Klassenmodul des Formulars, sondern im aktuellen Klassenmodul clsOrder nach einer entsprechenden Ereignisprozedur sucht. Im gleichen Zuge wird auch noch die Eigenschaft OnClick der soeben referenzierten Schaltfläche mit [Event Procedure] gefüllt.

Die Prozeduren für die übrigen Schaltflächen sind prinzipiell genau gleich aufgebaut. Dass alle die Objektvariable m_Form füllen, liegt daran, dass nicht zwangsläufig alle Schaltflächen verwendet werden. Das Formular der Schaltfläche soll jedoch auf jeden Fall referenziert werden. Die Variable für das Formular wird so deklariert:

Private WithEvents m_Form As Form

Außerdem soll entweder ein Listenfeld oder ein Unterformular angegeben werden, dessen Daten mit den angegebenen Schaltflächen sortiert werden können. Der entsprechende Verweis landet in einer der beiden folgenden Objektvariablen:

Private WithEvents m_Datasheet As Form
Private WithEvents m_Listbox As Listbox

Das Zuweisen des Listbox-Steuerelements ist ganz einfach. Hier werden nur der Verweis auf das Listenfeld übergeben und die Ereignisprozedur Nach Aktualisierung vorbereitet:

Public Property Set Listbox(lst As Listbox)
    Set m_Listbox = lst
    m_Listbox.AfterUpdate = "[Event Procedure]"
End Property

Diese Ereignisprozedur soll später dafür sorgen, dass je nach aktiviertem Listenfeldeintrag die richtigen Schaltflächen aktiviert werden.

Beim Unterformular kommen noch zwei weitere Ereignisprozeduren hinzu, eine wird ersetzt – außerdem wird die Referenz auf das Unterformular in der Variablen m_Datasheet gespeichert:

Public Property Set Datasheet(frm As Form)
    Set m_Datasheet = frm
    m_Datasheet.OnCurrent = "[Event Procedure]"
    m_Datasheet.BeforeUpdate = "[Event Procedure]"
    m_Datasheet.OnDelete = "[Event Procedure]"
End Property

Die Ereignisprozedur AfterUpdate des Listenfeldes wird durch BeforeUpdate ersetzt. Der Grund: Es kann sein, dass dieses Ereignis durch das Anlegen eines neuen Datensatzes ausgelöst wird. In diesem Fall soll vor dem Speichern noch ein entsprechender Wert in das Feld zur Festlegung der Reihenfolge (im Beispiel ReihenfolgeID) eingetragen werden. Das Ereignis Beim Anzeigen (OnCurrent) wird bei jedem Datensatzwechsel ausgelöst, das Ereignis Beim Löschen (OnDelete) beim Löschen eines Datensatzes.

Die drei Eigenschaften Table, PrimaryKeyField und OrderField werden für Unterformulare wie auch für Listenfelder gleichermaßen benötigt. Die entsprechenden Werte landen nach der Zuweisung an die entsprechenden Eigenschaften in diesen lokalen Variablen:

Private m_PKField As String
Private m_Orderfield As String
Private m_Table As String

Die Property Let-Prozeduren, welche die entsprechenden Eigenschaften bereitstellen, nehmen schlicht die übergebenen Werte entgegen und tragen diese in die oben deklarierten Variablen ein:

Public Property Let Table(str As String)
    m_Table = str
End Property
Public Property Let PrimaryKeyField(str As String)
    m_PKField = str
End Property
Public Property Let OrderField(str As String)
    m_Orderfield = str
End Property

Außerdem muss zur ordnungsgemäßen Funktion einmalig die Methode FillOrderID aufgerufen werden. Diese sieht wie in Listing 1 aus und ruft zunächst eine Funktion namens CheckMembers auf.

Listing 1: Sortierung per Reihenfolge-Feld aktualisieren

Public Sub FillOrderID()
    Dim db As DAO.Database
    Dim rst As DAO.Recordset
    If CheckMembers = False Then
         Exit Sub
    End If
    Set db = CodeDb
    Set rst = db.OpenRecordset("SELECT " & m_PKField & ", " & m_Orderfield & " FROM " & m_Table _
        & " ORDER BY " & m_Orderfield, dbOpenDynaset)
    Do While Not rst.EOF
         rst.Edit
        rst(m_Orderfield) = rst.AbsolutePosition + 1
        rst.Update
        rst.MoveNext
    Loop
End Sub

Diese Funktion nimmt einige Prüfungen vor, welche die Funktionsfähigkeit der Klasse sicherstellen. Sind die Prüfungen nicht erfolgreich, wird die Prozedur abgebrochen – mehr dazu weiter unten.

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