Datenblatt-Suchleiste

Datenblätter liefern ausreichende Möglichkeiten zum Durchsuchen und Filtern ihrer Inhalte. Leider sind diese meist versteckt, sodass Otto Normalverbraucher üblicherweise erst mit der Nase darauf gestoßen werden muss. Viel schöner wäre es doch, wenn das Datenblatt über jeder Spalte ein entsprechendes Steuerelement zur Eingabe der gesuchten Werte enthielte. Schauen wir uns also an, welche Möglichkeiten es hier gibt und wie wir die auftretenden Klippen umschiffen.

Die Aufgabe, ein Datenblatt mit einer integrierten Leiste mit Suchfeldern zu erweitern, scheitert zuerst einmal an der Natur von Formularen, die Daten in der Datenblattansicht anzeigen: Sie zeigen nämlich außer dem reinen Datenblatt gar nichts an. Die gewünschte Suchleiste müsste also, zusammen mit dem Formular in der Datenblattansicht, in einem weiteren übergeordneten Formular angezeigt werden.

Schnellanleitung

Mit unserer Lösung aus diesem Beitrag realisieren Sie dies aber ganz schnell und flexibel. Und so geht es:

  • Fügen Sie die Objekte frmSearchbar, clsDatasheetSearch und clsDatasheetSearchControl zur Zieldatenbank hinzu.
  • Öffnen Sie das Formular mit dem Unterformular in der Datenblattansicht und fügen Sie darüber ein weiteres, flaches Unterformular-Steuerelement ein. Stellen Sie seine Eigenschaft Herkunftsobjekt auf frmSearchbar ein.
  • Fügen Sie dem Hauptformular eine Prozedur hinzu, die durch das Ereignis Beim Laden ausgelöst wird und ändern Sie diese wie in Listing 1.
  • Ersetzen Sie dabei <Unterformular> durch den Namen des Unterformular-Steuerelements mit dem Formular in der Datenblattansicht.
  • Ersetzen Sie außerdem <Suchformular> durch den Namen des Unterformular-Steuerelements, dem Sie das Formular frmSearchbar zugewiesen haben. Achtung: Der Name des Unterformular-Steuerelements entspricht nicht zwangsläufig dem darin angezeigten Unterformular!

Listing 1: Mehr als dieser Code ist für den Betrieb der Suchleiste nicht notwendig.

Dim objDatasheetSearch As clsDatasheetSearch
Private Sub Form_Load()
    Set objDatasheetSearch = New clsDatasheetSearch
    With objDatasheetSearch
        Set .DatasheetControl = Me!<Unterformular>
        Set .SearchformControl = Me!<Suchformular>
    End With
End Sub

Such-Steuerelemente

Sie müssten also normalerweise die benötigten Steuerelemente – Textfelder, Kombinationsfelder und Kontrollkästchen – oberhalb des Unterformular-Steuerelements mit dem Unterformular in der Datenblattansicht einfügen. Diese werden dann in der entsprechenden Breite angelegt.

Die Suchfelder für normale Textfelder bleiben zunächst leer, die Suchfelder für Werte aus Kombinationsfeldern sollen ebenfalls als Kombinationsfelder ausgeführt werden. So kann der Benutzer einen der Einträge auswählen und damit die Datensätze des Unterformulars nach diesem Eintrag filtern (s. Bild 1).

pic001.png

Bild 1: Unterformular mit darüber liegenden Suchfeldern

Bei Kontrollkästchen im Unterformular müssen wir uns etwas einfallen lassen: Ein solches zeigt ja standardmäßig nur die Werte Wahr und Falsch an. Wir möchten aber auch die Möglichkeit bieten, sowohl Datensätze mit dem Feldinhalt Wahr als auch Falsch anzuzeigen.

Wenn dann als Suchsteuerelement ebenfalls ein Kontrollkästchen zum Einsatz kommt, ist dies nicht möglich. Also verwenden wir ein Kombinationsfeld, mit dem der Benutzer die drei Werte Alle, Wahr und Falsch auswählen kann.

Dementsprechend müssten auch die Steuerelemente zum Filtern von Datensätzen nach dem Inhalt von Kombinationsfeldern einen Eintrag namens <Alle> enthalten. Anderenfalls müsste der Benutzer das Kombinationsfeld von Hand leeren, was wenig intuitiv erscheint.

Datenblatt und fixe Suchfelder

Nun würden wir also ein Datenblatt aufbauen, es als Unterformular in ein anderes Formular integrieren und die gewünschten Suchsteuerelemente über den jeweiligen Spalten anordnen. Dies könnte wie in Bild 2 aussehen. Dann funktioniert die Suche aber auch nur solange, bis der Benutzer eine der folgenden Aktionen durchführt:

pic002.png

Bild 2: Zwei Unterformular für Suchfelder und Datenblatt

  • Verändern der Spaltenbreite eines Feldes
  • Vertauschen zweier Felder
  • Ein-/Ausblenden von Feldern
  • Scrollen des Datenblatts, wenn die Felder nicht gleichzeitig dargestellt werden können

All dies führt dazu, dass die Suchsteuerelemente nicht mehr richtig über den entsprechenden Feldern stehen. Fazit: Starr eingebaute Steuerelemente eignen sich nur für die Suche in Datenblattansichten, deren Layout nicht durch den Benutzer verändert werden kann.

Sprich: Für den Moment wirklich etwas zu langweilig. Wir wollen mehr!

Flexibles Such-Unterformular

Und zwar eine variabel einsetzbare Lösung, die nicht nur für ein einziges Datenblatt funktioniert, sondern möglichst für alle möglichen. Daher zunächst folgende Ideen:

  • Die Steuerelemente zum Eingeben von Such-/Filterbegriffen sollen in einem eigenen Unterformular liegen, damit diese in verschiedenen Formularen eingesetzt werden können.
  • Es sollen ausreichend Steuerelemente vorliegen, um auch breite Datenblatt-Unterformulare mit Suchfeldern ausstatten zu können. Also legen wir uns auf 20 fest, was auch breite Datenblätter abdecken sollte. Gegebenenfalls kann man dies auch erweitern.
  • Dummerweise weiß man vorher nie, wieviele Textfelder, Kombinationsfelder und Kontrollkästchen das Datenblatt enthält. Also legen wir diese flexibel an: Je ein Textfeld, Kombinationsfeld und Kontrollkästchen für jedes Feld des Datenblatts, für 20 Spalten also immerhin 60 Steuerelemente. Wir blenden ein, was für das jeweilige Feld des Datenblatts benötigt wird.
  • Beim Öffnen des Formulars soll Code das Datenblatt-Unterformular untersuchen und die entsprechenden Steuerelemente im Such-Unterformular ein-/ausblenden, auf die richtige Breite anpassen und gegebenenfalls mit Daten füllen (gilt nur für Kombinationsfelder und Kontrollkästchen).
  • Wenn der Benutzer die Datenblatt-Konfiguration ändert, also etwa Spaltenbreiten anpasst, Spalten vertauscht, ein- oder ausblendet oder scrollt, müssen die Steuerelemente im Such-Unterformular angepasst werden. Wann und wie dies geschieht, erfahren Sie weiter unten im Detail.

Grundlage: Ausreichend Steuerelemente!

Das Unterformular mit den Such-Steuerelementen soll frmSearchbar heißen. Der Rest, also das Hauptformular und das Unterformular mit der Datenblattansicht, können nach Wunsch benannt werden – das Suchformular soll ohnehin dynamisch daran angepasst werden.

Bevor wir überhaupt mit der eigentlichen Programmierung der Lösung beginnen, statten wir das Unterformular mit den oben erwähnten 60 Steuerelementen für die Eingabe der Suchkriterien aus.

Erfreulicherweise brauchen Sie nicht einen Handschlag selbst durchzuführen, denn wir haben eine kleine Routine programmiert, die diese Aufgabe für uns erledigt.

Die Prozedur SuchleistenformularErstellen aus Listing 2 löscht zunächst ein eventuell vorhandenes Formular gleichen Namens und erstellt dann mit der CreateForm-Methode ein neues Formular. Dieses referenziert es dann mit der Objektvariablen frm und öffnet es in der Entwurfsansicht.

Listing 2: Anlegen des Suchformulars

Public Sub SuchleistenformularErstellen()
    ...
    On Error Resume Next
    DoCmd.DeleteObject acForm, "frmSearchbar"
    On Error GoTo 0
    Set frm = Application.CreateForm
    strForm = frm.Name
    DoCmd.OpenForm frm.Name, acDesign
    For i = 1 To 20
        Set txt = Application.CreateControl(frm.Name, acTextBox, acDetail)
        With txt
            .Name = "txt" & Format(i, "00")
            .Visible = False
        End With
        Set cbo = Application.CreateControl(frm.Name, acComboBox, acDetail)
        With cbo
            .Name = "cbo" & Format(i, "00")
            .Visible = False
        End With
        Set chk = Application.CreateControl(frm.Name, acComboBox, acDetail)
        With chk
            .Name = "chk" & Format(i, "00")
            .Visible = False
            .RowSourceType = "Value List"
            .RowSource = "''Alle'';''Wahr'';''Falsch''"
        End With
    Next i
    With frm
        .NavigationButtons = False
        .RecordSelectors = False
        .ScrollBars = False
        .DividingLines = False
        .HasModule = True
    End With
    DoCmd.Close acForm, frm.Name, acSaveYes
    DoCmd.Rename "frmSearchbar", acForm, strForm
End Sub

Die Hauptarbeit führt dann eine Schleife mit 20 Durchläufen aus: Sie legt jeweils ein Steuerelement zum Eingeben von Filterkriterien für Textbox-, ComboBox– und Checkbox-Steuerelemente an und weist diesen durchnummerierte Namen wie txt01, cbo01 oder chk01 zu.

Die mit chk… benannten Steuerelemente sind jedoch, wie oben bereits beschrieben, keine Kontrollkästchen, sondern Kombinationsfelder, die lediglich zur Auswahl der Werte Alle, Wahr und Falsch dienen.

Alle Steuerelemente werden zunächst mit der Eigenschaft Visible auf Unsichtbar eingestellt. Nach dem Anlegen der Steuerelemente legt die Prozedur noch einige Eigenschaften für das Unterformular fest. So werden die Datensatzmarkierer, die Navigationsleiste, Bildlaufleisten und Trennlinien ausgeschaltet.

Außerdem wird mit HasModule = True ein Klassenmodul zum Formular hinzugefügt. Schließlich speichert die Routine das neue Formular und benennt es in frmSearchbar um.

Probieren Sie es aus – Formularerstellung per Mausklick! Die Steuerelemente würden, sorgfältig ausgerichtet, wie in Bild 3 aussehen.

pic003.png

Bild 3: Alle Steuerelemente des Formulars frmSearchbar

Klasseneinsatz

Den größten Teil des Codes dieser Lösung könnte man im Unterformular frmSearchbar unterbringen. Allerdings müssen auch die 60 Steuerelemente dieses Formulars Ereignisprozeduren besitzen, die etwa nach Eingabe oder änderung von Suchbegriffen ausgelöst werden.

Da zumindest jeweils 20 Steuerelemente identische Anweisungen aufrufen dürften, wollen wir aus Gründen der Wartbarkeit zwei Klassenmodule einführen.

Das erste heißt clsDatasheetSearch und enthält die wesentlichen Elemente der Steuerung des Suchformulars. Es greift auch durch das Datenblatt-Formular ausgelöste Aktionen – wie etwa das ändern der Spaltenbreiten oder -anordnung – auf und reagiert entsprechend darauf.

Die zweite Klasse heißt clsDatasheetSearchControl und wird für jedes der 60 Steuerelemente einmal instanziert. Diese Klasse enthält im Wesentlichen Ereignisprozeduren, die beim ändern der Suchbegriffe durch den Benutzer ausgelöst werden und in der Folge die angezeigten Datensätze im Datenblatt-Unterformular filtern sollen.

Black Box

Eigentlich können Sie die beiden Klassen als Black Box betrachten. Damit die Suchleiste funktioniert, brauchen Sie nämlich nur ganz wenige Aufgaben zu erledigen.

Sie fügen das Formular frmSearchbar zum Hauptformular hinzu, fügen darunter in der gleichen Breite das Formular mit der Datenblattansicht hinzu und legen einige Zeilen Code an.

Zunächst brauchen Sie eine Zeile, die eine Objektvariable für eine Instanz der Klasse clsDatasheetSearch aufnehmen kann:

Dim objDatasheetSearch As clsDatasheetSearch

Außerdem legen Sie für das Hauptformular eine Ereignisprozedur an, die durch das Ereignis Beim Laden ausgelöst wird und wie folgt aussieht:

Private Sub Form_Load()
    Set objDatasheetSearch = New clsDatasheetSearch
    With objDatasheetSearch
        Set .DatasheetControl = Me!sfmDatasheet
        Set .SearchformControl = Me!sfmSearchform
    End With
End Sub

Diese instanziert das Objekt und stellt zwei seiner Eigenschaften ein. Der Eigenschaft DatasheetControl weisen Sie einen Verweis auf das Unterformularsteuerelement zur Anzeige der Datenblattansicht zu und der Eigenschaft SearchformControl einen Verweis auf das Unterformularsteuerelement mit dem Unterformular frmSearchbar.

Für beides ermitteln Sie zunächst den Namen des jeweiligen Unterformular-Steuerelements, etwa sfmDatasheet oder sfmSearchform, und weisen diese den Eigenschaften des Objekts objDatasheetSearch zu.

Innereien der Klasse clsDatasheetSearch

Natürlich wollen wir Ihnen an dieser Stelle die Funktionsweise der Klasse nicht vorenthalten – dies ist unabdingbar, wenn Sie eigene Feineinstellungen vornehmen möchten.

Die Klasse definiert zunächst vier Objektvariablen. Die ersten beiden speichern Verweise auf die vom instanzierenden Formular übergebenen Unterformular-Steuerelemente:

Private m_sfmSearch As SubForm
Private m_sfmDatasheet As SubForm

Die Klasse benötigt aber auch Zugriff auf die darin enthaltenen Formulare. Objektverweise darauf landen später in diesen Variablen:

Private m_frmSearch As Form
Private WithEvents m_frmDatasheet As Form

Die Variable für das Formular in der Datenblattansicht wird als WithEvents deklariert, was bedeutet, das wir in der Klasse clsDatasheetSearch mit entsprechenden Prozeduren auf die Ereignisse dieses Formulars reagieren können. Warum dies notwendig ist, erfahren Sie gleich.

Datenblatt- und Suchformular festlegen

Das Festlegen der beiden Unterformularsteuerelemente aus Sicht des Hauptformulars haben Sie weiter oben schon kennengelernt. Nun schauen wir uns an, was dabei innerhalb der Klasse clsDatasheetSearch geschieht.

Die öffentliche Property Set-Prozedur DatasheetControl schreibt den übergebenen Verweis auf das Unterformular mit dem Datenblatt in die Variable m_sfmDatasheet und den daraus abzuleitenden Verweis auf das enthaltene Unterformular in die Objektvariable m_frmDatasheet.

Dann erledigt sie wichtige Aufgaben im Hinblick auf die ständige Aktualisierung der Anordnung der Elemente der Suchleiste: Sie stellt die Eigenschaft TimerInterval des in m_frmDatasheet gespeicherten Formulars auf den Wert 10 ein, was bedeutet, dass die Ereignisprozedur Bei Zeitgeber ziemlich oft ausgelöst wird.

Damit diese jedoch überhaupt feuert, fehlen noch zwei Dinge: erstens die Zuweisung des Wertes [Event Procedure] zur Eigenschaft OnTimer des Formular-Objekts und zweitens die Definition einer entsprechenden Ereignisprozedur innerhalb der Klasse clsDatasheetSearch (hierzu gleich mehr).

Schließlich wird auch noch die Ereigniseigenschaft Bei Mauszeiger ab mit dem Wert [Event Procedure] gefüllt, was dem manuellen Auswählen des Wertes [Ereignisprozedur] in der deutschen Access-Version entspricht.

Public Property Set DatasheetControl(sfm As SubForm)
    Set m_sfmDatasheet = sfm
    Set m_frmDatasheet = m_sfmDatasheet.Form
    With m_frmDatasheet
        .TimerInterval = 10
        .OnMouseUp = "[Event Procedure]"
        .OnTimer = "[Event Procedure]"
    End With
    Call FillCollection
End Property

Wozu diese Ereignisprozeduren Kurz gefasst: Sie prüfen erstens alle paar Millisekunden, ob der Benutzer die angezeigten Spalten im Datenblatt mit der Bildlaufleiste geändert oder ob er die Spalten des Datenblatts mit der Maus bezüglich Größe, Position oder Sichtbarkeit manipuliert hat.

In all diesen Situationen müssen nämlich auch die Suchfelder erneut an die Spalten angepasst werden. Eine Anweisung haben wir noch unterschlagen: FillCollection ruft die gleichnamige Prozedur auf, welche die folgende Collection-Variable füllen soll:

Private m_Fields As Collection

Die Prozedur FillCollection durchläuft alle Felder des Recordsets des Unterformulars mit dem Datenblatt. Für jedes Feld fügt sie der Collection m_Fields ein neues Objekts hinzu und trägt den Feldnamen als Key der Collection ein.

Private Sub FillCollection()
    Dim fld As DAO.Field
    Set m_Fields = New Collection
    For Each fld In m_frmDatasheet.Recordset.Fields
        m_Fields.Add Null, fld.Name
    Next fld
End Sub

Wir klären gleich, wofür wir diese Collection verwenden.

Die andere Property Set-Prozedur SearchformControl nimmt den Verweis auf das Unterformularsteuerelement mit dem Suchformular entgegen (s. Listing 3). Sie füllt die Variablen m_sfmSearch und m_frmSearch mit Verweisen auf das Unterformularsteuerelement und das Formular selbst.

Listing 3: Diese Routine nimmt den Verweis auf das Suchformular entgegen.

Public Property Set SearchformControl(sfm As SubForm)
    Dim i As Integer
    Set m_sfmSearch = sfm
    Set m_frmSearch = sfm.Form
    m_sfmSearch.Width = m_sfmDatasheet.Width
    m_sfmSearch.Left = m_sfmDatasheet.Left
    For i = 1 To 20
         m_frmSearch.Controls("txt" & Format(i, "00")).Top = m_frmSearch.InsideHeight * 0.05
        ...
        m_frmSearch.Controls("txt" & Format(i, "00")).Height = m_frmSearch.InsideHeight * 0.95
        ....
    Next i
End Property

Danach passt es die Breite des Unterformularsteuerelements so an, dass es der Breite des zu durchsuchenden Formulars entspricht. Aus diesem Grund muss das Formular mit dem Datenblatt auch zuerst übergeben werden.

Schließlich durchläuft die Prozedur alle Such-Steuerelemente und stellt deren Höhe entsprechend der Höhe des Such-Unterformulars ein.

Die Vorbereitungen sind damit abgeschlossen.

Aufbau der Such-Steuerelemente anstoßen

Zum aktuellen Zeitpunkt haben wir lediglich vier mit Verweisen auf die Unterformularsteuerelemente und den enthaltenen Formularen gefüllte Objektvariablen sowie eine Collection mit den Namen der Felder des Recordsets im Datenblatt.

Die Steuerelemente im Such-Unterformular befinden sich noch im jungfräulichen Zustand. Damit sich dies ändert, legen wir die beiden oben bereits angesprochenen Ereignisprozeduren an, die durch den Timer alle zehn Millisekunden sowie durch manuelle änderungen des Spaltenaufbaus im Unterformular ausgelöst werden.

Wenn der Benutzer an Spaltenbreiten, -sichtbarkeit oder -anordnung herumjustiert, erledigt er dies mit der Maus.

Nach Abschluss der jeweiligen änderung lässt er die Maustaste los, was das Ereignis Bei Maustaste auf auslöst. Die dadurch aufgerufene Prozedur sieht wie folgt aus und startet lediglich die Prozedur CustomizeControls (s. Listing 4).

Listing 4: Ereignisprozedur, die nach einem Mausklick auf die Spaltenköpfe feuert.

Private Sub m_frmDatasheet_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
    Call CustomizeControls
End Sub

Die durch den Timer ausgelöste Prozedur besitzt ein paar Zeilen Code mehr. Während ein Mausklick auf die Spaltenköpfe mit hoher Wahrscheinlichkeit auf ein ändern der Spalten abzielt, wird das Timer-Ereignis eher ausgelöst, um ja keine Scrollbewegung des Datenblatts zu verpassen.

Daher ruft das Ereignis OnMouseUp immer die Prozedur CustomizeControls zum Anpassen der Suchleiste auf, während das Timer-Ereignis zuerst prüft, ob eine änderung der Spalten durch das Scrollen im Datenblatt erfolgte (s. Listing 5).

Listing 5: Diese Routine wird alle 10 Millisekunden durch den Timer ausgelöst.

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