Zur Hauptseite ... Zum Onlinearchiv ... Zum Abonnement ... Zum Newsletter ... Zu den Tools ... Zum Impressum ... Zum Login ...

Gedrucktes Heft

Diesen Beitrag finden Sie in Ausgabe 5/2015.

Unser Angebot für Sie!

Lesen Sie diesen Beitrag und 500 andere sofort im Onlinearchiv, und erhalten Sie alle zwei Monate brandheißes Access-Know-how auf 72 gedruckten Seiten! Plus attraktive Präsente, zum Beispiel das bald erscheinende Buch 'Access 2010 - Das Grundlagenbuch für Entwickler'!

Diesen Beitrag twittern

Nach Daten im Unterformular suchen

Die Konstellation von Haupt- und Unterformular zur Darstellung von Daten aus 1:n- beziehungsweise m:n-Beziehungen ist bekannt. Einen Datensatz im Hauptformular zu suchen ist auch kein Hexenwerk. Aber wie sieht es aus, wenn wir das Hauptformular nach den Datensätzen filtern wollen, deren verknüpfte Tabelle einen Datensatz mit einem bestimmten Kriterium enthält? Und wenn wir dann noch einen Schritt weitergehen und noch den ersten passenden Datensatz im Unterformular markieren wollen? Wie dies gelingt, zeigt der vorliegende Beitrag.

Die Tabellen der Beispieldatenbank rekrutieren sich wieder eimal aus der Südsturm-Datenbank, unserer angepassten Nordwind-Variante. Im ersten Beispiel schauen wir uns die Kategorien im Hauptformular an und die Artikel einer jeden Kategorie im Unterformular.

Mit einem Suchfeld im Hauptformular wollen wir nach Artikeln filtern. Außerdem soll es zwei Schaltflächen geben, mit denen wir zwischen den Ergebnissen hin- und herblättern können. Das Formular soll dann wie in Bild 1 aussehen.

Formular mit Filter nach Artikelname, Variante I

Bild 1: Formular mit Filter nach Artikelname, Variante I

Aufbau des Formulars

Das Hauptformular verwendet die Tabelle tblKategorien als Datenherkunft und zeigt die beiden Felder KategorieID und Kategoriename an. Das Unterformular steuert die Daten der Tabelle tblArtikel bei, und zwar in der Daten­blatt­ansicht. Damit es jeweils nur die Datensätze anzeigt, die mit dem aktuellen Datensatz der Tabelle tblKategorien im Hauptformular verknüpft sind, erhalten die Eigenschaften Verknüpfen von und Verknüpfen nach des Unterformular-Steuerelements jeweils den Wert KategorieID.

Steuerelemente

Die zusätzlichen Steuerelemente im Formular heißen txtSuchbegriff, cmdVorheriger und cmdNaechster. Wenn der Benutzer einen Begriff in das Textfeld txtSuchbegriff eingibt, soll die erste Kombination aus Kategorie und Artikel gefunden werden, deren Artikelname dem im Suchfeld eingegebenen Ausdruck entspricht (Platzhalter wie das Sternchen (*) sind dabei erlaubt).

Die beiden Schaltflächen cmdVorheriger und cmdNaechster sind beim Laden des Formulars noch deaktiviert. Erst, wenn der Benutzer einen Suchbegriff eingibt, wird geprüft, ob eine der Schaltflächen aktiviert werden soll – oder auch beide. Zeigt das Formular den ersten Treffer an und gibt es noch einen weiteren, soll die Schaltfläche cmdNaechster aktiviert werden. Blättert der Benutzer damit zum folgenden Treffer, soll auch die Schaltfläche cmdVorheriger aktiviert werden. Die Schaltfläche cmdNaechster bleibt dabei verfügbar, bis der Benutzer zum letzten Ergebnis weitergeklickt hat.

Programmierung der Suche

Die Suchfunktion erfordert einen etwas anderen Ansatz als übliche Suchfunktionen. Direkt nach der Eingabe des Suchbegriffs erstellen wir ein Recordset, das die Tabelle tblArtikel aufnimmt – mit dem eingegebenen Suchbegriff als Vergleichswert für das Feld Artikelname. Die Tabelle liefert für jedes Suchergebnis sowohl die ArtikelID also auch die KategorieID aus dem entsprechenden Fremdschlüsselfeld. Damit können wir dann also sowohl den gesuchten Datensatz im Unterformular einstellen als auch die dazu passende Kategorie im Hauptformular.

Schaltflächen deaktivieren

Beim Laden des Formulars sollen die beiden Schaltflächen cmdVorheriger und cmdNaechster zunächst deaktiviert sein. Dazu legen wir für das Ereignis Beim Laden die folgende Ereignisprozedur an:

Private Sub Form_Load()
     Me!cmdVorheriger.Enabled = False
     Me!cmdNaechster.Enabled = False
End Sub

Nach der Eingabe eines Suchbegriffes und dem Auslösen des Ereignisses Nach Aktualisierung soll die Prozedur aus Listing 1 ausgelöst werden. Die Prozedur füllt die Variable db mit einem Verweis auf das Database-Objekt der aktuellen Datenbank. Dann liest sie den Suchbegriff aus dem Textfeld txtSuchbegriff in die Variable strSuchbegriff ein. Hat der Suchbegriff eine Länge von mehr als null Zeichen, erstellt die Prozedur ein neues Recordset, das alle Datensätze der Tabelle tblArtikel enthält, deren Feld Artikelname dem Suchbegriff entspricht. Dieses Recordset speichert die Prozedur in einer Variablen, die im Kopf des Klassenmoduls wie folgt deklariert wird und damit von allen Prozeduren des Moduls aus erreichbar ist:

Private Sub txtSuchbegriff_AfterUpdate()
     Dim db As DAO.Database
     Dim strSuchbegriff As String
     Set db = CurrentDb
     strSuchbegriff = Nz(Me!txtSuchbegriff, "")
     If Len(strSuchbegriff) > 0 Then
         Set rstErgebnis = db.OpenRecordset("SELECT ArtikelID, KategorieID FROM tblArtikel WHERE Artikelname LIKE '" _
             & strSuchbegriff & "' ORDER BY tblKategorien.KategorieID, tblArtikel.ArtikelID", dbOpenSnapshot)
         SteuerelementeAktualisieren
         Filtern
     Else
         FilterAufheben
     End If
End Sub

Listing 1: Prozedur für das Ereignis Nach Aktualisierung des Suchfeldes

Dim rstErgebnis As DAO.Recordset

Wir verwenden den Wert dbOpenSnapshot als Parameter, da dies direkt den Wert der Eigenschaft Recordcount des Recordsets verfügbar macht. Nachdem dies geschehen ist, ruft die Prozedur zwei weitere Routinen namens SteuerelementeAktualisieren und Filtern auf. Erstere aktiviert oder deaktiviert die beiden Schaltflächen cmdVorheriger und cmdNaechster in Abhängigkeit von der Datensatzposition, der zweite filtert die Daten in Haupt- und Unterformular nach dem aktuellen Datensatz des Recordsets rstErgebnis.

Sollte das Textfeld txtSuchbegriff keinen Wert enthalten, ruft die Prozedur die Routine FilterAufheben auf, was wieder alle Datensätze in Haupt- und Unterformular anzeigt.

Steuerelemente aktualisieren

Die Routine SteuerelementeAktualisieren kümmert sich um das Aktivieren und Deaktivieren der beiden Schaltflächen cmdVorheriger und cmdNaechster. Die erste If...Then-Bedingung dieser Routine prüft, ob die aktuelle Position des Datensatzzeigers von rstErgebnis kleiner als die Anzahl der Datensätze minus eins ist.

Minus eins deshalb, weil AbsolutePosition für den ersten Datensatz den Wert 0 liefert. In diesem Fall aktiviert die Routine die Schaltfläche cmdNaechster, anderenfalls wird sie deaktiert. Bei der Schaltfläche cmdVorheriger sieht es ähnlich aus: Die If...Then-Bedingung prüft, ob AbsolutePosition größer 0 ist. Falls ja, kann der Benutzer noch einen Datensatz nach vorn blättern und die Schaltfläche cmdVorheriger wird aktiviert:

Private Sub SteuerelementeAktualisieren()
     If rstErgebnis.AbsolutePosition _
             < rstErgebnis.RecordCount - 1 Then
         Me!cmdNaechster.Enabled = True
     Else
         Me!cmdNaechster.Enabled = False
     End If
     If rstErgebnis.AbsolutePosition > 0 Then
         Me!cmdVorheriger.Enabled = True
     Else
         Me!cmdVorheriger.Enabled = False
     End If
End Sub

Filtern der Artikel

Die Routine Filtern sorgt für die Anzeige des jeweils aktuellen Datensatzes des Recordsets rstErgebnis (s. Listing 2). Dabei prüft die Routine zunächst, ob das Recordset mindestens einen Datensatz enthält. Falls ja, stellt sie die Eigenschaft Filter des Hauptformulars auf einen Ausdruck ein, bei dem der Wert von KategorieID der Datenherkunft dem Wert dieses Feldes im aktuelle Datensatz des Recordsets entspricht und aktiviert den Filter durch Setzen von FilterOn auf True.

Private Sub Filtern()
     If Not rstErgebnis.RecordCount = 0 Then
         With Me
             .Filter = "KategorieID = " & rstErgebnis!KategorieID
             .FilterOn = True
         End With
         With Me!sfmArtikelNachKategorie.Form
             .Filter = "ArtikelID = " & rstErgebnis!ArtikelID
             .FilterOn = True
         End With
     Else
         With Me
             .Filter = "1=2"
             .FilterOn = True
         End With
     End If
End Sub

Listing 2: Prozedur zum Filtern der Daten in Haupt- und Unterformular

Auf die gleiche Weise filtert es das Unterformular so, dass nur der Artikel aus dem aktuellen Datensatz des Recordsets erscheint. Liefert rstErgebnis keinen Datensatz, stellt die Prozedur den Filter für das Hauptformular auf den Ausdruck 1=2 ein, was keine Datensätze liefert. Dementsprechend bleibt auch das Unterformular leer.

Aufheben des Filters

Leert der Benutzer das Textfeld strSuchbegriff und löst das Ereignis Nach Aktualisierung des Textfeldes aus, ruft die Prozedur txtSuchbegriff_AfterUpdate wie oben erwähnt die Prozedur FilterAufheben auf.

Diese leert die Eigenschaft Filter sowohl des Unterformulars als auch des Hauptformulars. Dabei ist die Reihenfolge wichtig – Sie müssen erst den Filter im Unterformular aufheben und dann den im Hauptformular, anderenfalls zeigt das Formular die Datensätze im Unterformular nicht korrekt an:

Private Sub FilterAufheben()
     Me!sfmArtikelNachKategorie.Form.Filter = ""
     Me.Filter = ""
     Set rstErgebnis = Nothing
     Me!cmdVorheriger.Enabled = False
     Me!cmdNaechster.Enabled = False
End Sub

Außerdem leert die Prozedur das Recordset rstErgebnis und deaktiviert die Schaltflächen cmdVorheriger und cmdNaechster.

Funktion der Schaltflächen

Die Schaltfläche cmdNaechster soll beim Anklicken das nächste Suchergebnis liefern, also den folgenden Datensatz der Recordsets rstErgebnis. Dazu bewegt die Prozedur den Datensatzzeiger mit der Methode MoveNext zum folgenden Datensatz und ruft dann die beiden Routinen SteuerelementeAktualisieren und Filtern auf, um sowohl die Aktivierung der Steuerelemente zu prüfen als auch Haupt- und Unterformular nach dem aktuellen Suchergebnis zu filtern:

Private Sub cmdNaechster_Click()
     rstErgebnis.MoveNext
     SteuerelementeAktualisieren
     Filtern
End Sub

Die Prozedur, die durch einen Klick auf die Schaltfläche cmdVorheriger ausgelöst wird, erledigt die gleiche Aufgabe, springt aber zum vorherigen Datensatz des Recordsets mit den Suchergebnissen:

Private Sub cmdVorheriger_Click()
     rstErgebnis.MovePrevious
     SteuerelementeAktualisieren
     Filtern
End Sub

Die Prozeduren sorgen in dieser Form dafür, dass Haupt- und Unterformular jeweils nur einen Datensatz anzeigen. Das mag für bestimmte Anwendungszwecke passen, aber nicht für alle – also stellen wir eine Alternative vor.

Variante II: Suchergebnis aktivieren statt filtern

Die zweite Variante soll das Haupt- und Unterformular nicht nach dem aktuell gefundenen Datensatz filtern, sondern weiterhin alle verfügbaren Datensätze anzeigen. Allerdings soll der jeweilige Datensatz im Unterformular markiert werden.

Formular und Unterformular sind gleich aufgebaut, allerdings heißt das Formular nun frmArtikelNachKategorien_Markieren und das Unterformular sfmArtikelNachKategorien_Markieren (s. Bild 2).

Formular mit Filter nach Artikelname, Variante II

Bild 2: Formular mit Filter nach Artikelname, Variante II

Nach der Eingabe des Suchbegriffs sollen Haupt- und Unterformular gar nicht gefiltert werden, sondern nur die jeweiligen Datensätze markiert werden. Auch hier verwenden wir ein Recordset zum Ermitteln der Suchergebenisse:

Dim rstErgebnis As DAO.Recordset

Beim Laden des Formulars deaktivieren wir wieder die beiden Schaltflächen:

Private Sub Form_Load()
     Me!cmdVorheriger.Enabled = False
     Me!cmdNaechster.Enabled = False
End Sub

Nach der Eingabe des Suchbegriffs wird die Prozedur aus Listing 3 ausgelöst. Diese sieht so ähnlich aus wie die aus dem vorherigen Beispiel, aber sie ruft die Routine Markieren statt Filtern auf. Und bei Aktualisierung bei leerem Textfeld erfolgt der Aufruf der Routine MarkierungAufheben, nicht mehr von FilterAufheben.

Private Sub txtSuchbegriff_AfterUpdate()
     Dim db As DAO.Database
     Dim strSuchbegriff As String
     Set db = CurrentDb
     strSuchbegriff = Nz(Me!txtSuchbegriff, "")
     If Len(strSuchbegriff) > 0 Then
         Set rstErgebnis = db.OpenRecordset("SELECT ArtikelID, KategorieID FROM tblArtikel WHERE Artikelname LIKE '" _
             & strSuchbegriff & "' ORDER BY KategorieID, ArtikelID", dbOpenSnapshot)
         SteuerelementeAktualisieren
         Markieren
     Else
         MarkierungAufheben
     End If
End Sub

Listing 3: Aktionen nach dem Eingeben des Suchbegriffs

Fund markieren

Die Prozedur Markieren prüft zunächst, ob das Recordset mit der Ergebnisliste überhaupt einen Eintrag enthält. Falls ja, ruft sie die FindFirst-Methode des Recordset-Objekts des Hauptformulars auf, um die richtige Kategorie auszuwählen (s. Listing 4).

Private Sub Markieren()
     If Not rstErgebnis.RecordCount = 0 Then
         Me.Recordset.FindFirst "KategorieID = " & rstErgebnis!KategorieID
         Me!sfmArtikelNachKategorie_Markieren.Form.Recordset.FindFirst "ArtikelID = " & rstErgebnis!ArtikelID
     Else
         MsgBox "Keine passenden Daten gefunden"
         MarkierungAufheben
     End If
End Sub

Listing 4: Diese Prozedur markiert den nächsten Eintrag des Ergebnis-Recordsets.

Dabei übergibt sie ein Kriterium, das den Wert des Feldes KategorieID des Recordsets enthält. Das Hauptformular zeigt so schon einmal den richtigen Datensatz an.

Nun soll das Unterformular noch den aktuellen Datensatz der Ergebnisliste aus rstErgebnis liefern. Deshalb verwenden wir auch hier die FindFirst-Methode des entsprechenden Recordset-Objekts – nur diesmal mit dem Kriterium "ArtikelID = " & rstErgebnis!ArtikelID. Dadurch landet der Datensatzzeiger im Unterformular genau bei dem gesuchten Datensatz.

Markierung aufheben

Sollen die Markierungen wieder entfernt werden, was beispielsweise nach dem Leeren und Aktualisieren des Textfeldes txtSuchbegriff geschehen soll, wird die folgende Prozedur aufgerufen:

Private Sub MarkierungAufheben()
     Me.Recordset.MoveFirst
     Me!sfmArtikelNachKategorie_Markieren. Form.Recordset.MoveFirst
     Set rstErgebnis = Nothing
     Me!cmdVorheriger.Enabled = False
     Me!cmdNaechster.Enabled = False
End Sub

Diese Prozedur springt wieder zum ersten Datensatz des Hauptformulars und dann zum ersten Datensatz des Unterformulars. Das Recordset mit der Ergebnisliste wird geleert und die Schaltflächen zum Vor- und Zurückblättern werden deaktiviert.

In Ergebnissen blättern

Das Verhalten beim Blättern in den gefundenen Datensätzen sieht natürlich ganz anders aus: Wenn etwa eine Kategorie gleich mehrere Treffer liefert, bleibt die Kategorie im Hauptformular erhalten und es werden beim Anklicken der Schaltfläche cmdNaechster nacheinander die einzelnen Funde im Unterformular markiert:

Private Sub cmdNaechster_Click()
     rstErgebnis.MoveNext
     SteuerelementeAktualisieren
     Markieren
End Sub

Genauso läuft es natürlich auch andersherum:

Private Sub cmdVorheriger_Click()
     rstErgebnis.MovePrevious
     SteuerelementeAktualisieren
     Markieren
End Sub

Die Prozedur zum Aktivieren oder Deaktivieren der beiden Schaltflächen cmdNaechster und cmdVorheriger entspricht genau dem vorherigen Beispiel, daher führen wir diese hier nicht nochmals auf.

Nach Daten aus m:n-Beziehungen suchen

Im letzten Beispiel schauen wir uns eine Konstellation aus Haupt- und Unterformular an, bei der Sie ein Feld durchsuchen, das über die im Unterformular angezeigte m:n-Beziehung mit dem Datensatz im Hauptformular verknüpft ist. Das Beispielformular finden Sie in Bild 3.

Suche nach Daten aus einer m:n-Beziehung

Bild 3: Suche nach Daten aus einer m:n-Beziehung

Die Prozeduren übernehmen wir weitgehend aus den vorherigen Beispielen. Einen wichtigen Unterschied finden Sie jedoch in der Prozedur, die nach der Eingabe eines Suchbegriffs ausgelöst wird (s. Listing 5). Diese stellt eine etwas andere SQL-Abfrage zusammen. Diese berücksichtigt die beiden verknüpften Tabellen tblBestelldetails und tblArtikel, weil wir ja die Daten des Formulars nach dem Namen des Artikels durchsuchen wollen.

Private Sub txtSuchbegriff_AfterUpdate()
     Dim db As DAO.Database
     Dim strSuchbegriff As String
     Set db = CurrentDb
     strSuchbegriff = Nz(Me!txtSuchbegriff, "")
     If Len(strSuchbegriff) > 0 Then
         Set rstErgebnis = db.OpenRecordset("SELECT tblBestelldetails.BestellungID, "_
             & "tblBestelldetails.BestelldetailID,  tblBestelldetails.ArtikelID " _
             & "FROM tblBestelldetails INNER JOIN tblArtikel ON tblBestelldetails.ArtikelID  " _
             & "= tblArtikel.ArtikelID WHERE tblArtikel.Artikelname LIKE '" _
             & strSuchbegriff & "' ORDER BY tblBestelldetails.BestelldetailID", dbOpenSnapshot)
         SteuerelementeAktualisieren
         Markieren
     Else
         MarkierungAufheben
     End If
End Sub

Listing 5: Zusammenstellen der Ergebnisabfrage nach dem Eingeben eines Suchbegriffs

Der zweite Unterschied findet sich in der Prozedur, welche die gefundenen Daten in Haupt- und Unterformular markiert. Hier haben wir noch zwei Zeilen hinzugefügt, die dafür sorgen, dass der Datensatz im Unterformular auch noch komplett hervorgehoben wird. Dafür verschieben wir zunächst den Fokus auf das Unterformular und rufen dann die Methode RunCommand mit dem Parameter acCmdSelectRecord auf, um die komplette Spalte zu markieren:

Private Sub Markieren()
     If Not rstErgebnis.RecordCount = 0 Then
         Me.Recordset.FindFirst "BestellungID = " _
             & rstErgebnis!BestellungID
         Me!sfmArtikelNachBestellung.Form.Recordset. FindFirst "ArtikelID = " & rstErgebnis!ArtikelID
         Me!sfmArtikelNachBestellung.SetFocus
         RunCommand acCmdSelectRecord
     Else
         MsgBox "Keine passenden Daten gefunden"
         MarkierungAufheben
     End If
End Sub

Zusammenfassung und Ausblick

Dieser Beitrag hat drei Varianten für die Suche nach Daten der Datenherkunft des Unterformulars in Haupt-/Unterformularkonstellationen geliefert.

Die erste zeigt jeweils nur die Kombination aus Master-/Detaildatensatz an, die zum aktuellen Suchergebnis passt.

Die zweite schränkt die Datenherkunft von Haupt- und Unterformular nicht ein, sondern springt zu den gefundenen Datensätzen.

Die dritte liefert ein etwas komplexeres Beispiel, da die Daten des Unterformulars aus einer m:n-Beziehung stammen.

Kompletten Artikel lesen?

Einfach für den Newsletter anmelden, dann lesen Sie schon in einer Minute den kompletten Artikel und erhalten die Beispieldatenbanken.

E-Mail:

Download

Download

Die .zip-Datei enthält folgende Dateien:

Nach1nDatenSuchen.mdb

Beispieldateien downloaden

© 2003-2015 André Minhorst Alle Rechte vorbehalten.