Button Home/Top/Zurück


Kombinations- und Listenfelder




Kombinationsfelder und Listenfelder sind von ihren Eigenschaften und Methoden ziemlich identisch.
In der Access-Hilfe steht ja auch, daß ein Kombinationsfeld eine Kombination aus Textfeld und Listenfeld ist.
Darum werde ich hier auch keine großartige Unterscheidung zwischen den beiden machen, da mit Ausnahme der Mehrfachauswahl alles auf beide Steuerelemente zutrifft.

Kombinations- und Listenfelder werden verwendet um dem Benutzer bei der Datenerfassung vordefinierte Werte auswählen zu lassen.
Ob man ein Listenfeld oder ein Kombinationsfeld verwendet hängt vom persönlichem Geschmack, vom Platz auf dem Formular und von der Aufgabenstellung ab.

Ich möchte hier nicht oberlehrerhaft erscheinen aber es gibt zwei Faustregeln für den Einsatz von Kombinations- oder Listenfeldern:
  1. In einem Kombinationsfeld sollten nicht mehr als 20 Datensätze zur Auswahl angeboten werden.
  2. In einem Listenfeld sollten max. 5 * die Anzahl der sichtbare Zeilen zur Auswahl angeboten werden.
Diese Regeln haben sich in etlichen Projekten bewährt. Mein lieblings Beispiel, um diese Regeln zu untermauern ist die Auswahl von Postleitzahl und Ort über eine Kombinationsfeld. Es gibt z.Zt. über 40.000 Postleitzahlen in Deutschland. Jeder der eine Postleitzahl oder einen Ort aus diesen 40.000 Einträgen eines Kombinationsfeldes auswählen muß, wird den Entwickler verfluchen.
Außerdem sollte man nicht außer Acht lassen, daß diese zig Tausend Datensätze erst einmal in den Arbeitsspeicher geladen werden müssen. Wenn das Ganze, bei einer Aufteilung der Datenbank in Front- und Backend, auch noch über das Netz geladen werden muß, dann kann man sich vorstellen das die Performance zu wünschen übrig läßt.
Hinweis:
Um den Benutzer eine Auswahl aus einer großen Datenmenge vornehmen zu lassen, empfiehlt sich ein Suchdialog. Wie so etwas aussehen kann, ist im Beispiel Popup-Suchdialog beschrieben.

So, genug geschwafelt, ran an den Feind Zwinker Smilie

In den nachfolgenden Erläuterungen gehe ich von einer deutschen Access 97 Version aus. Dies ist erwähnenswert, da sich die Bezeichnungen der Eigenschaften und die Angaben von Maßen zwischen einer deutschen und einer englischen Version unterscheiden (ist ja klar).

Noch ein kleiner Hinweis zur den nachfolgenden Ausführungen.
Die Eigenschaften bzw. Werte der Eigenschaften werden einmal in "deutsch" und einmal in "englisch" (in Klammern) angegeben. Die "deutsche" Bezeichnung entspricht der im Eigenschaftenfenster, die "englische" Bezeichnung der, wie sie in VBA verwendet werden muß.

Als erstes werden wir uns mit den Grundlagen beschäftigen, bevor wir uns mit Tips und Tricks zu den Themen zusätzliche Daten anzeigen, abhängige Kombinations- und Listenfelder, ein Kombinationsfeld um den Eintrag "Alle" erweitern, Werte aus einem Listenfeld in ein anderes übertragen, Mehrfachauswahl eines Listenfeldes verwenden beschäftigen.


Die wichtigsten Eigenschaften von Listenfeldern:

Herkunftstyp (RowSourceType)


In dieser Eigenschaft wird der Typ der Datensatzherkunft angegeben. Folgende Einstellungen sind möglich:
  • Abfrage/Tabelle (Table/Query): Die Daten des Kombinations-/Listenfeldes werden aus einer Abfrage oder Tabelle entnommen.
  • Wertliste (Value List): Die Daten des Kombinations-/Listenfeldes werden in einer Wertliste angegeben. Die Werte werden in der Datensatzherkunft, mit Semikolon getrennt, angegeben.
  • Feldliste (Field List): Die wohl einfachste Art die Felder einer Abfrage/Tabelle in einem Kombinations-/Listenfeld aufzuführen, denn bei diesem Herkunftstyp werden die Felder der Abfrage/Tabelle, welche als Datensatzherkunft definiert ist, angezeigt.

Datensatzherkunft (RowSource)

Der Inhalt der Datensatzherkunft ist abhängig von der Einstellung des Herkunftstyp des Kombinations-/Listenfeldes.

Für den Fall, daß als Herkunftstyp Abfrage/Tabelle oder Feldliste angegeben wurde, kann der Name einer Abfrage/Tabelle oder ein SQL-SELECT-Statement angegeben werden. Das SQL-SELECT-Statement macht bei dem Herkunftstyp Feldliste zwar keinen großen Sinn, funktioniert aber trotzdem .
Für die Erstellung des SELECT-Statements kann der Access-Abfrageentwurf über den Button Abfrageentwurf starten gestartet werden. Dieser Button erscheint im Eigenschaftendialog, am rechten Rand des Feldes Datensatzherkunft, wenn der Cursor in dieses Feld positioniert wird.

Für den Fall, daß als Herkunftstyp Wertliste angegeben wurde, muß in der Datensatzherkunft die Wertliste angegeben werden. Die einzelnen Werte sind dabei durch Semikolon voneinander zu trennen. Dies könnte für die Abteilungsliste eines Unternehmens so aussehen:

Datensatzherkunft/Wertlsite
Beispiel: FrmWertliste01

Das selbe in VBA:
Private Sub Form_Current()
    '
    ' Herkunftstyp auf "Wertliste" einstellen
    '
    Me.LbTest.RowSourceType = "Value List"
    '
    ' Wertliste als Datensatzherkunft zuweisen
    '
    Me.LbTest.RowSource = "Personal;Fertigung;Qualitätssicherung;Lager;Logistik"
 
End Sub

Beispiel: FrmWertliste02

Es empfiehlt sich - in VBA - den Herkunftstyp (RowSourceType) und die Datensatzherkunft (RowSource) immer gemeinsam einzustellen. Dies dienst als Sicherheit für den Fall, daß Jemand die Einstellungen im Formularentwurf ändert. Wird nun in VBA lediglich die Datensatzherkunft gesetzt, kann dies zu unangenehmen Effekten führen.

Spaltenanzahl (ColumnCount)


In der Eigenschaft Spaltenanzahl wird angegeben, wie viele Spalten das Kombinations-/Listenfeld haben soll. Es muß hier ein Wert zwischen 1 und 255 angegeben werden.

Zwischen der Spaltenanzahl, der Datensatzherkunft und dem Herkunftstyp besteht ein Zusammenhang.

Wird als Herkunftstyp Feldliste angegeben, so muß als Spaltenanzahl 1 verwendet werden. Wird eine größere Spaltenanzahl angegeben, so werden die Feldnamen entsprechend mehrfach angezeigt.

Wird als Herkunftstyp Tabelle/Abfrage angegeben und als Datensatzherkunft der Name einer Tabelle oder Abfrage, dann werden die ersten n Felder der Tabelle/Abfrage angezeigt. Dabei ist n der Wert, welcher in der Spaltenanzahl angegeben wurde. Die Reihenfolge der Tabellen/Abfragefelder ist von ihrer Position im Tabellen/Abfrageentwurf abhängig.
Will man z.B. die Felder 3, 5 und 7 einer Tabelle sehen, dann muß man als Datensatzherkunft ein SELECT-Statement angeben, in dem die entsprechenden Felder, in der entsprechende Reihenfolge selektiert werden.

Wird als Herkunftstyp Wertliste angegeben, so ist für jede Spalte ein Wert in der Liste vorzusehen. D.h. eine Wertliste für ein Listenfeld mit 3 Spalten und 2 Zeilen muß so aussehen:


Zeile 1/Spalte 1;Zeile 1/Spalte 2;Zeile 1/Spalte 3; Zeile 2/Spalte 1;Zeile 2/Spalte 2;Zeile 2/Spalte 3

In der Listbox sieht das Ganze dann so aus:

Listbox mit Wertliste
Beispiel: FrmWertliste03


Spaltenbreiten (ColumnWidths)


Hier können die Breiten der einzelnen Spalten eines Kombinations-/Listenfeldes eingestellt werden.
Wird in dieser Eigenschaft keine Angabe gemacht (das Feld ist leer), dann werden die Spalten gleichmäßig über die ganze Breite des Feldes aufgeteilt.

Wenn hier eine Angabe erfolgt, dann
  • ist die Breite in Zentimetern anzugeben. Das Kürzel cm wird dabei (gnädigerweise) von Access ergänzt.
  • muß für jede Spalte des Feldes eine Breite angegeben werden (mit Ausnahmen)
Eine der Ausnahmen ist, das z.B. die Breite der ersten und/oder der letzten Spalten weggelassen werden kann. Dazu folgendes Beispiel:

Listbox 2 Spalten

Hier ist lediglich für die 1. Spalte ein Breite von 1,7 cm angegeben. Der verbleibende Platz im Listenfeld geht an die 2. Spalte. Im nächsten Beispiel ist für die mittlere Spalte keine Breite angegeben.

Listbox 3 Spalten
Beispiel: FrmSpaltenbreiten01

Das sieht im Eigenschaftenfenster so aus:

Listbox, Eigenschaft Spaltenbreite

D.h. die erste Spalte ist immer 1,702cm und die letzte Spalte immer 1cm breit. Der verbleibende Platz im Listenfeld wird der mittleren Spalte eingeräumt.
Hinweis 1:
Bei der Zuweisung der Spaltenbreiten via VBA muß die Maßeinheit cm mit angegeben werden, anderenfalls werden die angegebenen Werte als Twips interpretiert (1 cm = 567 Twips) was zu sehr schmalen Spalten führt.
	  Me.LbTest.ColumnWidths = "1,4cm;2cm"
	  MsgBox Me.LbTest.ColumnWidths
	  
Nach der Zuweisung in cm stehen die Spaltenbreiten in Twips in der Listbox-Eigenschaft. Das führt bei Debuggen zu einiger Verwirrung.

Hinweis 2:
Spaltenbreiten sollten (nach Möglichkeit) nur für Spalten vergeben werden, die eine feste Formatierung haben (Datum, Uhrzeit, Ja/Nein). Hier ist die Breite dann so einzustellen, daß die Information komplett angezeigt wird. Für alle anderen Spalten sollte keine feste Breite angegeben werden, um z.B. bei einer Größenänderung des Formulars (und damit des Listenfeldes) eine automatische Verbreiterung dieser Spalten zu erhalten.


Spaltenüberschriften (ColumnHeads)

Mit dieser Eigenschaft kann die Anzeige der Spaltenüberschriften im Kombinations-/Listenfeld an, bzw. abgeschaltet werden. Wird die Eigenschaft auf Ja (True) eingestellt, werden die Überschriften angezeigt, wird sie auf Nein (False) eingestellt, werden die Überschriften nicht angezeigt.
Es besteht ein Zusammenhang zwischen der Einstellung in der Eigenschaft Herkunftstyp und dem was als Überschrift angezeigt wird.
  • Feldliste: Hier wird der Name des ersten Tabellen/Abfragefeldes als Überschrift angezeigt. Es ist also nicht sinnvoll, die Spaltenüberschriften bei diesem Herkunftstyp anzuzeigen.
  • Abfrage/Tabelle: Die Namen der Tabellen/Abfragefelder werden als Überschriften angezeigt.
  • Wertliste: Hier werden die Werte der 1. Zeile als Überschriften verwendet.
Für die Wertliste folgendes Beispiel:

Listbox, Überschriften
Beispiel: FrmSpaltenUeberschriften01

Die Wertliste zu diesem Beispiel sieht so aus:
E.Datum;Vor-/Zuname;Angst.;14.04.1990;Heiner von Müller;Ja;28.05.1990;Erwin Schulze;Nein
Die Listbox hat drei Spalten, also werden die ersten drei Werte der Wertliste als Überschriften verwendet.

Gebundene Spalte (BoundColumn)

Mit der Eigenschaft Gebundene Spalte wird festgelegt, welchen Wert das Kombinations-/Listenfeld zurückliefert, sobald ein Eintrag selektiert ist.
Es muß ein Wert zwischen 1 und n angegeben werden, wobei n dem Wert aus der Eigenschaft Spaltenanzahl entspricht. In den allermeisten Fällen handelt es sich hierbei um die Spalte 1, welche mit einer Breite von 0cm festgelegt ist und aus der Datensatzherkunft die ID (Primärschlüssel) zugewiesen bekommt.

Mehrfachauswahl (MultiSelect)
gilt nicht für Kombinationsfelder

Über die Eigenschaft Mehrfachauswahl kann festgelegt werden, ob der Benutzer lediglich ein Zeile des Listenfeldes oder mehrere (ggf. alle) Zeilen selektieren kann. Wird eine Mehrfachauswahl zugelassen, liefert das Listenfeld keinen Wert zurück !! Während bei einfacher Auswahl in VBA über den Namen der Listbox auf den Wert der gebundenen Spalte zugegriffen werden kann, ist dies bei einer Mehrfachauswahl nicht mehr möglich.
Die Eigenschaft Mehrfachauswahl kann folgende Werte annehmen:
  • Keine (0), dann ist keine Mehrfachauswahl möglich.
  • Einzeln (1), dann ist durch Anklicken der Zeilen eine Selektion/Deselektion möglich.
  • Erweitert (2), dann erfolgt die Selektion/Deselektion wie man es vom Explorer her kennt.

Nun einige Tip's und Tricks zum Umgang mit Kombinations- und Listenfeldern

Zusätzliche Daten anzeigen
In den Foren wird ziemlich oft der Wunsch geäußert, zu den in einem Kombinations-/Listenfeld ausgewählten Eintrag zusätzlich Informationen anzuzeigen.
Dazu folgendes Beispiel:

Zusätzliche Daten anzeigen
Beispiel: FrmZusaetzlicheDatenAnzeigen


In diesem Beispiel werden in einen Listenfeld Artikel mit ihrer Artikelnummer und der Artikelbezeichnung angezeigt.
In der Gruppe Artikeldaten werden zusätzlich die Warengruppe sowie der Soll- und Ist-Lagerbestand zum Artikel angezeigt.
Zu guter letzt wird am unteren Rand des Formulars der Lieferant des Artikels ausgegeben.

Der wesentlich Trick bei der Realisierung liegt in der Datensatzherkunft des Listenfeldes begraben. Dort werden nämlich nicht nur die Informationen ermittelt, die für die Anzeige des Listenfeldes notwendig sind, sondern alle Daten die für die Anzeige im Dialog benötigt werden.
In dem Beispiel habe ich eine Abfrage erstellt, um die Informationen aus verschiedenen Tabellen zusammenzutragen. Dies könnte man allerdings auch in einem SQL-Select-Statement in der Datensatzherkunft des Listenfeldes machen.
Im Beispiel ist als Datensatzherkunft folgendes angegeben:
SELECT DISTINCTROW AbfArtikelInfo.ID, AbfArtikelInfo.ArtikelNr, 
                   AbfArtikelInfo.ArtikelBezeichnung, AbfArtikelInfo.Warengruppe, 
                   ArtikelInfo.Sollbestand, AbfArtikelInfo.Istbestand, 
                   AbfArtikelInfo.LieferantID 
FROM AbfArtikelInfo;
	  
Dabei ist AbfArtikelInfo die zuvor erwähnte Abfrage.
Die Eigenschaft Spaltenanzahl des Listenfeldes ist auf 7 eingestellt, um alle Felder der Datensatzherkunft aufzunehmen.
Die Eigenschaft Spaltenbreiten sieht im Beispiel so aus: 0cm;1,404cm;;0cm;0cm;0cm;0cm
D.h. im Listenfeld sind lediglich die Spalten ArtikelNr und ArtikelBezeichnung sichtbar.

Der nächste Trick besteht darin, die Textfelder der Gruppe Artikeldaten mit den einzelnen Spalten des Listenfeldes zu verknüpfen. Das geht über die Column-Auflistung des Listenfeldes.
Bei den Textfeldern ist als Steuerelementinhalt lediglich ein Spalte des Listenfeldes angegeben.
Beim Textfeld Artikelnr.: sieht dies z.B. so aus: =[LbArtikel].[Column](1)
Dabei ist LbArtikel der Name des Listenfeldes, Column die Column-Auflistung des Listenfeldes und (1) besagt, daß auf die 2. Spalte des Listenfeldes zugegriffen werden soll.


Hinweis:
Beim Verweisen auf die Spalten eines Kombinations-/Listenfeldes über die Column-Auflistung ist darauf zu achten, daß die Zählung der Spalten mit 0 beginnt und bei Spaltenanzahl - 1 endet.


Bei der Anzeige des Lieferanten wird ein etwas anderer Weg verwendet. Das hängt damit zusammen, daß die Anschrift des Lieferanten als Memo Textfeld definiert ist, welche nicht (komplett) über eine Kombinations-/Listenfeld-Spalte transportiert werden können. Hier ist der Weg allerdings nur unbedeutend umständlicher, da der Steuerelementinhalt des Textfeldes so aussieht:

=DomWert("[LieferantInfo]";"AbfLieferantInfo";"[ID]=" & Str([LbArtikel].[Column](6)))

Ich gehe mal davon aus, daß die Funktionen DomWert() und Str() bekannt sind bzw. schnell in der Access-Hilfe zu finden sind.
Auch hier verwende ich eine Abfrage, die mir aus den Angaben einer Tabelle die LieferantInfo zusammensetzt. D.h. der Name des Lieferanten und die Anschrift werden zu einem Feld zusammengefaßt.

Hinweis:
Memo-Felder können nicht in voller Länge über die Spalten von Kombinations-/Listenfelder transportiert werden, da diese max. 250 Zeichen aufnehmen können.


So, daß war es schon zu diesem Beispiel.

Abhängige Kombinations- und Listenfelder
Unter abhängigen Kombinations- und Listenfelder versteht man Konstrukte bei denen durch die Auswahl eines Wertes in einem Kombinationsfeld die Anzeige in einem Listenfeld beeinflußt wird.

Dazu folgendes Beispiel:

Abhängige Felder
Beispiel: FrmAbhaenigeFelder01


Im Kombinationsfeld Warengruppe kann ein Wert aus der Tabelle Warengruppen ausgewählt werden. Wird eine Warengruppe selektiert, werden im Listenfeld Artikel zur Warengruppe alle Datensätze aus der Tabelle Artikel angezeigt, die zur gewählten Warengruppe gehören.

Jetzt sollte ich wohl erst mal die beiden Tabellen beschreiben.
Tabelle: Warengruppen    Tabelle: Artikel
ID | Warengruppe         ID | ArtikelNr | ArtikelBezeichnung | LieferantID | WarengruppeID
---+-------------        ---+-----------+--------------------+-------------+----------------
 1 | Obst                 1 | 0-0100    | Apfel              |      1      |       1
 2 | Gemüse               2 | 0-0200    | Birne              |      1      |       1
 3 | Konserven            3 | G-1000    | Gurke              |      1      |       2
 4 | Brot                 4 | G-1010    | Tomate             |      4      |       2
 5 | Molkereiprodukte     :      :                 :                :              :
 6 | Tiefkühlkost        12 | Mopro-07  | Quark              |      5      |       6
Wie man deutlich sehen kann, weisen alle Artikel einer Warengruppe die gleiche WarengruppeID auf. Diese entspricht der ID aus der Tabelle Warengruppe.

Als erstes sehen wir uns nun das Kombinationsfeld Warengruppe näher an.
Als Datensatzherkunft ist dort folgendes SQL-Statement eingetragen:
		
SELECT DISTINCTROW [Warengruppen].[ID], [Warengruppen].[Warengruppe]
FROM   [Warengruppen];

Die Spaltenanzahl ist auf 2 eingestellt, die gebundene Spalte ist 1 ([Warengruppen].[ID]). Das sorgt dafür, daß bei der Auswahl eines Wertes aus dem Kombinationsfeld die ID als Wert zurückgeliefert wird.
Das Kombinationsfeld trägt den Namen CmbWarengruppe. Das ist für den folgenden Teil wichtig.

Das Listenfeld trägt den Namen LbArtikel. Es hat als Datensatzherkunft folgendes SQL-Statement eingetragen:
SELECT DISTINCTROW Artikel.ID, Artikel.ArtikelNr, Artikel.ArtikelBezeichnung 
FROM   Artikel WHERE (((Artikel.WarengruppeID)=CmbWarengruppe));
In der WHERE-Klausel wird die Verbindung zwischen dem Kombinationsfeld (man beachte den Namen) und dem Listenfeld hergestellt.

Hinweis:
Bei der Angabe des Kombinationsfeldnamen ist darauf zu achten, daß dieser NICHT in Anführungszeichen eingeschlossen ist (passiert, wenn man den Abfrageentwurf-Editor zur Erstellung der Datensatzherkunft des Listenfeldes verwendet).

Wird der Kombinationsfeldname mit Referenzierung aller Me.Kombinationsfeldname angegeben, erkennt MS Access dieses nicht und fragt beim Öffnen des Formulars nach einem Parameterwert.
Dies gilt für alle Formen der Referenzierung (Me.CmbName, [Me].[CmbName] und Me!CmbName).


Nun fehlt nicht der wichtigste aber der effektivste Schritt in der Verarbeitung. Dieser besteht darin, dafür zu sorgen, daß das Listenfeld seine Daten nach Auswahl einer Warengruppe neu einliest.
Dazu wird die Ereignisprozedur Bei Änderung des Kombinationsfeldes implementiert (VBA ! aber kein Bange).
Private Sub CmbWarengruppe_Change()
    '
    ' Das Listenfeld dazu veranlassen, die
    ' Daten neu einzulesen.
    '
    Me.LbArtikel.Requery
 
End Sub

Das war's schon - auch mit dem Beispiel !

Ein Kombinationsfeld um den Eintrag "Alle" erweitern

Eine weiteres Problem, welches einem fast um den Verstand bringen kann, ist die Erweiterung der Daten eines Kombinationsfeldes um den Eintrag "Alle", um mit dessen Auswahl zu signalisieren, daß keine Einschränkung auf einen der Werte des Kombinationsfeldes erfolgen soll (puh...sorry, aber manchmal geht es mit mir durch).

Folgendes Beispiel soll entstehen:

Erweiterung um ALLE
Beispiel: FrmErweiterungUmAlle


Im Kombinationsfeld Warengruppe kann ein Wert aus der Tabelle Warengruppen oder der Wert "Alle" ausgewählt werden. Wird eine Warengruppe selektiert, werden im Listenfeld Artikel zur Warengruppe alle Datensätze aus der Tabelle Artikel angezeigt, die zur gewählten Warengruppe gehören. Wird der Wert Alle ausgewählt, sollen im Listenfeld alle Datensätze der Tabelle Artikel angezeigt werden.

Als erstes benötigt man zur Lösung dieses Problems eine neue Tabelle (das ist mein Ernst).
Es ist vollkommen egal, wie diese Tabelle aussieht, Hauptsache ist daß dort immer nur EIN Datensatz enthalten ist. Ich nenne diese Tabelle immer SingleRow, definiere ein Feld vom Typ Text in dem - in dem einzigen Datensatz in der Tabelle - der Text In dieser Tabelle darf nur ein Datensatz enthalten sein steht (sicher ist sicher, man weiß ja nie).
Jemand mit SQL-Kenntnissen wird schon ahnen was ich mit dieser Tabelle vorhabe, den anderen erkläre ich es sofort.

Als nächstes benötigen wir eine UNION-Abfrage, welche wir als Datensatzherkunft des Kombinationsfeldes verwenden werden. Diese Abfrage sieht in der SQL-Ansicht so aus:
SELECT -1 As ID, "Alle" As Warengruppe
FROM   SingleRow
UNION 
SELECT Warengruppen.ID, Warengruppen.Warengruppe
FROM   Warengruppen;

Im ersten SELECT der UNION-Abfrage definieren wir einen eigenen Datensatz, indem wir zwei konstante Feldwerte (-1 und Alle) für genau eine Zeile (aus der Tabelle SingleRow) definieren.
Im zweiten SELECT der Abfrage fügen wir alle Werte aus der Tabelle Warengruppe hinzu.
Hinweis:
Würde in der Tabelle SingleRow mehr als EIN Datensatz vorhanden sein, würden in dieser UNION-Abfrage für jeden Datensatz einmal die Zeile -1, Alle in der Ergebnismenge auftauchen.
Ist die Tabelle SingleRow leer, taucht in der Ergebnismenge keine Zeile mit dem Wert Alle auf.



Ich gehe jetzt mal davon aus, daß jeder das vorhergehende Beispiel gelesen hat und beschränke mich hier auf die Unterschiede zum vorhergehende Beispiel.
Die Datensatzherkunft des Kombinationsfeldes sieht so aus:
		
SELECT DISTINCTROW AbfWarengruppeMitAlle.ID, AbfWarengruppeMitAlle.Warengruppe 
FROM   AbfWarengruppeMitAlle;
Dabei ist AbfWarengruppeMitAlle der Name der UNION-Abfrage.
Heureka, der Wert Alle ist im Kombinationsfeld zu sehen und auswählbar.
Dummer Weise weiß das Listenfeld noch nicht so richtig was damit anzufangen. Das ändern wir jetzt, indem wir ihm folgende Datensatzherkunft verpassen:
SELECT DISTINCTROW Artikel.ID, Artikel.ArtikelNr, Artikel.ArtikelBezeichnung 
FROM   Artikel WHERE Artikel.WarengruppeID=CmbWarengruppe OR CmbWarengruppe=-1;
Damit sagen wir ihm, daß es alle Artikel aus der Tabelle Artikel anzeigen soll, bei denen die WarengruppeID mit dem ausgewählten Wert im Kombinationsfeld übereinstimmt, es sei denn, im Kombinationsfeld wurde der Wert -1 ausgewählt (Alle).

Um ein wenig zu fachsimpeln:
Bei einer Oder-Verknüpfung (OR) wird der Gesamtausdruck (Artikel.WarengruppeID=CmbWarengruppe OR CmbWarengruppe=-1) wahr, wenn eine der beiden Seiten (Seite A: Artikel.WarengruppeID=CmbWarengruppe; Seite B:CmbWarengruppe=-1) wahr ist.
Für den Fall, daß im Kombinationsfeld eine vorhanden Warengruppe ausgewählt wird (ID <> -1) wird die Seite A für alle Artikel wahr, die dieser WarengruppeID entsprechen, genau diese werden dann im Listenfeld angezeigt. Das dabei die Seite B immer nicht wahr ist spielt keine Rolle, es reicht daß die Seite A wahr ist.
Der Umgekehrte Fall ist ein wenig schwerer zu verstehen.
Für den Fall, daß im Kombinationsfeld der Wert -1 ausgewählt wird, ist die Seite A immer nicht wahr (solange es keine Artikel mit der WarengruppeId -1 gibt). Dafür ist aber die Seite B immer wahr weil dort im Klartext steht ..OR -1 = -1. Das gilt aber nun für alle Datensätze aus der Tabelle Artikel, womit diese im Listenfeld angezeigt werden.

Fertig...noch ein Beispiel ?

Werte aus einem Listenfeld in ein anderes übertragen

Hierbei geht es darum mehrere Werte, die in einem Listenfeld zur Auswahlangeboten, in ein anderes Listenfeld und damit in eine andere Tabelle zu übertragen.

Folgendes Beispiel:

Werte aus Listenfeld übernehmen
Beispiel: FrmListboxNachListbox01


Mit diesem Dialog sollen einer Filiale aus der Menge aller verfügbaren Artikel eine Untermenge an Artikel zugeordnet werden.
Dazu sind folgende Tabellen definiert:
Tabelle: Filialen                     Tabelle: FilialeArtikel
ID | Filialnr  | Anschrift            FilialID | ArtikelID
---+-----------+-------------------   ---------+-----------
 1 | Nord 20   | Am Daich 12c             1    |     3
 2 | Nord 25   | Hinterm Daich 1          1    |     4
 3 | West 10   | An der A40 3             :          :
 4 | West 12   | Nevigeser Str. 17        4    |    12
Die Struktur der Tabelle Artikel kann im vorgehendem Beispiel nachgelesen werden.

Anhand der Tabellenstruktur kann man erkennen, daß für jedem zugeordneten Artikel ein Eintag in der Tabelle FilialeArtikel vorhanden sein muß, wobei die FilialID für die Filiale und die ArtikelID für den zugeordneten Artikel steht.
Die Datensatzherkunft des Listenfeldes Alle Artikel ist denkbar einfach:
SELECT DISTINCTROW [Artikel].[ID], [Artikel].[ArtikelNr], [Artikel].[ArtikelBezeichnung] 
FROM  [Artikel];
Die Datensatzherkunft des Listenfeldes zugeordnete Artikel ist nicht sehr viel komplizierter:
SELECT DISTINCTROW AbfFilialeArtikel.ID, AbfFilialeArtikel.ArtikelNr, 
       AbfFilialeArtikel.ArtikelBezeichnung 
FROM   AbfFilialeArtikel WHERE AbfFilialeArtikel.FilialID=CmbFiliale;
Wer nicht weiß, was die WHERE-Klausel bedeutet, sollte sich dieses Beispiel noch einmal ansehen.
Die Abfrage AbfFilialeArtikel verbindet die Tabelle FilialeArtikel mit der Tabelle Artikel um die Artikelnummer und -bezeichnung mit ausgeben zu können.

Die entscheidende Funktionalität steckt hinter den beiden Buttons.

Button 'Wert zuordnen'
Über diesen Button (Name: PbAdd) erfolgt die Zuordnung eines Artikels zur Filiale.
Die Ereignisprozedur Beim Klick des Button sieht so aus:
Private Sub PbAdd_Click()
 
Dim vVar As Variant
    '
    ' Prüfen, ob ein Arikel in der Liste "Alle Artikel" ausgewählt wurde
    '
    If IsNull(Me.LbAlleArtikel) Then
        MsgBox "Sie müssen erste einen Artikel aus der Liste 'Alle Artikel' auswählen", _
               vbInformation, Me.Caption
        Exit Sub
    End If
    '
    ' Prüfen, ob der Artikel bereits der Filiale zugeordnet ist
    '
    vVar = DLookup("[ArtikelID]", "FilialeArtikel", _
                   "[FilialID]=" & Str(Me.CmbFiliale) & " AND " & _
                   "[ArtikelID]=" & Str(Me.LbAlleArtikel))
    '
    If IsNull(vVar) Then
        '
        ' OK, Artikel ist noch nicht zugeordnet !
        ' Dann ein INSERT auf die Zuordnungstabelle machen
        '
        CurrentDb.Execute "INSERT INTO FilialeArtikel(FilialID, ArtikelID) VALUES(" & _
                          Str(Me.CmbFiliale) & "," & Str(Me.LbAlleArtikel) & ")"
        '
        ' Anzeige des Listenfeldes "zugeordnete Artikel" aktualisieren
        '
        Me.LbFilialeArtikel.Requery
    End If
    '
    ' Man könnte im Else-Zweig noch eine Fehlermeldung ausgeben,
    ' das tut aber nicht unbedingt nötig.
    '
End Sub

Die Zuordnung des Artikels passiert beim INSERT-Statement in die Tabelle FilialeArtikel

Button 'Wert zuordnen'
Über diesen Button (Name: PbDel) wird die Zuordnung eines Artikels zur Filiale entfernt.
Die Ereignisprozedur Beim Klick des Button sieht so aus:
Private Sub PbDel_Click()
    '
    ' Prüfen, ob ein Artikel in der Liste "zugeordnete Artikel" ausgewählt wurde
    '
    If IsNull(Me.LbFilialeArtikel) Then
        MsgBox "Sie müssen erste einen Artikel aus der Liste 'zugeordnete Artikel' auswählen", _
                vbInformation, Me.Caption
        Exit Sub
    End If
    '
    ' Artikel aus der Liste der zugeordneten Artikel entfernen
    '
    CurrentDb.Execute "DELETE FROM FilialeArtikel WHERE " & _
                      "[FilialID]=" & Str(Me.CmbFiliale) & " AND " & _
                      "[ArtikelID]=" & Str(Me.LbFilialeArtikel)
    '
    ' Anzeige des Listenfeldes "zugeordnete Artikel" aktualisieren
    '
    Me.LbFilialeArtikel.Requery
End Sub

Die Zuordnung wird durch das DELETE-Statement entfernt.
So, daß war es schon fast.
Als kleine Zugabe implementieren wir nun noch, die Zuordnung eines Artikels durch einen Doppelklick auf einen Artikel im Listenfeld Alle Artikel und die Entfernung der Zuordnung durch einen Doppelklick auf einen Artikel im Listenfeld zugeordnete Artikel.
Die Ereignisprozeduren dafür sehen so aus:
Private Sub LbAlleArtikel_DblClick(Cancel As Integer)
 
    PbAdd_Click
 
End Sub

Private Sub LbFilialeArtikel_DblClick(Cancel As Integer) PbDel_Click End Sub
So, eine letzte Zugabe.
Es könnte durchaus nützlich sein, wenn aus dem Listenfeld Alle Artikel jene Artikel verschwinden, die der Filiale bereits zugeordnet sind.
Dafür kann man allerdings nicht die zugeordneten Artikel aus der Tabelle Artikel löschen. D.h. man kann schon, wenn man nur einen Artikel hat.
Spaß beiseite. Für diesen Zweck brauchen wir eine andere Datensatzherkunft für das Listenfeld Alle Artikel
SELECT DISTINCTROW Artikel.ID, Artikel.ArtikelNr, Artikel.ArtikelBezeichnung 
FROM   Artikel  
WHERE  NOT EXISTS(SELECT *
                  FROM   FilialeArtikel
                  WHERE  FilialeArtikel.FilialID = CmbFiliale
                  AND    FilialeArtikel.ArtikelID = Artikel.ID);
Was sagt uns dieses Statement ?
1.) Es sollen Daten aus der Tabelle Artikel selektiert werden.
2.) Es sollen nur Artikel selektiert werden die nicht in der Tabelle FilialeArtikel enthalten sind und dort der Filiale zugeordnet sind, die im Kombinationsfeld CmbFiliale ausgewählt ist.

Nun muß nur noch überall dort (in den Ereignisprozeduren) wo die Anzeige des Listenfeldes zugeordnete Artikel aktualisiert wird auch die Anzeige des Listenfeldes Alle Artikel aktualisiert werden.

Mehrfachauswahl eines Listenfeldes verwenden

In Foren wird sehr oft nach der Verwendung der Mehrfachauswahl in Listenfeldern gefragt.
Dazu erst einmal etwas Grundsätzliches. Die Mehrfachauswahl dient dazu, aus einem Listenfeld mehrere Einträge auf einmal auszuwählen, um diese Auswahl anschließend irgendwie/irgendwo zuzuordnen.
Auf keinen Fall sollte die Mehrfachauswahl dazu verwendet werden um in einem Listenfeld die Zuordnung darzustellen.

Anhand des vorhergehenden Beispiels soll nun die Mehrfachauswahl erläutert werden.

Mehrfachauswahl übernehmen



Um die Arbeit bei der Zuordnung/Entfernung der Zuordnung von Artikel zu einer Filiale zu vereinfachen, ist in den Listenfeldern eine Mehrfachauswahl möglich.
Bei Betätigung der Buttons zuordnen (>) bzw. entfernen (<) werden also mehrer Datensätze angelegt bzw. gelöscht.

Hier die Ereignisprozedur Beim Klick des Button zuordnen:
Private Sub PbAdd_Click()
 
Dim vVar As Variant     ' Varinat für DLookup()
Dim LbElem As Variant   ' eine selektiertes Listenelement
Dim SelArtId As Long    ' die ID eines Artikels
Dim i As Long           ' Zählervariable
    '
    ' Prüfen, ob Artikel in der Liste "Alle Artikel" ausgewählt wurde.
    ' Bei Mehrfachauswahl geht das über die Anzahl der selektierten Einträge.
    '
    If Me.LbAlleArtikel.ItemsSelected.Count = 0 Then
        MsgBox "Sie müssen erste einen Artikel aus der Liste " & _
               "'Alle Artikel' auswählen", _
               vbInformation, Me.Caption
        Exit Sub
    End If
    '
    ' Schleife über alle selektieren Einträge des Listenfeldes
    '
    For Each LbElem In Me.LbAlleArtikel.ItemsSelected
        '
        ' ACHTUNG: In LbElem steht jetzt der Column-Index des selektieren
        '          Eintrags, dieser ist nicht mit dem Wert der gebundenen
        '          Spalte des selektierten Eintrags identisch.
        '
        ' Wert der gebundenen Spalte zum aktuellen Listenelement ermitteln.
        '
        SelArtId = Me.LbAlleArtikel.Column(0, LbElem)
        '
        ' Prüfen, ob der Artikel bereits der Filiale zugeordnet ist
        '
        vVar = DLookup("[ArtikelID]", "FilialeArtikel", _
                       "[FilialID]=" & Str(Me.CmbFiliale) & " AND " & _
                       "[ArtikelID]=" & Str(SelArtId))
        '
        If IsNull(vVar) Then
            '
            ' OK, Artikel ist noch nicht zugeordnet !
            ' Dann ein INSERT auf die Zuordnungstabelle machen
            '
            CurrentDb.Execute "INSERT INTO FilialeArtikel(FilialID, ArtikelID) " & _
                              "VALUES(" & Str(Me.CmbFiliale) & "," & _
                              Str(SelArtId) & ")"
        End If
        '
        ' Man könnte im Else-Zweig noch eine Fehlermeldung ausgeben,
        ' das tut aber nicht unbedingt nötig.
        '
    Next LbElem
    '
    ' Anzeige des Listenfeldes "zugeordnete Artikel" aktualisieren
    '
    Me.LbFilialeArtikel.Requery
    '
    ' Anzeige des Listenfeldes "Alle Artikel" aktualisieren
    '
    Me.LbAlleArtikel.Requery
    '
    ' Im Listenfeld "LbAlleArtikel" sind noch immer Datensätze markiert.
    ' Diese Markierung wird jetzt entfernt.
    '
    For i = 0 To Me.LbAlleArtikel.ListCount - 1
        Me.LbAlleArtikel.Selected(i) = False
    Next i
 
End Sub

Sobald bei einem Listenfeld die Mehrfachauswahl aktiviert ist, liefert es keinen Wert über die Value Eigenschaft bzw. immer den Wert Null. Dieser Umstand ist bei der Prüfung, ob ein Eintrag des Listenfeldes ausgewählt wurde zu berücksichtigen. Während die Prüfung ohne Mehrfachauswahl

If IsNull(Listenfeld) Then


lauten muß, ist bei Mehrfachauswahl die Anzahl der ausgewählten Einträge zu prüfen

If Listenfeld.ItemsSelected.Count = 0 Then

.
Um alle ausgewählten Einträge des Listenfeldes zu verarbeiten ist eine Schleife zu implementieren. In der MS Access Hilfe wird hier eine For Each Schleife empfohlen. Ich habe mich Anfangs an einer normalen For-Schleife versucht, bin aber (wie im Code zu erkennen) eines besseren belehrt worden.

Egal welche Schleife man nun implementiert, wichtig ist, daß die ItemsSelected-Auflistung den Listenindex und nicht den Wert der gebundenen Spalte des ausgewählten Eintrages zurückliefert.
In unserem Beispiel muß man sich die Liste des Listenfeldes Alle Artikel ungefähr so vorstellen:
ListIndex | Column(0) | Column(1)
----------+-----------+--------------
    0     | O-0100    | Apfel
    1     | O-0200    | Birne
    2     | G-1000    | Gurke
    :          :          :
   11     | Mopro-07  | Quark
Für die Verarbeitung heißt dies, wenn man sich die Auswahl im oben zu sehenden Bild ansieht, daß LbElem nacheinander die Werte 0, 1, 5, 6 und 7 annimmt. Diese Werte sind allerdings weit von der benötigten Artikelnummer entfernt.


Hinweis:
Die Werte der ItemsSelected-Auflistung eines Listenfeldes geben den Listenindex der ausgewählten Einträge an.


Für die weitere Verarbeitung muß daher die Artikelnummer (steht in Column(0) des Listenfeldes) aus der Column-Auflistung des Listenfeldes ermittelt werden. Im Code steht hierfür die Zeile

SelArtId = Me.LbAlleArtikel.Column(0, LbElem)


D.h. wir greifen auf die 1. Spalte der Zeile LbElem (aus ItemsSelected) zu, weil dort unsere Artikelnummer abgelegt ist.

Die Zuordnung der Werte passiert wie gehabt.

Mir ist eine weiter Eigenheit bei der Mehrfachauswahl aufgefallen, die evtl. nur unter Access 97 vorkommt.
Nach dem Requery auf das Listenfeld beleiben die Markierungen im Listenfeld erhalten. Da in unserem Beispiel im Listenfeld Alle Artikel nur jene Artikel zu sehen sind, die noch nicht zugeordnet sind, werden anschließend irgendwelche Artikel markiert.
Aus diesem Grund werden am Ende der Verarbeitung die Markierungen aus dem Listenfeld Alle Artikel entfernt, indem alle Werte in der Selected-Auflistung des Listenfeldes auf False eingestellt werden.

Der Vollständigkeit halber hier die Ereignisprozedur Beim Klick des Buttons entfernen:
Private Sub PbDel_Click()
 
Dim LbElem As Variant
Dim SelArtId As Long
    '
    ' Prüfen, ob ein Artikel in der Liste "zugeordnete Artikel" ausgewählt wurde.
    ' Bei Mehrfachauswahl geht das über die Anzahl der selektierten Einträge.
    '
    If Me.LbFilialeArtikel.ItemsSelected.Count = 0 Then
        MsgBox "Sie müssen erste einen Artikel aus der Liste " & _
               "'zugeordnete Artikel' auswählen", _
                vbInformation, Me.Caption
        Exit Sub
    End If
    '
    ' Schleife über alle selektieren Einträge des Listenfeldes
    '
    For Each LbElem In Me.LbFilialeArtikel.ItemsSelected
        '
        ' ACHTUNG: In LbElem steht jetzt der Column-Index des selektieren
        '          Eintrags, dieser ist nicht mit dem Wert der gebundenen
        '          Spalte des selektierten Eintrags identisch.
        '
        ' Wert der gebundenen Spalte zum aktuellen Listenelement ermitteln.
        '
        SelArtId = Me.LbFilialeArtikel.Column(0, LbElem)
        '
        ' Artikel aus der Liste der zugeordneten Artikel entfernen
        '
        CurrentDb.Execute "DELETE FROM FilialeArtikel WHERE " & _
                          "[FilialID]=" & Str(Me.CmbFiliale) & " AND " & _
                          "[ArtikelID]=" & Str(SelArtId)
    Next LbElem
    '
    ' Anzeige des Listenfeldes "zugeordnete Artikel" aktualisieren
    '
    Me.LbFilialeArtikel.Requery
    '
    ' Anzeige des Listenfeldes "zugeordnete Artikel" aktualisiern
    '
    Me.LbAlleArtikel.Requery
End Sub

Also, daß war es erst mal zu Kombinations- und Listenfeldern.
Sobald ich in den Foren auf ein neues Problem stoße werde ich ein entsprechendes Beispiel ergänzen.


WinZip Icon Beispiel als Zip-File (70 KB) zum downloaden.

Button Home/Top/Zurück