Home > Artikel > Ausgabe 12/2012 > ListView-Steuerelement: Drag and Drop, Teil I

ListView-Steuerelement: Drag and Drop, Teil I

Achtung: Sie sind nicht angemeldet. Wenn Sie Abonnent sind und sich anmelden, lesen Sie den kompletten Artikel, laden das PDF herunter oder probieren die Beispieldatenbank aus (sofern vorhanden).

ListView-Steuerelemente unterstützen im Gegensatz zum Listenfeld sogar den Einsatz von Drag and Drop. Damit können Sie nicht nur Einträge von einem ListView-Steuerelement zum nächsten ziehen, sondern auch die Reihenfolge der Einträge eines ListView-Steuerelements einstellen. Im ersten Teil dieser Mini-Artikelreihe zeigen wir, wie Sie Datensätze zwischen zwei ListView-Steuerelementen hin- und herziehen.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 1212_ListView.mdb.

Von ListView zu ListView per Drag and Drop

Einträge von einem ListView-Steuerelement zum nächsten Ziehen und fort fallenlassen – und umgekehrt? Wozu soll das gut sein? Nun: Beispielsweise können Sie so die Zuordnung von Elementen in einer m:n-Beziehung verwalten. Das Hauptformular zeigt dann die Datensätze der einen Seite der Beziehung an, die beiden ListView-Steuerelemente die diesem Datensatz zugeordneten und die nicht zugeordneten Datensätze der anderen Seite der Beziehung.

Die bisher in den Beispielen zum ListView-Steuerelement verwendeten Tabellen der Südsturm-Beispieldatenbank enthalten leider nur eine einzige m:n-Beziehung (zwischen den Bestellungen und den Artikeln). Diese ist leider nicht für dieses Beispiel geeignet, da ja hier noch einige weitere Daten wie Menge, Einzelpreis et cetera verwaltet werden müssen.

Also fingieren wir einfach ein geeignetes Beispiel. Die Tabelle tblArtikel enthält bislang ein Fremdschlüsselfeld, mit dem der Lieferant des Artikels eingestellt werden kann. Der Nachteil dieser Verknüpfung ist, dass Sie für jeden Artikel nur einen einzigen Lieferanten bestimmen können. In der Praxis gibt es jedoch meist noch weitere Lieferanten, die herangezogen werden, wenn der erste Lieferant nicht liefern kann oder ein anderer Lieferant einen günstigeren Preis anbietet.

Wir benötigen also eine Tabelle, mit der Sie einem Artikel einen oder mehrere Lieferanten zuordnen können. Diese sieht wie in Bild 1 aus und enthält neben dem Primärschlüsselfeld noch zwei weitere Felder namens ArtikelID und LieferantID. Für diese beden Felder legen Sie außerdem einen zusammengesetzten Schlüssel fest, dessen Eigenschaft Eindeutig den Wert Ja erhält. Damit stellen Sie sicher, dass jeder Lieferant jedem Artikel nur einmal zugeordnet werden kann.

Tabelle zur Herstellung einer m:n-Beziehung

Bild 1: Tabelle zur Herstellung einer m:n-Beziehung

Nun müssen Sie noch festlegen, dass die beiden Felder nur die Primärschlüsselwerte der Tabellen tblArtikel und tblLieferanten annehmen können. Dazu öffnen Sie das Beziehungen-Fenster von Access und fügen die drei Tabellen tblArtikel, tblLieferanten und tblArtikelLieferanten hinzu. Ziehen Sie das Feld ArtikelID der Tabelle tblArtikel auf das gleichnamige Feld der Tabelle tblArtikelLieferanten. Erledigen Sie das Gleiche für das Feld LieferantID.

Anschließend legen Sie die Beziehungseigenschaften fest (siehe Bild 2). Aktivieren Sie die Optionen Mit Referentieller Integrität und Löschweitergabe an verwandte Datensätze. Damit stellen Sie erstens sicher, dass die Fremdschlüsselfelder ArtikelID und LieferantID nur Werte aufnehmen können, die im Primärschlüsselfeld der jeweils verknüpften Tabelle enthalten sind. Außerdem werden Datensätze in der Tabelle tblArtikelLieferanten automatisch gelöscht, wenn ein verknüpfter Datensatz der Tabellen tblArtikel oder tblLieferanten entfernt wird.

Festlegen der Beziehungen zwischen den Tabellen der m:n-Verknüpfung

Bild 2: Festlegen der Beziehungen zwischen den Tabellen der m:n-Verknüpfung

Aber ist das überhaupt in Ordnung? Was geschieht, wenn ich alle Datensätze der Tabelle tblLieferanten lösche, die einem Artikel zugeordnet sind – diese besitzt dann ja keinen Lieferanten mehr? Kein Problem: Das Fremdschlüsselfeld LieferantID der Tabelle tblArtikel legt ja den standardmäßig zu verwendenden Lieferanten fest.

Die entsprechende Verknüpfung ist nicht mit Löschweitergabe ausgestattet. Der Versuch, einen Lieferanten zu löschen, der noch über das Fremdschlüsselfeld der Tabelle tblArtikel mit einem Artikel verknüpft ist, führt zu einem Fehler.

Erste Daten hinzufügen

Die Tabelle tblArtikelLieferanten soll alle möglichen Lieferanten eines Artikels anzeigen. In der Tabelle tblArtikel gibt es bereits ein Fremdschlüsselfeld LieferantID, welches den aktuellen Lieferanten festlegen. Natürlich soll dieser Lieferant auch in der Tabelle tblArtikelLieferanten auftauchen.

Wie erledigen wir diese Aufgabe? Alle entsprechenden Kombinationen aus ArtikelID und LieferantID manuell in die Tabelle übertragen? Natürlich nicht! Diese Aufgabe erledigen wir mit einem Einzeiler über den Direktbereich. Dieser sieht wie folgt aus:

CurrentDb.Execute "INSERT INTO tblArtikelLieferanten(ArtikelID, LieferantID) SELECT ArtikelID, LieferantID FROM tblArtikel", dbFailOnError

Sie lösen hiermit eine Aktionsabfrage aus, die für jeden Datensatz der Tabelle tblArtikel die Inhalte der Felder ArtikelID und LieferantID in die entsprechenden Felder der Tabelle tblArtikelLieferanten einträgt. Das Resultat sieht später wie in Bild 3 aus.

Die Tabelle tblArtikelLieferanten mit den Standardlieferanten eines jeden Artikels

Bild 3: Die Tabelle tblArtikelLieferanten mit den Standardlieferanten eines jeden Artikels

Formular aufbauen

Das Formular soll zunächst jeweils einen Datensatz der Tabelle tblArtikel anzeigen. Dazu erstellen Sie ein neues Formular in der Entwurfsansicht und stellen die Eigenschaft Datenherkunft auf die Tabelle tblArtikel ein. Anschließend ziehen Sie die beiden Felder ArtikelID und Artikelname sowie LieferantID in den Detailbereich des Formulars und speichern es unter dem Namen frmArtikelLieferanten (siehe Bild 4).

Das Formular zeigt einige Daten der Tabelle tblArtikel an.

Bild 4: Das Formular zeigt einige Daten der Tabelle tblArtikel an.

Danach legen Sie zwei ListView-Steuerelemente namens lvwLieferantenZugeordnet und lvwLieferantenNichtZugeordnet an (siehe Bild 5). Die Einstellung der Eigenschaften dieser beiden Steuerelemente nehmen wir wiederum per VBA beim Laden des Formulars vor.

Formular mit den beiden ListView-Steuerelementen

Bild 5: Formular mit den beiden ListView-Steuerelementen

Die dazu notwendige Prozedur wird durch das Ereignis Beim Laden ausgelöst. Hinterlegen Sie für die entsprechende Eigenschaft den Wert [Ereignisprozedur] und klicken Sie auf die Schaltfläche mit den drei Punkten neben der Eigenschaft, um die Ereignisprozedur anzulegen. Ergänzen Sie diese im nun erscheinenden VBA-Editor wie in Listing 1.

Dim WithEvents objLvwLieferantenZugeordnet As MSComctlLib.ListView

Dim WithEvents objLvwLieferantenNichtZugeordnet As MSComctlLib.ListView

Private Sub Form_Load()

     Set objLvwLieferantenZugeordnet = Me!lvwLieferantenZugeordnet.Object

     Set objLvwLieferantenNichtZugeordnet = Me!lvwLieferantenNichtZugeordnet.Object

     With objLvwLieferantenZugeordnet

         .View = lvwReport

         .Appearance = ccFlat

         .BorderStyle = ccNone

         .FlatScrollBar = True

         .ColumnHeaders.Clear

         .ColumnHeaders.Add , , "Lieferant zugeordnet", Me!lvwLieferantenZugeordnet.Width

     End With

     With objLvwLieferantenNichtZugeordnet

         .View = lvwReport

         .Appearance = ccFlat

         .BorderStyle = ccNone

         .FlatScrollBar = True

         .ColumnHeaders.Clear

         .ColumnHeaders.Add , , "Lieferant nicht zugeordnet", Me!lvwLieferantenNichtZugeordnet.Width

     End With

End Sub

Listing 1: Einrichten der beiden ListView-Steuerelemente

Im Kopf des Moduls Form_frmArtikelLieferanten deklarieren Sie zunächst zwei Objektvariablen für die beiden ListView-Steuerelemente mit dem Schlüsselwort WithEvents. Dies ist nötig, damit wir auf die Ereignisse dieser Nicht-Standard-Steuerelemente von Access zugreifen können.

Die folgende Prozedur weist diesen Variablen die entsprechenden Steuerelemente zu. Danach stellt sie einige Eigenschaften ein, die das Aussehen beeinflussen. Außerdem fügt sie jedem Steuerelement eine Überschrift hinzu und stellt die Breite so ein, dass die einzige Spalte die komplette Breite des Steuerelements ausfüllt.

Datenherkunft für die ListView-Steuerelemente

Anschließend benötigen wir zwei Abfragen, welche die Daten für die beiden ListView-Steuerelemente liefern. Die Abfrage für das ListView-Steuerelement lvwLieferantenZugeordnet ist die einfachere der beiden Abfragen. Sie enthält die beiden Tabellen tblArtikelLieferanten und tblLieferanten. Die Tabelle tblLieferanten steuert das Feld Firma bei, die Tabelle tblArtikelLieferanten die beiden Felder LieferantID und ArtikelID. Das Feld LieferantID dient später zum Füllen der Key-Eigenschaft der ListView-Elemente, das Feld Firma liefert den in der einzigen Spalte anzuzeigenden Wert und das Feld ArtikelID hilft dabei, die zum aktuell im Formular angezeigten Artikel gehörenden Datensätze dieser Abfrage zu filtern (siehe Bild 6).

Datenherkunft für das erste ListView-Steuerelement zur Anzeige der zugeordneten Lieferanten

Bild 6: Datenherkunft für das erste ListView-Steuerelement zur Anzeige der zugeordneten Lieferanten

Diese Abfrage liefert ungefiltert die Werte aus Bild 7. Diese müssen vor der Anzeige im ersten ListView-Steuerelement noch gefiltert werden.

Daten für das erste ListView-Steuerelement, allerdings noch nicht nach ArtikelID gefiltert

Bild 7: Daten für das erste ListView-Steuerelement, allerdings noch nicht nach ArtikelID gefiltert

Wie aber übermitteln wir die ArtikelID, deren Lieferanten angezeigt werden sollen? Dazu fügen Sie der Abfrage einen Parameter als Kriterium hinzu. Tragen Sie dazu den Ausdruck [prmArtikelID] in der Zeile Kriterium der Spalte ArtikelID ein. Wenn Sie die Abfrage nun aufrufen, fragt Access in einem kleinen Dialog nach dem Wert, der als Kriterium verwendet werden sollen. Dies müssen Sie natürlich später nicht von Hand erledigen, das Kriterium wird per VBA übermittelt. Damit das Formular im ersten List­View-Steuerelement die hier ermittelten Datensätze ausgibt, verwenden wir eine kleine Prozedur, die eine weitere Prozedur gleich zweimal hintereinander aufruft. Diese Prozedur soll durch das Ereignis Beim Anzeigen des Formulars ausgelöst werden, welches beim Anzeigen eines jeden Datensatzes feuert.

Dabei übergibt sie beim ersten Aufruf den Verweis auf das linke ListView-Steuerelement, beim zweiten das rechte. Der zweite Parameter ist der Name der Abfrage, die als Datenherkunft beim Füllen der ListView-Steuerelemente zum Einsatz kommen soll. Die erste kennen Sie bereits – es ist die Abfrage qryLieferanten:

Private Sub Form_Current()

     ListeAktualisieren objLvwZugeordnet, _

         "qryLieferanten"

     ListeAktualisieren objLvwNichtZugeordnet, _

         "qryNichtLieferanten"

End Sub

Die Prozedur ListeAktualisieren sieht wie in Listing 2 aus und landet ebenfalls im Klassenmodul Form_frmArtikelLieferanten. Diese Prozedur nimmt zunächst die beiden von der Prozedur Form_Current übergebenen Parameter entgegen. Dann füllt die Prozedur ein QueryDef-Objekt mit der per strAbfrage übergebenen gespeicherten Abfrage. Dies ist die einzige Möglichkeit, einen Parameter per Code mit einem Wert zu versehen. Deshalb referenziert die Prozedur in der folgenden Anweisung den für die Abfrage definierten Parameter namens prmArtikelID mit einer Objektvariablen entsprechenden Typs. Dieser offeriert eine Eigenschaft namens Value, der Sie den Wert des Parameters zuweisen können – in diesem Fall den aktuellen Wert des Feldes ArtikelID des aktuellen Datensatzes der Datenherkunft des Formulars.

Private Sub ListeAktualisieren(objListView As ListView, strAbfrage As String)

     Dim db As DAO.Database

     Dim qdf As DAO.QueryDef

     Dim prm As DAO.Parameter

     Dim rst As DAO.Recordset

     Set db = CurrentDb

     Set qdf = db.QueryDefs(strAbfrage)

     Set prm = qdf.Parameters("prmArtikelID")

     prm.Value = Me!ArtikelID

     Set rst = qdf.OpenRecordset

     objListView.ListItems.Clear

     Do While Not rst.EOF

         objListView.ListItems.Add , "a" & rst!LieferantID, rst!Firma

         rst.MoveNext

     Loop

     rst.Close

     Set rst = Nothing

     Set db = Nothing

End Sub

Listing 2: Prozedur zum Füllen eines ListView-Steuerelements auf Basis der angegebenen Abfrage

Wenn Sie danach wie in der Prozedur ein neues Recordset auf Basis des QueryDef-Objekts erzeugen, wird dieses bereits nach dem entsprechenden Parameterwert gefiltert.

Mehr zum Thema Parameterabfragen unter VBA erfahren Sie im Artikel DAO: QueryDefs und Parameterabfragen unter VBA.

Datenherkunft für das rechte ListView-Steuerelement

Die Datenherkunft des rechten ListView-Steuerelements ist etwas komplizierter. Diese soll alle Datensätze der Tabelle tblLieferanten liefern, die nicht bereits im linken ListView-Steuerelement angezeigt werden.

Dazu erstellen Sie eine neue Abfrage, die zunächst nur die Tabelle tblLieferanten enthält. Es kommt auch noch die bereits für das linke ListView-Steuerelement verwendete Abfrage qryLieferanten hinzu. Allerdings nicht als Element der Datenherkunft der Abfrage, sondern als Unterabfrage im Kriterium des Feldes LieferantID. Genauer definiert, soll die Abfrage nämlich alle Datensätze der Tabelle tblLieferanten liefern, deren Werte im Feld LieferantID noch nicht in den Datensätzen der Abfrage qryLieferanten vorkommen.

Dies ist nicht allzu kompliziert. Der Kriteriumsausdruck sieht schlicht und einfach so aus (siehe Bild 8):

Datenherkunft des zweiten ListView-Steuerelements

Bild 8: Datenherkunft des zweiten ListView-Steuerelements

Nicht In (SELECT LieferantID FROM qryLieferanten)

Die Unterabfrage SELECT LieferantID FROM qryLieferanten liefert dabei das Äquivalent einer Wertliste wie 1, 2, 5, 6. Die IN-Klausel vergleicht den Inhalt des aktuellen Feldes mit den gelieferten Werten.

Was aber geschieht mit dem Parameter [prmLieferantID], der in der Abfrage qryLieferanten definiert wurde? Dieser wird beim Öffnen der Abfrage qryNichtLieferanten genauso abgefragt wie beim Öffnen der Abfrage qryLieferanten – und zwar durch die Prozedur ListeAktualisieren.

Daten verschieben

Kommen wir nun zum eigentlichen Verschieben der Datensätze zwischen den beiden ListView-Steuerelemente. Das ListView-Steuerelement bietet zwei Eigenschaften an, die Sie für die programmgesteuerte Nutzung der Drag-and-Drop-Fähigkeiten entsprechend einstellen müssen. Diese fügen Sie noch wie folgt zur Prozedur Form_Load hinzu:

Sie haben das Ende des frei verfügbaren Teil dieses Artikels erreicht!

Wenn Sie mehr lesen und auf viele weitere Artikel zugreifen möchten, melden Sie sich als Abonnent unter Login an. Falls nicht, bestellen Sie doch einfach ein Jahresabonnement!