Rechnungsverwaltung: Kundenübersicht mit Suche

Wenn der Kunde anruft, möchten Sie schnell den entsprechenden Kundendatensatz auf dem Bildschirm haben. Dazu stellen wir im vorliegenden Beitrag ein Formular samt Unterformular zusammen, mit denen die gewünschten Daten schnell ermittelt werden können. Im Hauptformular bieten wir einige Suchfunktionen an, im Unterformular liefern wir die den Suchkriterien entsprechenden Daten in der Datenblattansicht. Außerdem soll das Formular die Möglichkeit bieten, den gefundenen Kundendatensatz im Detailformular zu öffnen, damit wir auch noch die Bestellungen des Kunden einsehen können.

Unterformular für die Kundenliste

Bevor wir beginnen, das Hauptformular zu programmieren, legen wir zunächst das Unterformular an. Dann können wir dieses gleich im Anschluss direkt zum Hauptformular hinzufügen.

Das neue Unterformular soll sfmKundenuebersicht heißen und standardmäßig die Daten der Tabelle tblKunden anzeigen. Deshalb stellen wir seine Eigenschaft Datensatzquelle auf diese Tabelle ein. Anschließend können wir alle Felder der Feldliste in den Detailbereich des Formulars ziehen. Gegebenenfalls lassen wir das Primärschlüsselfeld ID weg, da dieses nur zum Herstellen von Beziehungen dient und keine geschäftliche Funktion hat.

Damit das Formular die Daten in der Datenblattansicht anzeigt, legen wir für die Eigenschaft Standardansicht noch den Wert Datenblatt fest (siehe Bild 1). Damit können wir die Arbeiten am Unterformular bereits beenden und dieses schließen.

Entwurf des Unterformulars sfmKundenuebersicht

Bild 1: Entwurf des Unterformulars sfmKundenuebersicht

Hauptformular erstellen

Anschließend erstellen wir das Hauptformular und speichern dieses direkt unter dem Namen frmKundenuebersicht. Diesem weisen wir keine Datensatzquelle zu, weil es selbst keine Daten anzeigen soll – diese liefert allein das Unterformular. Im Hauptformular wollen wir nur Steuerelemente zum Durchsuchen der Kundendaten und zum Öffnen von Detaildatensätzen bereitstellen. Daher benötigen wir im Hauptformular auch nicht die Steuerelemente zum Navigieren in Datensätzen und stellen daher einige Eigenschaften ein. Die Eigenschaften Navigationsschaltflächen, Datensatzmarkierer, Bildlaufleisten und Trennlinien setzen wir auf den Wert Nein, die Eigenschaft Automatisch zentrieren auf Ja.

Dann ziehen wir aus dem Navigationsbereich das Unterformular sfmKundenuebersicht in den Detailbereich des Formularentwurfs von frmKundenuebersicht. Das Bezeichnungsfeld des Unterformular-Steuerelements entfernen wir, da wir ja wissen, dass dieses Kundendaten anzeigt.

Damit das Unterformular beim Vergrößern des Hauptformulars ebenfalls vergrößert wird, stellen wir seine Eigenschaften Horizontaler Anker und Vertikaler Anker jeweils auf Beide ein.

Wenn wir oben und unten ein wenig Platz für die Steuerelemente zum Suchen und zum Aufrufen der Anzeige der Kundendetails lassen, sieht der Entwurf nun wie in Bild 2 aus.

Haupt- und Unterformular im Entwurf

Bild 2: Haupt- und Unterformular im Entwurf

Steuerelemente zum Filtern der Kunden hinzufügen

Damit kommen wir zu den Steuerelementen, mit denen wir die Filterkriterien für die anzuzeigenden Kunden eingeben wollen. Diese platzieren wir wie in Bild 3 im Bereich über dem Unterformular. Die Steuerelemente heißen:

Hinzufügen der Steuerelemente zum Filtern der Kundendatensätze

Bild 3: Hinzufügen der Steuerelemente zum Filtern der Kundendatensätze

  • txtFilterKundennummer
  • txtFilterFirma
  • txtFilterVorname
  • txtFilterNachname
  • txtFilterStrasse
  • txtFilterPLZ
  • txtFilterOrt
  • txtFilterEMail

Das Filtern wollen wir einfach und schnell realisieren: Jede Eingabe in eines der Filterfelder soll unmittelbar die gefilterten Daten anzeigen. Außerdem soll immer im kompletten zu filternden Feld nach dem im jeweiligen Filterfeld eingegebenen Text gesucht werden. Sprich: Wenn der Benutzer im Feld txtFilterKundennummer den Wert 1 eingibt, sollen alle Datensätze angezeigt werden, die an beliebiger Stelle im Feld Kundennummer den Wert 1 enthalten.

Das heißt auch, dass wir für jedes der Steuerelemente zur Eingabe der Filterkriterien das Ereignis Bei Änderung implementieren müssen. Mit diesem rufen wir dann jeweils eine weitere Prozedur auf, welche die jeweiligen Werte der Filter-Textfelder ausliest und ein entsprechendes SQL-Kriterium zusammenstellt.

Unterschiedliche Eigenschaften zum Ermitteln der Vergleichskriterien

Das Problem dabei ist, dass wir für die Inhalte der Filter-Textfelder auf unterschiedliche Eigenschaften zugreifen müssen. Normalerweise greifen wir einfach auf die Value-Eigenschaft zu. Diese können wir allerdings nicht nutzen, wenn wir während der Eingabe auf den Inhalt im aktuellen Textfeld zugreifen wollen. Der Grund ist, dass die Value-Eigenschaft erst mit dem aktuell im Textfeld enthaltenen Wert gefüllt wird, wenn der Benutzer die Eingabe bestätigt – beispielsweise, indem er das Textfeld verlässt und den Fokus auf das nächste Textfeld verschiebt. In diesem Fall lässt sich der aktuell im Textfeld dargestellte Text nur mit der Eigenschaft Text auslesen.

Wir müssen also zumindest für das aktuelle Textfeld auf die Text-Eigenschaft zugreifen und können nicht die Value-Eigenschaft nutzen. Aber warum ist das überhaupt ein Problem? Können wir nicht einfach auf den Inhalt aller Felder über die Text-Eigenschaft zugreifen? Das wiederum gelingt nicht, weil man nur für das Textfeld auf die Text-Eigenschaft zugreifen kann, das aktuell den Fokus hat.

Wir müssen also den Inhalt des aktuell bearbeiteten Textfeldes mit der Text– und den der übrigen Textfelder mit der Value-Eigenschaft abfragen und prüfen, ob diese Kriteriumsausdrücke enthalten. Wobei die Value-Eigenschaft wiederum die Standardeigenschaft der TextBox-Klasse ist, weshalb wir diese gar nicht angeben müssen. Statt Me!txtFilterKundennummer.Value können wir einfach Me!txtFilterKundennummer schreiben.

Prozedur zum Ermitteln des Filterkriteriums für das Unterformular

Wir legen also zunächst für jedes der Filter-Textfelder eine Ereignisprozedur für das Ereignis Bei Änderung an. Diese enthält für alle Filter-Textfelder den Aufruf einer weiteren Prozedur namens KundenFiltern – wie hier am Beispiel der Prozedur txtFilterEMail zu sehen:

Private Sub txtFilterEMail_Change()
     KundenFiltern
End Sub

Einfache Variante zum Zusammensetzen des Filterkriterums

Die Prozedur zum Zusammenstellen eines Filterausdrucks für das Unterformular können wir geradlinig und einfach aufbauen oder auch auf den ersten Blick etwas komplizierter, dafür aber flexibler. Wir schauen uns beide Varianten an. Die erste finden Sie in Listing 1.

Private Sub KundenFiltern()
     Dim strFilter As String
     If Me!txtFilterKundennummer Is Me.ActiveControl Then
         If Not Len(Me!txtFilterKundennummer.Text) = 0 Then
             strFilter = strFilter & " AND Kundennummer LIKE ''*" & Me!txtFilterKundennummer.Text & "*''"
         End If
     Else
         If Not Len(Me!txtFilterKundennummer.Value) = 0 Then
             strFilter = strFilter & " AND Kundennummer LIKE ''*" & Me!txtFilterKundennummer.Value & "*''"
         End If
     End If
     If Me!txtFilterFirma Is Me.ActiveControl Then
         If Not Len(Me!txtFilterFirma.Text) = 0 Then
             strFilter = strFilter & " AND Firma LIKE ''*" & Me!txtFilterFirma.Text & "*''"
         End If
     Else
         If Not Len(Me!txtFilterFirma.Value) = 0 Then
             strFilter = strFilter & " AND txtFilterFirma LIKE ''*" & Me!txtFilterFirma.Value & "*''"
         End If
     End If
     ''...
     If Not Len(strFilter) = 0 Then
         strFilter = Mid(strFilter, 5)
         With Me!sfmKundenuebersicht.Form
             .Filter = strFilter
             .FilterOn = True
         End With
     Else
         Me!sfmKundenuebersicht.Form.Filter = ""
     End If
End Sub

Listing 1: Prozedur zum Einstellen des Filters für das Unterformular, einfache Version

Diese Version verwendet eine Variable namens strFilter, um den Filterausdruck darin zusammenzustellen. Sie prüft für jedes der Filter-Textfelder, ob es sich dabei um das aktuelle Steuerelement handelt, also das Steuerelement, das aktuell den Fokus enthält. Das dient der Unterscheidung, ob wir die Text– oder die Value-Eigenschaft zum Ermitteln des jeweils in dem Steuerelement enthaltenen Textes verwenden müssen.

Um herauszufinden, ob wir es bei dem aktuellen Steuerelement mit dem aktiven Steuerelement zu tun haben, vergleichen wir dieses mit dem Verweis auf das Steuerelement, das wir mit der Eigenschaft ActiveControl des Formulars ermitteln können. Sind beide gleich, handelt es sich um das aktive Steuerelement, sonst nicht. Ist das Steuerelement das aktive Steuerelement und ist dieses nicht leer, lesen wir den Inhalt mit der Text-Eigenschaft und stellen damit einen Ausdruck wie den folgenden zusammen:

  AND Kundennummer LIKE ''*Vergleichsausdruck*''

Vergleichsausdruck entspricht dabei dem Inhalt des jeweiligen Textfeldes. Falls es sich bei dem aktuell untersuchten Steuerelement nicht um das aktive Steuerelement handelt, verwenden wir für den gleichen Ausdruck den Inhalt der Value-Eigenschaft des Textfeldes.

Das Problem ist, dass wir in dieser Version für jedes Textfeld die folgenden Zeilen, hier beispielhaft am Textfeld txtFilterKundennummer gezeigt, in der Prozedur anlegen müssen:

If Me!txtFilterKundennummer Is Me.ActiveControl Then
     If Not Len(Me!txtFilterKundennummer.Text) = 0 Then
         strFilter = strFilter & " AND Kundennummer  LIKE ''*" & Me!txtFilterKundennummer.Text & "*''"
     Else
         strFilter = strFilter & " AND Kundennummer  LIKE ''*" & Me!txtFilterKundennummer.Value & "*''"
     End If
End If

In der Beispielprozedur haben wir aus Platzgründen nur zwei dieser Konstrukte abgebildet. strFilter wird nur gefüllt, wenn überhaupt irgendeins der Filter-Textfelder einen Wert enthält. Dies prüft die folgende If…Then-Bedingung. Entdeckt diese in strFilter eine Zeichenfolge mit einer Länge größer als 0, dann trennt sie das führende AND ab und stellt den Filter für die Eigenschaft Filter des Formulars im Unterformular-Steuerelement ein.

Außerdem aktiviert sie diesen, indem sie die Eigenschaft FilterOn auf den Wert True setzt. Enthält strFilter eine leere Zeichenkette, stellt die Prozedur auch die Filter-Eigenschaft auf eine leere Zeichenkette ein. Die Eigenschaft FilterOn muss dann nicht auf False gesetzt werden.

Alternative Programmierung des Filters

Es gibt eine Alternative zur vorherigen Variante der Einstellung des Filters im Unterformular. In dieser ersetzen wir die Untersuchung des Inhalts der Textfelder in jeweils einem eigenen verschachtelten If…Then-Konstrukt durch eine Schleife, mit der wir die Filter-Textfelder durchlaufen.

Das ist möglich, weil wir die Benennung dieser Textfelder geschickt durchgeführt haben. All diese Textfelder beginnen nämlich mit der Zeichenkette txtFilter und enden mit dem Namen des Feldes, für welches das Textfeld den Vergleichswert liefern soll. Auf diese Weise können wir die Anweisungen, die wir oben in acht einzelnen verschachtelten If…Then-Konstrukten erledigt haben, innerhalb einer Schleife über alle Steuerelemente abhandeln.

Dazu deklarieren wir in der Prozedur aus Listing 2 zwei Objektvariablen. Die erste namens ctl dient zum Durchlaufen aller Steuerelemente des Hauptformulars in einer For Each-Schleife. Die zweite heißt txt und hat den Datentyp TextBox. Mit ihr referenzieren wir alle Textfelder unter den durchlaufenen Steuerelementen.

Private Sub KundenFiltern()
     Dim ctl As Control
     Dim txt As TextBox
     Dim strFilter As String
     For Each ctl In Me.Controls
         If Left(ctl.Name, 9) = "txtFilter" Then
             Set txt = ctl
             If txt Is Me.ActiveControl Then
                 If Not Len(txt.Text) = 0 Then
                     strFilter = strFilter & " AND " & Mid(txt.Name, 10) & " LIKE ''*" & txt.Text & "*''"
                 End If
             Else
                 If Not Len(Nz(txt.Value, "")) = 0 Then
                     strFilter = strFilter & " AND " & Mid(txt.Name, 10) & " LIKE ''*" & txt.Value & "*''"
                 End If
             End If
         End If
     Next ctl
     If Not Len(strFilter) = 0 Then
         strFilter = Mid(strFilter, 5)
         With Me!sfmKundenuebersicht.Form
             .Filter = strFilter
             .FilterOn = True
         End With
     Else
         Me!sfmKundenuebersicht.Form.Filter = ""
     End If
End Sub

Listing 2: Prozedur zum Einstellen des Filters für das Unterformular per Schleife

Die For Each-Schleife verwendet die Auflistung Me.Controls, um alle Steuerelemente zu durchlaufen, und die Variable ctl zum Referenzieren des jeweiligen Steuerelements. Die erste If…Then-Bedingung innerhalb der Schleife prüft, ob die ersten neun Zeichen des Steuerelementnamens txtFilter lauten.

Ist das der Fall, referenzieren wir das Steuerelement mit der Variablen txt. Das machen wir eigentlich nur, um IntelliSense zu nutzen. Wir könnten auch einfach mit der Variablen ctl weiterarbeiten.

Die zweite If…Then-Bedingung prüft, ob es sich bei dem mit txt referenzierten Steuerelement um das Steuerelement handelt, das aktuell den Fokus hat. Das dient wie im obigen Beispiel dazu, den Inhalt des Textfeldes entweder über die Text– oder die Value-Eigenschaft auszulesen.

Wenn dann noch die Eigenschaft Text beziehungsweise Value des Textfeldes nicht leer ist, fügt die Prozedur ein Kriterium zur Variablen strFilter hinzu. Hier ermitteln wir mit Mid(txt.Name, 10) aus einem Textfeld wie txtFilterKundennummer den Wert Kundennummer, welcher dem Namen des im Filterausdruck zu verwendenden Feldes entspricht.

Auf diese Weise durchlaufen wir alle Steuerelemente und setzen die Informationen aus den Feldern, deren Name mit txtFilter beginnt, als Filterausdruck in der Variablen strFilter zusammen. Die Zuweisung des Filterausdrucks zum Unterformular erfolgt analog zu der im zuvor beschriebenen Beispiel.

Vorteil beim Filtern per Schleife

Die Programmierung der hier vorgestellten Schleife ist zwar etwas aufwendiger, als wenn wir die betroffenen Filter-Textfelder nacheinander untersuchen. Allerdings sparen wir eine Menge Code und, was noch wichtiger ist: Wir können nun weitere Filter-Textfelder hinzufügen oder vorhandene Entfernen und brauchen den Code nicht mehr zu ändern.

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