Ungebundene List- und ComboBox per Callback

Im Beitrag “Ungebundene Listen und Kombis mit Daten füllen” (www.access-im-unternehmen.de/1440) haben wir uns angesehen, wir man Kombinations- und Listenfelder über das Zuweisen einer Wertliste oder mit der AddItem-Methode füllen kann. Es gibt noch eine interessante Alternative, von der wir hoffen, dass wir damit sogar noch einige Einträg mehr zu den Listen-Steuerelementen hinzufügen können als mit den oben genannten Methoden. Bei dieser Alternative handelt es sich um eine Technik, die eher stiefmütterlich behandelt wird: Die Callbackfunktion. Wie man diese nutzt und ob man tatsächlich mehr Daten als mit einer Wertliste in ungebundenen Listen-Steuerelementen anzeigen, untersuchen wir in diesem Beitrag.

Einfaches Beispiel für das Füllen per Callbackfunktion

Bevor wir uns daran begeben, die Daten umfangreicher Tabellen in ein Listenfeld zu füllen, schauen wir und die grundlegende Vorgehensweise beim Verwenden von Callback-Funktionen zum Füllen von Kombinations- und Listenfeldern an. Bei Verwendung einer Callbackfunktion nutzen wir keine der drei bekannten Möglichkeiten für die Eigenschaft Herkunftsart, sondern wir geben dort den Namen einer Funktion an.

Diese legen wir außerdem im Klassenmodul des Formulars an, in dem sich auch das Kombinations- oder Listenfeld befindet. Die Callbackfunktion muss einen speziellen Aufbau haben. Zumindest die Parameter müssen nach einem festen Schema angegeben werden und auch innerhalb der Funktion sind einige Regel zu beachten, damit diese wie gewünscht arbeitet. Für ein einfaches Beispiel fügen wir einem Formular ein Listenfeld hinzu, für dessen Eigenschaft Herkunftstyp wie den Namen der Callbackfunktion angeben, in diesem Fall Beispielcallback (siehe Bild 1).

Listenfeld mit Callback-Funktion

Bild 1: Listenfeld mit Callback-Funktion

Aufbau und Parameter der Callbackfunktion

Die Callbackfunktion erwartet bestimmte Parameter, die beim Aufruf automatisch von dem Steuerelement, dem sie zugeordnet ist, übergeben werden. Der Kopf der Prozedur sieht so aus:

Function Beispielcallback(ctl As Control, lngID As Long, _
          lngRow As Long, lngColumn As Long, _
          intCode As Integer) As Variant

Die hier verwendeten Parameter liefern die folgenden Werte:

  • ctl: Verweis auf das Steuerelement, für welches die Callbackfunktion aufgerufen wurde. Dies ist sinnvoll, falls es mehr als ein Steuerelement gibt, das die gleiche Callbackfunktion aufruft – was aber eher selten der Fall sein dürfte.
  • lngID: Liefert eine eindeutige ID für das aufrufende Steuerelement.
  • lngRow: Gibt an, für welche Zeile ein Wert abgefragt wird.
  • lngColumn: Gibt an, für welche Spalte ein Wert abgefragt wird.
  • intCode: Gibt die aktuelle Funktion des Aufrufs an.

Mit dem Rückgabewert mit dem Datentyp Variant übergeben wir die jeweils angeforderten Informationen an das Steuerelement.

Aufbau der Callbackfunktion

Die Callbackfunktion muss, damit sie korrekt funktioniert und das Steuerelement wie gewünscht gefüllt wird, die mit dem Parameter intCode angefragten Informationen zusammenstellen und zurückliefern. intCode kann verschiedene Werte annehmen, für die jeweils Konstanten definiert sind. Diese lauten:

  • acLBInitialize (0): Dieser Wert für intCode wird beim ersten Aufruf der Callback-Funktion übergeben. Hier wird geprüft, ob das Kombinations- oder Listenfeld überhaupt mit dieser Funktion gefüllt werden soll. Falls ja, muss der Wert True als Rückgabewert der Funktion festgelegt werden.
  • acLBOpen (1): Beim Aufruf der Funktion mit dem Wert acLBOpen für den Parameter intCode erwartet Access eine Zahl als Rückgabewert, die das Steuerelement, von dem aus die Callback-Funktion aufgerufen wurde, eindeutig identifiziert. Hintergrund ist, dass man theoretisch mehrere Steuerelemente mit der gleichen Callbackfunktion ausstatten kann. Wenn sichergestellt ist, dass nur ein Steuerelement diese Funktion als Wert für die Eigenschaft Herkunftsart verwendet, können Sie hier einfach den Wert 1 als Funktionswert angeben. Anderenfalls geben Sie das Ergebnis der Funktion Timer zurück, welche die Zeit in Millisekunden berechnet – dies geht nur schief, wenn die Funktion zweimal in der gleichen Millisekunde aufgerufen wird, was unwahrscheinlich ist.
  • acLBGetRowCount (3): Dieser Aufruf der Funktion soll die Anzahl der zu füllenden Zeilen ermitteln. Dies ist der geeignete Ort, um die anzuzeigenden Daten zusammenzustellen.
  • acLBGetColumnCount (4): Dieser Funktionsaufruf erwartet die Übergabe der Anzahl der zu füllenden Spalten.
  • acLBGetColumnWidth (5): Dieser Funktionsaufruf erwartet die Übergabe der Breiten der zu füllenden Spalten.
  • acLBGetValue (6): Die Callback-Funktion wird für jedes anzuzeigende Elemente einmal mit dem Wert acLBGetValue für den Parameter intCode aufgerufen. Die Anzahl der Elemente ergibt sich aus dem Produkt aus Zeilen- und Spaltenzahl. Welche Zeile und Spalte gerade abgefragt wird, können Sie den weiteren Parametern lngRow und lngColumn entnehmen.
  • acLBGetFormat (7): Auch der Aufruf mit diesem Parameterwert erfolgt für jedes anzuzeigende Element einmal. Hier können Sie die Formatierung der Listeneinträge vornehmen (standardmäßig -1).
  • acLBClose (8), acLBEnd (9): Diese beiden Parameter werden in weiteren Aufrufen der Callback-Funktion nach dem Füllen des Steuerelements übergeben. Damit können Sie beispielsweise den Zeitpunkt erfassen, zu dem das Steuerelement komplett gefüllt ist.

Beispiel für eine Callbackfunktion

Nachfolgend haben wir das denkbar einfachste Beispiel für eine Callbackfunktion zusammengestellt:

Function Beispielcallback(ctl As Control, _
         lngID As Long, lngRow As Long, _
         lngColumn As Long, intCode As Integer) _
         As Variant
     Select Case intCode
         Case acLBInitialize
             Beispielcallback = True
         Case acLBOpen
             Beispielcallback = 1
         Case acLBGetRowCount
             Beispielcallback = 1
         Case acLBGetColumnCount
             Beispielcallback = 1
         Case acLBGetColumnWidth
             Beispielcallback = -1
         Case acLBGetValue
             Beispielcallback = "Test"
         Case acLBGetFormat
             Beispielcallback = Null
         Case Else
             Debug.Print intCode
     End Select
End Function

[

Dabei werden in diesem Fall alle Codes für den Parameter intCode wie oben angegeben nacheinander durchlaufen. Wichtig dabei ist: Der Aufruf von acLBGetColumnCount erfolgt einmal. Je nachdem, wieviele Spalten dort zurückgegeben werden, erfolgt die entsprechende Anzahl von Aufrufen mit dem Parameter acLBGetColumnWidth. Dabei wird mit dem Parameter lngColumn der 0-basierte Index der Spalte übergeben, für welche die Breite abgefragt werden soll. Hier liefern wir immer den Wert -1 zurück, wenn wir keine explizite Spaltenbreite definieren wollen. Danach wird die Anzahl der Zeilen abgefragt (acLBGetRowCount).

Schließlich erfolgen abwechselnde Aufrufe mit den Werten acLBGetValue und acLBGetFormat für den Parameter intCode mit einer Anzahl Wiederholungen, die dem Produkt aus Spalten und Zeilen entspricht.

In dem Beispiel oben wollen wir nur eine Zeile und eine Spalte füllen, also insgesamt nur einen Wert im Listenfeld anlegen. Das führt zu dem Ergebnis aus Bild 2.

Listenfeld mit einem Beispielwert

Bild 2: Listenfeld mit einem Beispielwert

Daten aus einer Tabelle per Callbackfunktion laden

Damit wenden wir uns nun einem echten Anwendungsfall zu – dem Füllen eines Listenfeldes mit acht Spalten und vielen hundert Zeilen. Dabei wollen wir ein Listenfeld namens lstKunden mit den Daten der Tabelle tblKunden füllen, die mehr als 3.000 Datensätze aufweist. Dies war für das Füllen eines Listenfeldes per Wertliste oder mit der AddItem-Methode deutlich zu viel. Wir werden sehen, ob wir mit der Callbackfunktion mehr Daten in das Listenfeld einlesen können. Das Listenfeld bereiten wir wie in Bild 3 vor, indem wir die Eigenschaften Spaltenanzahl und die Spaltenbreiten einstellen. Außerdem legen wir für das Listenfeld die Eigenschaften Horizontaler Anker und Vertikaler Anker auf Beide fest. Als Herkunftstyp legen wir die Callbackfunktion Kundenliste fest. Im Kopf des Klassenmoduls des Formulars deklarieren wir die folgenden beiden Variablen:

Vorbereiten des Listenfeldes für viele Kundendaten

Bild 3: Vorbereiten des Listenfeldes für viele Kundendaten

Dim db As DAO.Database
Dim rst As DAO.Recordset

Die Callbackfunktion gestalten wir wie folgt. Der Kopf unterscheidet sich im Namen vom vorherigen Beispiel:

Function Kundenliste(ctl As Control, _
         lngID As Long, lngRow As Long, _
         lngColumn As Long, intCode As Integer) _
         As Variant

Beim Initialisieren erfolgen jedoch einige neue Schritte. Wir lesen hier das Recordset basierend auf den Daten der Tabelle tblKunden in die Variable rst ein. Danach bewegen wir den Datensatzzeiger einmal auf den letzten und dann wieder auf den ersten Datensatz, damit wir später mit der RecordCount-Funktion die Anzahl der Datensätze ermitteln können. Dann legen wir die Anzahl der benötigten Spalten für das Listenfeld fest, die wir mit rst.Fields.Count aus der Anzahl der Felder der Datensatzherkunft beziehen. Als ersten Rückgabewert liefern wir True zurück:

     Select Case intCode
         Case acLBInitialize
             Set db = CurrentDb
             Set rst = db.OpenRecordset( _
                 "SELECT * FROM tblKunden", dbOpenSnapshot)
             rst.MoveLast
             rst.MoveFirst
             Me!lstKunden.ColumnCount = rst.Fields.Count
             Kundenliste = True

Der Schritt acLBOpen erwartet wie zuvor den Wert 1:

         Case acLBOpen
             Kundenliste = 1

Für die Ermittlung der anzuzeigenden Zeilen im Schritt acLBGetRowCount lesen wir dann aus dem in rst gespeicherten Recordset die Anzahl der Datensätze und geben diese zurück:

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