{"id":55001604,"date":"2026-06-01T00:00:00","date_gmt":"2026-05-06T11:11:33","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1604"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\/","title":{"rendered":"Ein Filterformular f&uuml;r alle F&auml;lle: Die Programmierung"},"content":{"rendered":"<p><b>Im ersten Teil dieser Beitragsreihe haben wir gezeigt, wie das Filterformular in eine eigene Datenbank eingebaut und bedient wird. In diesem zweiten Teil werfen wir einen Blick hinter die Kulissen: Wie ist das Filterformular aufgebaut, wie erkennt es den Feldtyp aus dem Recordset, wie baut es den SQL-WHERE-String zusammen, wie funktioniert die dynamische H&ouml;henanpassung &#8211; und welche technischen Besonderheiten mussten gel&ouml;st werden. Einige dieser L&ouml;sungen sind nicht auf den ersten Blick offensichtlich, liefern aber wertvolle Denkanst&ouml;&szlig;e f&uuml;r eigene Projekte.<\/b><\/p>\n<h2>Architektur: Zwei Bausteine, eine Schnittstelle<\/h2>\n<p>Das Filterformular besteht aus zwei Bausteinen: dem Modul <b>mdlFilter<\/b> mit allen Hilfsfunktionen und dem Formularmodul <b>Form_frmFilter<\/b> mit der gesamten Steuerlogik. Die Trennung ist bewusst gew&auml;hlt: <b>mdlFilter<\/b> enth&auml;lt ausschlie&szlig;lich Funktionen ohne Bindung an ein konkretes Formular &#8211; Feldtyperkennung, SQL-Aufbau, Datumsparsen, Wertevalidierung.<\/p>\n<p>Das Formularmodul nutzt diese Funktionen und k&uuml;mmert sich um alles was mit der Benutzeroberfl&auml;che zu tun hat: Steuerelemente ein- und ausblenden, Positionen setzen, Ereignisse verarbeiten.<\/p>\n<p>Diese klare Aufgabentrennung hat einen praktischen Vorteil: <b>mdlFilter<\/b> kann unver&auml;ndert wiederverwendet werden wenn das Formular k&uuml;nftig angepasst oder durch eine andere Darstellung ersetzt wird.<\/p>\n<p>Die Kommunikation zwischen dem Filterformular und dem aufrufenden Formular l&auml;uft &uuml;ber zwei &ouml;ffentliche Eigenschaften und eine &ouml;ffentliche Prozedur. Das aufrufende Formular &uuml;bergibt beim &Ouml;ffnen &uuml;ber <b>CallingForm<\/b> eine Referenz auf sich selbst und &uuml;ber <b>RecordsetForm<\/b> das zu filternde Recordset.<\/p>\n<p>Das Filterformular ruft sp&auml;ter <b>FilterAnwenden<\/b> im aufrufenden Formular auf und &uuml;bergibt den fertigen <b>WHERE<\/b>-String als Parameter. Diese drei Elemente bilden die vollst&auml;ndige Schnittstelle &#8211; mehr ist f&uuml;r den Einbau nicht notwendig. Das folgende Listing zeigt die beiden Property-Prozeduren:<\/p>\n<pre><span style=\"color:blue;\">Public Property <span style=\"color:blue;\">Set<\/span> <\/span>CallingForm(frm<span style=\"color:blue;\"> As <\/span>Form)\r\n    <span style=\"color:blue;\">Set<\/span> m_frmAufrufend = frm\r\n<span style=\"color:blue;\">End Property<\/span>\r\n<span style=\"color:blue;\">Public Property <span style=\"color:blue;\">Set<\/span> <\/span>RecordsetForm(rst<span style=\"color:blue;\"> As Object<\/span>)\r\n    <span style=\"color:blue;\">Set<\/span> m_rst = rst\r\n    m_bolDAO = (TypeName(rst) = \"Recordset\") _\r\n        Or (TypeName(rst) = \"Recordset2\")\r\n    LadeFelder\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p>Der Parameter <b>RecordsetForm<\/b> ist als <b>Object<\/b> typisiert statt als konkreter Recordset-Typ.<\/p>\n<p>Das hat zwei Vorteile: Erstens funktioniert die &Uuml;bergabe sowohl mit einem DAO-Recordset als auch mit einem ADODB-Recordset, ohne dass der Aufrufer sich um den Typ k&uuml;mmern muss.<\/p>\n<p>Zweitens ist das Filterformular damit f&uuml;r k&uuml;nftige Erweiterungen vorbereitet. Das Flag <b>m_bolDAO<\/b> wird &uuml;ber <b>TypeName<\/b> gesetzt: Ein DAO-Recordset tr&auml;gt den Typnamen <b>Recordset<\/b> oder <b>Recordset2<\/b> &#8211; je nach Access-Version und Datenbanktyp &#8211; ein ADODB-Recordset dagegen nicht.<\/p>\n<p>Dieses Flag ist entscheidend f&uuml;r die Feldtyperkennung, wie wir im n&auml;chsten Abschnitt sehen werden.<\/p>\n<p>Sobald <b>RecordsetForm<\/b> gesetzt wird, ruft die Property-Prozedur automatisch <b>LadeFelder<\/b> auf. Diese Prozedur liest alle Felder des Recordsets aus, bestimmt f&uuml;r jedes den Feldtyp und bef&uuml;llt die <b>RowSource<\/b> aller 25 <b>cboFeld<\/b>-Kombinationsfelder mit den Feldnamen.<\/p>\n<p>Das bedeutet: Der Benutzer sieht im Feldauswahl-Dropdown sofort nach dem &Ouml;ffnen alle verf&uuml;gbaren Felder &#8211; ohne dass eine manuelle Konfiguration der Felder notwendig w&auml;re. Das Filterformular passt sich damit automatisch an jedes Formular an, dem es zugewiesen wird.<\/p>\n<h2>Das Steuerelemente-Raster: 175 Elemente auf einem Haufen<\/h2>\n<p>Das Filterformular unterst&uuml;tzt bis zu f&uuml;nf <b>ODER<\/b>-Gruppen mit je f&uuml;nf Filterzeilen &#8211; also 25 Zeilen insgesamt.<\/p>\n<p>Jede Zeile besteht aus sieben Steuerelementen die je nach Feldtyp und gew&auml;hlter Bedingung wechselweise ein- und ausgeblendet werden.<\/p>\n<p>Die Steuerelemente pro Zeile im &Uuml;berblick: <b>cboFeld_g_z<\/b> f&uuml;r die Feldauswahl, <b>cboBedingung_g_z<\/b> f&uuml;r den Vergleichsoperator, <b>cboDatumstyp_g_z<\/b> f&uuml;r den Datumstyp bei Datumsfeldern, <b>txtWert_g_z<\/b> f&uuml;r den Vergleichswert bei Text- und Zahlenfeldern, <b>txtDatum_g_z<\/b> f&uuml;r konkrete Datumseingaben, <b>txtDatumA_g_z<\/b> f&uuml;r den Bis-Wert bei Datum-Zwischen-Bedingungen, <b>txtWert2_g_z<\/b> f&uuml;r den Bis-Wert bei Zahlen-Zwischen-Bedingungen und <b>cmdLoeschen_g_z<\/b> zum Entfernen der Zeile.<\/p>\n<p>Das Namensschema ist einheitlich: <b>g<\/b> steht f&uuml;r die Gruppennummer (1 bis 5), <b>z<\/b> f&uuml;r die Zeilennummer (1 bis 5).<\/p>\n<p>Alle Ereignisprozeduren im Formularmodul folgen demselben Schema und delegieren an generische Hilfsprozeduren: <b>cboFeld_1_1_AfterUpdate<\/b> ruft <b>FeldGewaehlt 1, 1<\/b> auf, <b>cmdLoeschen_2_3_Click<\/b> ruft <b>ZeileLeeren 2, 3<\/b> auf.<\/p>\n<p>Das macht den Code &uuml;berschaubar trotz der gro&szlig;en Zahl von Ereignisprozeduren. F&uuml;r alle 25 Zeilen entstehen so je Ereignistyp 25 identisch aufgebaute Einzeiler. Die eigentliche Logik steckt in den generischen Hilfsprozeduren die <b>g<\/b> und <b>z<\/b> als Parameter erhalten und alle Steuerelementnamen daraus zusammensetzen &#8211; also etwa <b>Me(&#8222;cboFeld_&#8220; &#038; g &#038; &#8222;_&#8220; &#038; z)<\/b>.<\/p>\n<p>Alle 175 Einzelsteuerelemente liegen im Formular an der Y-Position 0, also &uuml;bereinander gestapelt. Nur die jeweils aktiven Zeilen werden sichtbar gemacht und an ihre richtige vertikale Position verschoben.<\/p>\n<p>Unsichtbare Zeilen bleiben bei Y=0 und nehmen keinen Platz ein. Diese Technik ist der zentrale Trick der es &uuml;berhaupt erm&ouml;glicht, die H&ouml;he des Formulars dynamisch zu steuern: Access richtet die H&ouml;he des Detailbereichs nicht nach den unsichtbaren Steuerelementen aus, sondern nach dem Wert der Eigenschaft <b>Section(acDetail).Height<\/b> &#8211; und den setzen wir per Code.<\/p>\n<p>W&uuml;rden die unsichtbaren Steuerelemente an ihren urspr&uuml;nglichen Positionen verbleiben, w&uuml;rde Access den Detailbereich nicht kleiner darstellen als notwendig um alle Elemente &#8211; auch die unsichtbaren &#8211; zu umschlie&szlig;en. Das Formular w&auml;re dann stets so hoch wie alle 25 Zeilen zusammen.<\/p>\n<h2>Feldtypen erkennen: Die DAO\/ADODB-Kollision<\/h2>\n<p>Sobald das Recordset &uuml;bergeben wurde, liest <b>LadeFelder<\/b> alle Felder aus und bestimmt f&uuml;r jedes den Feldtyp. Das Filterformular unterscheidet vier eigene Feldtypen, die als &ouml;ffentliche Konstanten in <b>mdlFilter<\/b> definiert sind: <b>cFeldtypText<\/b> (1), <b>cFeldtypZahl<\/b> (2), <b>cFeldtypDatum<\/b> (3) und <b>cFeldtypJaNein<\/b> (4).<\/p>\n<p>Diese vier Typen steuern, welche Vergleichsoperatoren angeboten werden, welches Eingabefeld erscheint und wie der Wert sp&auml;ter im SQL-Ausdruck gequotet wird. Die Zuordnung vom nativen Typwert des Recordset-Feldes auf einen dieser vier Typen &uuml;bernimmt die Funktion <b>GetFeldtyp<\/b> in <b>mdlFilter<\/b>. Sie erh&auml;lt neben dem numerischen Typwert auch das Flag <b>bolDAO<\/b> (siehe Listing 1).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>GetFeldtyp(lngTyp<span style=\"color:blue;\"> As Long<\/span>, bolDAO<span style=\"color:blue;\"> As Boolean<\/span>)<span style=\"color:blue;\"> As Long<\/span>\r\n    <span style=\"color:blue;\">If <\/span>bolDAO<span style=\"color:blue;\"> Then<\/span>\r\n        Select Case lngTyp\r\n            <span style=\"color:blue;\">Case <\/span>2, 3, 4, 5, 6, 7, 15, 16, 17, 18, 19, 20\r\n                GetFeldtyp = cFeldtypZahl\r\n            <span style=\"color:blue;\">Case <\/span>8\r\n                GetFeldtyp = cFeldtypDatum  'dbDate = 8\r\n            <span style=\"color:blue;\">Case <\/span>1\r\n                GetFeldtyp = cFeldtypJaNein 'dbBoolean = 1\r\n            <span style=\"color:blue;\">Case Else<\/span>\r\n                GetFeldtyp = cFeldtypText\r\n        <span style=\"color:blue;\">End Select<\/span>\r\n    <span style=\"color:blue;\">Else<\/span>\r\n        Select Case lngTyp\r\n            <span style=\"color:blue;\">Case <\/span>2, 3, 4, 5, 6, 14, 15, 16, 17, 18, 19, 20, 21, 131\r\n                GetFeldtyp = cFeldtypZahl\r\n            <span style=\"color:blue;\">Case <\/span>7, 133, 134, 135\r\n                GetFeldtyp = cFeldtypDatum  'adDate = 7\r\n            <span style=\"color:blue;\">Case <\/span>11\r\n                GetFeldtyp = cFeldtypJaNein 'adBoolean = 11\r\n            <span style=\"color:blue;\">Case Else<\/span>\r\n                GetFeldtyp = cFeldtypText\r\n        <span style=\"color:blue;\">End Select<\/span>\r\n    <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: GetFeldtyp unterscheidet DAO- und ADODB-Typnummern<\/span><\/b><\/p>\n<p>Warum ist die strikte Trennung zwischen DAO und ADODB notwendig? Der Grund ist eine handfeste Kollision der Typnummern: DAO verwendet f&uuml;r den Typ <b>dbDouble<\/b> (Gleitkommazahl) den Wert 7, ADODB verwendet denselben Wert 7 f&uuml;r <b>adDate<\/b> (Datum).<\/p>\n<p>Eine einheitliche Behandlung beider Bibliotheken mit derselben Fallunterscheidung ist daher nicht m&ouml;glich. W&uuml;rde man in einem DAO-Recordset den Wert 7 als Datum interpretieren, w&uuml;rden alle Double-Felder f&auml;lschlicherweise als Datumsfelder behandelt &#8211; mit entsprechend falschen Vergleichsoperatoren und Datumsformatierung im SQL-Ausdruck als Folge.<\/p>\n<p>Die L&ouml;sung ist das Flag <b>m_bolDAO<\/b> das beim Setzen von <b>RecordsetForm<\/b> automatisch &uuml;ber <b>TypeName<\/b> bestimmt wird. Im DAO-Zweig von <b>GetFeldtyp<\/b> wird der Wert 7 korrekt als Zahl (<b>dbDouble<\/b>) behandelt, w&auml;hrend DAO-Datumsfelder den eindeutigen Wert 8 (<b>dbDate<\/b>) tragen. Im ADODB-Zweig wird 7 dagegen korrekt als Datum (<b>adDate<\/b>) behandelt, w&auml;hrend ADODB-Boolean-Felder den Wert 11 (<b>adBoolean<\/b>) haben.<\/p>\n<p>Das <b>bolDAO<\/b>-Flag ist damit nicht nur eine technische Notwendigkeit f&uuml;r den aktuellen Stand des Filterformulars &#8211; es ist gleichzeitig die Vorbereitung f&uuml;r die geplante Erweiterung auf ADODB-gebundene Formulare.<\/p>\n<p>Wird das Filterformular k&uuml;nftig mit einem ADODB-Recordset aufgerufen, wird <b>m_bolDAO<\/b> automatisch auf <b>False<\/b> gesetzt und der ADODB-Zweig kommt zum Einsatz, ohne dass an <b>GetFeldtyp<\/b> etwas ge&auml;ndert werden muss.<\/p>\n<p>Der Aufruf in <b>LadeFelder<\/b> ist entsprechend einfach gehalten:<\/p>\n<pre>For i = 0 To m_rst.Fields.Count - 1\r\n    m_strFeldnamen(i) = m_rst.Fields(i).Name\r\n    m_lngFeldtypen(i) = GetFeldtyp( _\r\n        m_rst.Fields(i).Type, m_bolDAO)\r\n<span style=\"color:blue;\">Next<\/span> i<\/pre>\n<p>Die ermittelten Feldnamen und Feldtypen werden in den Modulvariablen <b>m_strFeldnamen<\/b> und <b>m_lngFeldtypen<\/b> gespeichert.<\/p>\n<p>Auf diese Arrays greift das Formularmodul sp&auml;ter zu, wenn der Benutzer ein Feld ausw&auml;hlt. Der Feldtyp steckt dann bereits im Array und muss nicht erneut aus dem Recordset ausgelesen werden.<\/p>\n<p>Das ist effizienter und vermeidet Probleme, wenn das Recordset zwischenzeitlich geschlossen oder ver&auml;ndert wurde.<\/p>\n<h2>Bedingungen und Wertfelder dynamisch einblenden<\/h2>\n<p>Sobald der Benutzer ein Feld ausw&auml;hlt, reagiert <b>FeldGewaehlt<\/b> auf das <b>AfterUpdate<\/b>-Ereignis des jeweiligen <b>cboFeld<\/b>-Kombinationsfelds. Die Prozedur ermittelt den Feldtyp des gew&auml;hlten Felds aus dem gespeicherten Array und bef&uuml;llt <b>cboBedingung<\/b> mit den passenden Operatoren &uuml;ber <b>GetBedingungRowsource<\/b> (siehe Listing 2).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>GetBedingungRowsource(lngFeldtyp<span style=\"color:blue;\"> As Long<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n    Select Case lngFeldtyp\r\n        <span style=\"color:blue;\">Case <\/span>cFeldtypText\r\n            GetBedingungRowsource = _\r\n                \"Gleich;Nicht gleich;Enthaelt;Enthaelt nicht;Beginnt mit;Endet mit;Ist leer;Ist nicht leer\"\r\n        <span style=\"color:blue;\">Case <\/span>cFeldtypZahl\r\n            GetBedingungRowsource = _\r\n                \"Gleich;Nicht gleich;Groesser als;Kleiner als;Ist zwischen;Ist leer;Ist nicht leer\"\r\n        <span style=\"color:blue;\">Case <\/span>cFeldtypDatum\r\n            GetBedingungRowsource = _\r\n                \"Gleich;Ist vor;Ist nach;Ist zwischen;Ist leer;Ist nicht leer\"\r\n        <span style=\"color:blue;\">Case <\/span>cFeldtypJaNein\r\n            GetBedingungRowsource = \"Wahr;Falsch\"\r\n    <span style=\"color:blue;\">End Select<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: GetBedingungRowsource liefert die passenden Operatoren je Feldtyp<\/span><\/b><\/p>\n<p>Gleichzeitig mit dem Aktivieren des Bedingungsfelds blendet <b>FeldGewaehlt<\/b> alle Wertfelder der Zeile aus und setzt den Wert von <b>cboBedingung<\/b> auf <b>Null<\/b>. Ein Feldwechsel macht die bisherige Bedingung ung&uuml;ltig &#8211; ein Textoperator wie <b>Enth&auml;lt<\/b> ergibt f&uuml;r ein Zahlenfeld keinen Sinn. Au&szlig;erdem setzt <b>FeldGewaehlt<\/b> die <b>Left<\/b>-Positionen aller Wertfelder auf ihre Ursprungswerte zur&uuml;ck.<\/p>\n<p>Das ist notwendig weil bei Datumsfeldern mit der Bedingung <b>Ist zwischen<\/b> die Positionen der Eingabefelder per Code ver&auml;ndert werden &#8211; dazu gleich mehr.<\/p>\n<p>Schlie&szlig;lich blendet <b>FeldGewaehlt<\/b> automatisch eine neue leere Zeile unterhalb ein, sofern die aktuelle Zeile die letzte aktive in der Gruppe ist und noch nicht f&uuml;nf Zeilen belegt sind.<\/p>\n<p>Sobald der Benutzer eine Bedingung ausw&auml;hlt, entscheidet <b>BedingungGewaehlt<\/b> welche Eingabefelder eingeblendet werden. Bei Bedingungen wie <b>Ist leer<\/b>, <b>Ist nicht leer<\/b>, <b>Wahr<\/b> oder <b>Falsch<\/b> erscheint kein Eingabefeld &#8211; der Operator definiert bereits vollst&auml;ndig den Filterausdruck. F&uuml;r Textfelder erscheint <b>txtWert<\/b>.<\/p>\n<p>F&uuml;r Zahlenfelder erscheint ebenfalls <b>txtWert<\/b>, bei <b>Ist zwischen<\/b> zus&auml;tzlich <b>txtWert2<\/b> f&uuml;r den Bis-Wert. Bei Datumsfeldern mit <b>Ist zwischen<\/b> erscheinen <b>txtDatum<\/b> (Von) und <b>txtDatumA<\/b> (Bis) nebeneinander.<\/p>\n<p>Bei allen anderen Datumsbedingungen erscheint das Kombinationsfeld <b>cboDatumstyp<\/b> mit den vordefinierten Zeitr&auml;umen &#8211; und je nach gew&auml;hltem Datumstyp zus&auml;tzlich <b>txtDatum<\/b> f&uuml;r ein konkretes Datum oder <b>txtWert<\/b> f&uuml;r die Anzahl Tage bei <b>Letzte X Tage<\/b>.<\/p>\n<p>Da alle Steuerelemente einer Zeile an derselben X-Position liegen, werden beim Wechsel der Bedingung zuerst alle ausgeblendet und dann nur das jeweils ben&ouml;tigte eingeblendet. Bei Datumsfeldern mit <b>Ist zwischen<\/b> m&uuml;ssen <b>txtDatum<\/b> und <b>txtDatumA<\/b> nebeneinander sichtbar sein.<\/p>\n<p>Da beide im Formular &uuml;bereinander bei derselben X-Position liegen, setzt das Formular beim Einblenden die <b>Left<\/b>-Eigenschaft von <b>txtDatum<\/b> auf die Position des Wertfelds und platziert <b>txtDatumA<\/b> direkt dahinter.<\/p>\n<p>Beim Wechsel zu einer anderen Bedingung oder beim Wechsel des Felds wird <b>Left<\/b> auf den gespeicherten Ursprungswert zur&uuml;ckgesetzt. Dieser Ursprungswert ist als Konstante im Erstellungsmodul hinterlegt und gilt f&uuml;r alle 25 Zeilen gleicherma&szlig;en.<\/p>\n<h2>Eingaben validieren<\/h2>\n<p>Das Filterformular validiert Tastatureingaben in den Wertfeldern in Echtzeit. Jedes der 25 <b>txtWert<\/b>-Steuerelemente sowie die <b>txtDatum<\/b>-Felder implementieren ein <b>KeyPress<\/b>-Ereignis das an die zentrale Funktion <b>ValidiereEingabe<\/b> in <b>mdlFilter<\/b> delegiert. Diese erh&auml;lt den <b>KeyAscii<\/b>-Wert des gedr&uuml;ckten Zeichens sowie den Feldtyp der aktuellen Zeile und gibt den bereinigten <b>KeyAscii<\/b>-Wert zur&uuml;ck (siehe Listing 3).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>ValidiereEingabe(KeyAscii<span style=\"color:blue;\"> As Integer<\/span>, lngFeldtyp<span style=\"color:blue;\"> As Long<\/span>)<span style=\"color:blue;\"> As Integer<\/span>\r\n    Select Case lngFeldtyp\r\n        <span style=\"color:blue;\">Case <\/span>cFeldtypZahl\r\n            If <span style=\"color:blue;\">Not<\/span> (Chr(KeyAscii) Like \"[0-9]\" _\r\n                    Or KeyAscii = 44 _\r\n                    Or KeyAscii = 46 _\r\n                    Or KeyAscii = 45 _\r\n                    Or KeyAscii &lt; 32) Then\r\n                KeyAscii = 0\r\n            <span style=\"color:blue;\">End If<\/span>\r\n        <span style=\"color:blue;\">Case <\/span>cFeldtypDatum\r\n            If <span style=\"color:blue;\">Not<\/span> (Chr(KeyAscii) Like \"[0-9]\" _\r\n                    Or KeyAscii = 46 _\r\n                    Or KeyAscii = 45 _\r\n                    Or KeyAscii = 47 _\r\n                    Or KeyAscii &lt; 32) Then\r\n                KeyAscii = 0\r\n            <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">End Select<\/span>\r\n    ValidiereEingabe = KeyAscii\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: ValidiereEingabe filtert ung&uuml;ltige Zeichen je nach Feldtyp heraus<\/span><\/b><\/p>\n<p>Bei Zahlenfeldern l&auml;sst die Funktion Ziffern, Komma (ASCII 44), Punkt (46), Minuszeichen (45) und Steuerzeichen (ASCII-Wert kleiner als 32, also R&uuml;ck-, Tab-, Entf-Taste und so weiter) durch &#8211; alle anderen Zeichen werden durch R&uuml;ckgabe von 0 stillschweigend abgewiesen.<\/p>\n<p>Das Setzen von <b>KeyAscii<\/b> auf 0 ist die VBA-Standardmethode um eine Tastatureingabe zu unterdr&uuml;cken: Access ignoriert das Zeichen dann vollst&auml;ndig. Bei Datumsfeldern sind zus&auml;tzlich Punkt und Schr&auml;gstrich (47) erlaubt.<\/p>\n<p>F&uuml;r Textfelder und <b>Ja\/Nein<\/b>-Felder ist keine Validierung n&ouml;tig &#8211; Textfelder akzeptieren beliebige Zeichen, <b>Ja\/Nein<\/b>-Felder haben gar kein Eingabefeld.<\/p>\n<p>Dar&uuml;ber hinaus pr&uuml;ft <b>cmdAnwenden_Click<\/b> vor dem Aufbau des <b>WHERE<\/b>-Strings ob alle ausgef&uuml;llten Bedingungen auch einen Vergleichswert haben, sofern die gew&auml;hlte Bedingung einen erfordert. Fehlt ein Wert, erscheint eine Hinweismeldung mit dem Namen des betroffenen Feldes und die Verarbeitung wird abgebrochen.<\/p>\n<p>Nur Bedingungen wie <b>Ist leer<\/b>, <b>Ist nicht leer<\/b>, <b>Wahr<\/b> und <b>Falsch<\/b> sind von dieser Pr&uuml;fung ausgenommen. Bei Datumsfeldern mit <b>Ist zwischen<\/b> pr&uuml;ft die Funktion <b>PruefeDatumVonBis<\/b> zus&auml;tzlich ob das Von-Datum kleiner oder gleich dem Bis-Datum ist.<\/p>\n<h2>Dynamische H&ouml;henanpassung<\/h2>\n<p>Die dynamische H&ouml;henanpassung ist eine der zentralen Anforderungen des Filterformulars &#8211; und technisch anspruchsvoller als es auf den ersten Blick scheint. Das Formular soll beim &Ouml;ffnen kompakt sein und mit jeder neuen Filterzeile wachsen. Beim L&ouml;schen einer Zeile oder beim Zur&uuml;cksetzen soll es entsprechend schrumpfen.<\/p>\n<p>Umgesetzt wird das &uuml;ber zwei zusammenspielende Prozeduren: <b>PositioniereZeilen<\/b> setzt die <b>Top<\/b>-Eigenschaft jedes Steuerelements, <b>AktualisiereFormularHoehe<\/b> (siehe Listing 4) berechnet die ben&ouml;tigte Gesamth&ouml;he und setzt <b>Section(acDetail).Height<\/b> sowie <b>Me.InsideHeight<\/b>.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>AktualisiereFormularHoehe()\r\n    <span style=\"color:blue;\">Dim <\/span>lngHoehe<span style=\"color:blue;\"> As Long<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>g<span style=\"color:blue;\"> As Integer<\/span>, z<span style=\"color:blue;\"> As Integer<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>intSichtbar<span style=\"color:blue;\"> As Integer<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>bolErsteGruppe<span style=\"color:blue;\"> As Boolean<\/span>\r\n    lngHoehe = 400 'Spaltenueberschriften\r\n    bolErsteGruppe = <span style=\"color:blue;\">True<\/span>\r\n    For g = 1 To cMaxGruppen\r\n        intSichtbar = 0\r\n        For z = 1 To cMaxZeilen\r\n            <span style=\"color:blue;\">If <\/span>Me(\"cboFeld_\" & g & \"_\" & z).Visible Or Me(\"cboBedingung_\" & g & \"_\" & z).Visible<span style=\"color:blue;\"> Then<\/span>\r\n                intSichtbar = intSichtbar + 1\r\n            <span style=\"color:blue;\">End If<\/span>\r\n        <span style=\"color:blue;\">Next<\/span> z\r\n        <span style=\"color:blue;\">If <\/span>intSichtbar &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n            <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> bolErsteGruppe<span style=\"color:blue;\"> Then<\/span>\r\n                lngHoehe = lngHoehe + cODERHoehe + cZeileAbstand\r\n            <span style=\"color:blue;\">End If<\/span>\r\n            lngHoehe = lngHoehe + _\r\n                intSichtbar * (cZeileHoehe + cZeileAbstand)\r\n            bolErsteGruppe = <span style=\"color:blue;\">False<\/span>\r\n        <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">Next<\/span> g\r\n    PositioniereZeilen\r\n    Me.Section(acDetail).Height = lngHoehe\r\n    Me.InsideHeight = lngHoehe + Me.Section(acHeader).Height + Me.Section(acFooter).Height\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: AktualisiereFormularHoehe berechnet die H&ouml;he anhand tats&auml;chlich sichtbarer Zeilen<\/span><\/b><\/p>\n<p><b>AktualisiereFormularHoehe<\/b> ruft dabei <b>PositioniereZeilen<\/b> intern auf bevor es die H&ouml;he setzt &#8211; die Reihenfolge ist wichtig, denn die Positionen m&uuml;ssen stimmen bevor das Formular seine Gr&ouml;&szlig;e anpasst.<\/p>\n<p>Entscheidend ist, dass die H&ouml;he nicht anhand von Z&auml;hlvariablen wie <b>m_intAktiveZeilen<\/b> berechnet wird, sondern anhand der tats&auml;chlichen Sichtbarkeit der Steuerelemente.<\/p>\n<p>Ein fr&uuml;herer Ansatz mit Z&auml;hlvariablen f&uuml;hrte zu Problemen: Nach dem L&ouml;schen einer mittleren Zeile oder nach dem Ausblenden einer ganzen <b>ODER<\/b>-Gruppe war die Z&auml;hlvariable nicht mehr synchron mit der tats&auml;chlichen Anzahl sichtbarer Zeilen &#8211; das Formular zeigte dann eine falsche H&ouml;he. Die Pr&uuml;fung &uuml;ber <b>Visible<\/b> ist dagegen immer korrekt, weil sie den tats&auml;chlichen Zustand der Benutzeroberfl&auml;che widerspiegelt.<\/p>\n<p>Die Konstantenwerte f&uuml;r H&ouml;hen und Abst&auml;nde &#8211; <b>cZeileHoehe<\/b>, <b>cZeileAbstand<\/b>, <b>cODERHoehe<\/b> und <b>cGruppenAbstand<\/b> &#8211; sind als <b>Public Const<\/b> in <b>mdlFilter<\/b> definiert. Das stellt sicher dass das Formularmodul und das Erstellungsmodul stets dieselben Werte verwenden.<\/p>\n<p>W&uuml;rden die Werte an zwei Stellen gepflegt, k&ouml;nnten Inkonsistenzen entstehen &#8211; das Formular w&auml;re dann zu hoch oder zu niedrig f&uuml;r die tats&auml;chlich platzierten Steuerelemente.<\/p>\n<p><b>PositioniereZeilen<\/b> l&auml;uft durch alle Gruppen und Zeilen und setzt die <b>Top<\/b>-Position: Sichtbare Zeilen bekommen ihre berechnete Y-Position, unsichtbare Zeilen werden auf Y=0 gesetzt. Die Prozedur ber&uuml;cksichtigt dabei nur Gruppen die tats&auml;chlich sichtbare Zeilen haben &#8211; leere Gruppen werden &uuml;bersprungen ohne <b>ODER<\/b>-Trenner oder Abstand einzuf&uuml;gen. Das ist wichtig wenn eine mittlere <b>ODER<\/b>-Gruppe geleert wird: Die nachfolgenden Gruppen r&uuml;cken l&uuml;ckenlos nach oben. <b>cmdODERHinzufuegen<\/b> wird am Ende direkt unterhalb der letzten sichtbaren Zeile positioniert und wandert damit automatisch mit dem Formularinhalt (siehe Listing 5).<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>PositioniereZeilen()\r\n    <span style=\"color:blue;\">Dim <\/span>lngY<span style=\"color:blue;\"> As Long<\/span>, g<span style=\"color:blue;\"> As Integer<\/span>, z<span style=\"color:blue;\"> As Integer<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>bolErsteGruppe<span style=\"color:blue;\"> As Boolean<\/span>\r\n    lngY = 400\r\n    bolErsteGruppe = <span style=\"color:blue;\">True<\/span>\r\n    For g = 1 To cMaxGruppen\r\n        'Pruefen ob diese Gruppe sichtbare Zeilen hat\r\n        <span style=\"color:blue;\">Dim <\/span>bolHatZeilen<span style=\"color:blue;\"> As Boolean<\/span>\r\n        bolHatZeilen = <span style=\"color:blue;\">False<\/span>\r\n        For z = 1 To cMaxZeilen\r\n            If Me(\"cboFeld_\" & g & \"_\" & z).Visible Or _\r\n               Me(\"cboBedingung_\" & g & \"_\" & z).Visible Then\r\n                bolHatZeilen = <span style=\"color:blue;\">True<\/span>\r\n                <span style=\"color:blue;\">Exit For<\/span>\r\n            <span style=\"color:blue;\">End If<\/span>\r\n        <span style=\"color:blue;\">Next<\/span> z\r\n        <span style=\"color:blue;\">If <\/span>bolHatZeilen<span style=\"color:blue;\"> Then<\/span>\r\n            'ODER-Trenner positionieren (ausser bei erster sichtbarer Gruppe)\r\n            <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> bolErsteGruppe<span style=\"color:blue;\"> Then<\/span>\r\n                Me(\"lblODER_\" & g).Top = lngY\r\n                lngY = lngY + cODERHoehe + cZeileAbstand\r\n            <span style=\"color:blue;\">End If<\/span>\r\n            bolErsteGruppe = <span style=\"color:blue;\">False<\/span>\r\n            'Zeilen positionieren\r\n            For z = 1 To cMaxZeilen\r\n                If Me(\"cboFeld_\" & g & \"_\" & z).Visible Or _\r\n                    Me(\"cboBedingung_\" & g & \"_\" & z).Visible Then\r\n                    Me(\"cboFeld_\" & g & \"_\" & z).Top = lngY\r\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;    '... weitere Steuerelemente\r\n                    lngY = lngY + cZeileHoehe + cZeileAbstand\r\n                <span style=\"color:blue;\">Else<\/span>\r\n                    Me(\"cboFeld_\" & g & \"_\" & z).Top = 0\r\n                    Me(\"cboBedingung_\" & g & \"_\" & z).Top = 0\r\n                    '... weitere Steuerelemente\r\n                <span style=\"color:blue;\">End If<\/span>\r\n            <span style=\"color:blue;\">Next<\/span> z\r\n        <span style=\"color:blue;\">Else<\/span>\r\n            'Gruppe leer - alle auf Y=0\r\n            For z = 1 To cMaxZeilen\r\n                Me(\"cboFeld_\" & g & \"_\" & z).Top = 0\r\n                Me(\"cboBedingung_\" & g & \"_\" & z).Top = 0\r\n                '... weitere Steuerelemente\r\n            <span style=\"color:blue;\">Next<\/span> z\r\n        <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">Next<\/span> g\r\n    'ODER-Schaltflaeche positionieren\r\n    cmdODERHinzufuegen.Top = lngY\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: PositionereZeilen richtet die H&ouml;he der verschiedenen Steuerelemente ein.<\/span><\/b><\/p>\n<h2>ODER-Gruppen verwalten<\/h2>\n<p>Das Formular verwaltet den Zustand der aktiven Gruppen in den Modulvariablen <b>m_intAktiveGruppen<\/b> und <b>m_intAktiveZeilen(1 To 5)<\/b>. Wenn der Benutzer auf <b>ODER-Filtergruppe hinzuf&uuml;gen<\/b> klickt, wird <b>m_intAktiveGruppen<\/b> erh&ouml;ht, die erste Zeile der neuen Gruppe eingeblendet und <b>AktualisiereFormularHoehe<\/b> aufgerufen. Die Schaltfl&auml;che <b>ODER-Filtergruppe hinzuf&uuml;gen<\/b> erscheint und verschwindet dynamisch: Sie ist nur sichtbar wenn mindestens eine Bedingung in der aktuellen letzten Gruppe ausgef&uuml;llt ist, diese Gruppe noch nicht alle f&uuml;nf Zeilen belegt hat und noch nicht alle f&uuml;nf Gruppen aktiv sind. Diese Logik ist in <b>AktualisiereSchaltflaechen<\/b> zentralisiert, die bei jeder relevanten &Auml;nderung aufgerufen wird.<\/p>\n<p>Wird die letzte Bedingung einer <b>ODER<\/b>-Gruppe gel&ouml;scht (ausgenommen Gruppe 1, die immer bestehen bleibt), verschwindet die gesamte Gruppe automatisch: <b>ODER<\/b>-Trenner und Eingabezeile werden ausgeblendet.<\/p>\n<p><b>m_intAktiveGruppen<\/b> wird anschlie&szlig;end neu berechnet &#8211; nicht einfach dekrementiert, denn es k&ouml;nnte eine mittlere Gruppe geleert worden sein, w&auml;hrend eine sp&auml;tere noch Eintr&auml;ge enth&auml;lt. Das Formular iteriert daher nach dem L&ouml;schen durch alle Gruppen und setzt <b>m_intAktiveGruppen<\/b> auf die letzte Gruppe, die noch sichtbare Zeilen hat.<\/p>\n<h2>Den WHERE-String aufbauen<\/h2>\n<p>Wenn der Benutzer auf <b>Filter anwenden<\/b> klickt, sammelt <b>cmdAnwenden_Click<\/b> alle ausgef&uuml;llten Filterbedingungen in einem zweidimensionalen <b>Variant<\/b>-Array und &uuml;bergibt es an zwei Hilfsfunktionen in <b>mdlFilter<\/b>: <b>GetFilterausdruck<\/b> baut den SQL-Ausdruck f&uuml;r eine einzelne Zeile, <b>GetWhereString<\/b> kombiniert alle Gruppen zum vollst&auml;ndigen <b>WHERE<\/b>-String.<\/p>\n<p><b>GetFilterausdruck<\/b> erh&auml;lt Feldname, Bedingung, Wert, zweiten Wert, Feldtyp und Datumstyp als Parameter. Die kritische Aufgabe ist das korrekte Quoten des Werts abh&auml;ngig vom Feldtyp.<\/p>\n<p>Textwerte werden in einfache Anf&uuml;hrungszeichen gesetzt, wobei vorhandene Hochkommas im Wert selbst durch Verdopplung &#8222;escaped&#8220; werden. Datumswerte werden im US-Format mit Rauten als Begrenzer formatiert. Zahlenwerte ben&ouml;tigen kein Trennzeichen:<\/p>\n<pre>Select Case lngFeldtyp\r\n    <span style=\"color:blue;\">Case <\/span>cFeldtypZahl\r\n        strWertSQL = strWert\r\n    <span style=\"color:blue;\">Case <\/span>cFeldtypDatum\r\n        strWertSQL = \"#\" & _\r\n            Format(datVon, \"mm\/dd\/yyyy\") & \"#\"\r\n    <span style=\"color:blue;\">Case Else<\/span> 'Text\r\n        strWertSQL = Chr(39) & _\r\n            <span style=\"color:blue;\">Replace<\/span>(strWert, Chr(39), Chr(39) _\r\n                & Chr(39)) & Chr(39)\r\n<span style=\"color:blue;\">End Select<\/span><\/pre>\n<p>Beim Quoten von Textwerten wird <b>Chr(39)<\/b> statt eines direkt geschriebenen Hochkommas verwendet um Verwirrung im Quellcode zu vermeiden. <b>Chr(39)<\/b> ist das einfache Anf&uuml;hrungszeichen. <b>Replace(strWert, Chr(39), Chr(39) &#038; Chr(39))<\/b> verdoppelt alle Hochkommas im Wert &#8211; das ist die SQL-Standardmethode um Hochkommas in Zeichenketten zu escapen. Ein Name wie <b>O&#8217;Brien<\/b> wird damit zu <b>&#8218;O&#8220;Brien&#8216;<\/b> &#8211; ein g&uuml;ltiger SQL-Ausdruck.<\/p>\n<p>Bei <b>LIKE<\/b>-Ausdr&uuml;cken f&uuml;r Textoperatoren wird der Wert zus&auml;tzlich mit Platzhaltersternen umgeben: <b>LIKE &#8218;*Suchbegriff*&#8216;<\/b> f&uuml;r <b>Enth&auml;lt<\/b>, <b>LIKE &#8218;Suchbegriff*&#8216;<\/b> f&uuml;r <b>Beginnt mit<\/b> und <b>LIKE &#8218;*Suchbegriff&#8216;<\/b> f&uuml;r <b>Endet mit<\/b>.<\/p>\n<p><b>GetWhereString<\/b> kombiniert die Ausdr&uuml;cke: Innerhalb einer Gruppe werden alle Ausdr&uuml;cke mit <b>AND<\/b> verkn&uuml;pft, zwischen Gruppen mit <b>OR<\/b>.<\/p>\n<p>Jeder Teilausdruck und jede Gruppe werden in Klammern gesetzt. Ein Filter mit zwei Gruppen &#8211; Gruppe 1 mit KundeID und Firma, Gruppe 2 mit Geburtsdatum &#8211; ergibt etwa:<\/p>\n<pre>WHERE ((KundeID &gt; 95) AND (Firma LIKE 'M*'))\r\n    OR ((Geburtsdatum = #01\/23\/1971#))<\/pre>\n<p>Der Datumsbereich f&uuml;r vordefinierte Zeitr&auml;ume wie <b>Diesen Monat<\/b> oder <b>Diese Woche<\/b> wird in <b>GetFilterausdruck<\/b> zur Laufzeit berechnet. F&uuml;r <b>Diesen Monat<\/b> ermittelt die Funktion den ersten und letzten Tag des aktuellen Monats mit <b>DateSerial<\/b> und baut daraus einen Zwischen-Ausdruck mit zwei Datumsliteralen auf.<\/p>\n<p>F&uuml;r <b>Letzte X Tage<\/b> wird das Von-Datum als <b>Date &#8211; CLng(strWert)<\/b> berechnet. Diese Berechnungen finden zum Zeitpunkt des Klicks auf <b>Filter anwenden<\/b> statt &#8211; der Filter ist damit stets tagesaktuell.<\/p>\n<h2>Datumsangaben korrekt parsen<\/h2>\n<p>Eine h&auml;ufige Fehlerquelle bei der Verarbeitung von Datumseingaben in VBA ist die Funktion <b>CDate<\/b>. Sie interpretiert eine Zeichenkette wie <b>&#8222;23.1.1971&#8220;<\/b> nicht zuverl&auml;ssig als deutsches Datum: VBA liest <b>23.1<\/b> manchmal als Dezimalzahl 23,1 und wandelt diesen Wert in ein Datum um, was einem Datum Ende Januar 1900 entspricht.<\/p>\n<p>Das Verhalten ist systemabh&auml;ngig und deshalb schwer zu reproduzieren und zu debuggen &#8211; genau die Art von Problem, die in der Entwicklung nicht auff&auml;llt und erst beim Kunden sichtbar wird.<\/p>\n<p>Die L&ouml;sung ist die Funktion <b>ParseDatum<\/b> in <b>mdlFilter<\/b>, die das deutsche Format TT.MM.JJJJ &uuml;ber <b>DateSerial<\/b> verarbeitet:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>ParseDatum(strDatum<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Date<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>arrTeile()<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">On Error GoTo<\/span> Fehler\r\n    arrTeile = <span style=\"color:blue;\">Split<\/span>(strDatum, \".\")\r\n    <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">UBound<\/span>(arrTeile) &gt;= 2<span style=\"color:blue;\"> Then<\/span>\r\n        ParseDatum = DateSerial( _\r\n            CInt(arrTeile(2)), _\r\n            CInt(arrTeile(1)), _\r\n            CInt(arrTeile(0)))\r\n    <span style=\"color:blue;\">Else<\/span>\r\n        ParseDatum = CDate(strDatum)\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">Exit Function<\/span>\r\nFehler:\r\n    ParseDatum = Date\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b>Split<\/b> zerlegt die Eingabezeichenkette am Punkt in ein Array: Index 0 enth&auml;lt den Tag, Index 1 den Monat, Index 2 das Jahr. <b>DateSerial<\/b> erwartet diese drei Werte als separate Ganzzahlen &#8211; damit ist das Ergebnis vollst&auml;ndig unabh&auml;ngig von den regionalen Einstellungen des Systems. Im Fehlerfall gibt die Funktion das aktuelle Datum zur&uuml;ck. Der <b>Else<\/b>-Zweig mit <b>CDate<\/b> behandelt Eingaben ohne Punkte und &uuml;berl&auml;sst deren Interpretation VBA. <b>ParseDatum<\/b> wird in <b>GetFilterausdruck<\/b> &uuml;berall dort aufgerufen wo der Benutzer ein konkretes Datum eingegeben hat.<\/p>\n<h2>Das Fokus-Problem beim Ausblenden von Steuerelementen<\/h2>\n<p>Eine technische Besonderheit von Access sorgt beim Ausblenden von Steuerelementen regelm&auml;&szlig;ig f&uuml;r den Laufzeitfehler 2165 mit der Meldung <b>Sie k&ouml;nnen ein Steuerelement nicht ausblenden, solange es den Fokus hat<\/b>. Access erlaubt das Ausblenden eines Steuerelements schlicht nicht wenn dieses gerade den Eingabefokus h&auml;lt.<\/p>\n<p>Das ist eine harte Einschr&auml;nkung ohne Workaround &uuml;ber die Eigenschaften des Steuerelements selbst &#8211; der Fokus muss zwingend vorher auf ein anderes Steuerelement umgesetzt werden.<\/p>\n<p>Das Problem tritt an mehreren Stellen im Filterformular auf. Wenn der Benutzer auf die L&ouml;schen-Schaltfl&auml;che einer Filterzeile klickt, hat diese Schaltfl&auml;che durch den Klick den Fokus.<\/p>\n<p>Im Zuge der anschlie&szlig;enden Neuberechnung soll sie aber ausgeblendet werden &#8211; was Access verweigert. Dasselbe gilt f&uuml;r die Schaltfl&auml;che <b>ODER-Filtergruppe hinzuf&uuml;gen<\/b> wenn diese nach dem Klick aufgrund der ge&auml;nderten Situation ausgeblendet werden soll.<\/p>\n<p>Die L&ouml;sung ist die Hilfsprozedur <b>SetFocusSicher<\/b>, die vor dem Ausblenden den Fokus auf ein anderes Steuerelement umsetzt:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>SetFocusSicher( _\r\n        ParamArray arrNamen()<span style=\"color:blue;\"> As Variant<\/span>)\r\n    <span style=\"color:blue;\">Dim <\/span>i<span style=\"color:blue;\"> As Integer<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>ctl<span style=\"color:blue;\"> As <\/span>Control\r\n    For i = 0 To <span style=\"color:blue;\">UBound<\/span>(arrNamen)\r\n        On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n        <span style=\"color:blue;\">Set<\/span> ctl = Me(arrNamen(i))\r\n        <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n        <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> ctl Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n            <span style=\"color:blue;\">If <\/span>ctl.Visible And ctl.Enabled<span style=\"color:blue;\"> Then<\/span>\r\n                ctl.SetFocus\r\n                <span style=\"color:blue;\">Exit Sub<\/span>\r\n            <span style=\"color:blue;\">End If<\/span>\r\n        <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">Next<\/span> i\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Der Aufruf lautet etwa <b>SetFocusSicher &#8222;cmdAnwenden&#8220;, &#8222;cmdZuruecksetzen&#8220;<\/b>. Die Schaltfl&auml;chen im Formularfu&szlig; sind immer sichtbar und aktiviert und damit zuverl&auml;ssige Kandidaten. <b>ParamArray<\/b> erlaubt eine beliebige Anzahl von Kandidaten ohne explizite Array-Deklaration &#8211; der Aufrufer kann also je nach Situation unterschiedliche Kandidatenlisten angeben.<\/p>\n<p><b>SetFocusSicher<\/b> wird immer ganz am Anfang von <b>ZeileLeeren<\/b> aufgerufen &#8211; bevor irgendein Steuerelement ge&auml;ndert oder ausgeblendet wird.<\/p>\n<h2>Zeilen l&ouml;schen und Zustand wiederherstellen<\/h2>\n<p>Wenn der Benutzer eine Filterzeile l&ouml;scht, muss das Formular nicht nur diese Zeile ausblenden, sondern auch die verbleibenden Zeilen der Gruppe korrekt neu aufbauen.<\/p>\n<p>Das ist notwendig weil die Steuerelemente feste Indizes haben und beim L&ouml;schen einer mittleren Zeile keine L&uuml;cke in der Anzeige entstehen soll. <b>ZeileLeeren<\/b> geht so vor: Fokus umsetzen, alle Werte der betroffenen Zeile leeren, alle Zeilen der Gruppe ausblenden, dann alle Zeilen mit ausgef&uuml;lltem <b>cboFeld<\/b> von oben nach unten &uuml;ber <b>ZeileWiederherstellen<\/b> neu einblenden, schlie&szlig;lich eine leere Eingabezeile dahinter einblenden.<\/p>\n<p><b>ZeileWiederherstellen<\/b> macht dabei mehr als nur <b>Visible = True<\/b> zu setzen. Die Prozedur aktiviert <b>cboBedingung<\/b> wieder und blendet das passende Wertfeld ein &#8211; basierend auf dem gespeicherten Feldtyp und dem aktuellen Wert von <b>cboBedingung<\/b>.<\/p>\n<p>Das ist notwendig weil <b>ZeileSetzenSichtbar<\/b> beim Ausblenden <b>cboBedingung.Enabled = False<\/b> setzt und alle Wertfelder ausblendet. Ohne <b>ZeileWiederherstellen<\/b> w&auml;ren nach dem Neuaufbau zwar Feld und Bedingung sichtbar, aber das Wertfeld w&auml;re verschwunden und der eingegebene Filter f&uuml;r den Benutzer nicht mehr sichtbar und nicht mehr ver&auml;nderbar.<\/p>\n<p>Nach dem Neuaufbau pr&uuml;ft <b>ZeileLeeren<\/b> ob die Gruppe noch Eintr&auml;ge enth&auml;lt. Ist sie leer und ist es nicht Gruppe 1, wird auch der <b>ODER<\/b>-Trenner ausgeblendet und <b>m_intAktiveGruppen<\/b> neu berechnet. Zum Abschluss werden <b>AktualisiereFormularHoehe<\/b> und <b>AktualisiereSchaltflaechen<\/b> aufgerufen.<\/p>\n<h2>Zusammenfassung<\/h2>\n<p>Das Filterformular l&ouml;st eine Reihe von technischen Herausforderungen die beim Bau eines universell einsetzbaren dynamischen Filterformulars in Access auftreten.<\/p>\n<p>Die wichtigsten Erkenntnisse im &Uuml;berblick: Die DAO\/ADODB-Typkollision bei Wert <b>7 <\/b>erfordert ein explizites Flag das angibt welche Bibliothek verwendet wird &#8211; dieses Flag ist gleichzeitig die Vorbereitung f&uuml;r eine k&uuml;nftige ADODB-Erweiterung.<\/p>\n<p>Deutsches Datum muss &uuml;ber <b>DateSerial<\/b> statt &uuml;ber <b>CDate<\/b> geparst werden, um systemunabh&auml;ngig korrekte Ergebnisse zu liefern.<\/p>\n<p>Steuerelemente k&ouml;nnen nicht ausgeblendet werden, wenn sie den Fokus haben &#8211; eine Hilfsprozedur, die den Fokus vorab sicher umsetzt, l&ouml;st dieses Problem zuverl&auml;ssig.<\/p>\n<p>Die dynamische H&ouml;henanpassung funktioniert nur, wenn sie auf der tats&auml;chlichen Sichtbarkeit der Steuerelemente basiert, nicht auf Z&auml;hlvariablen. Das Stapeln aller Steuerelemente bei Y=0 erm&ouml;glicht die dynamische H&ouml;hensteuerung.<\/p>\n<p>Und beim Neuaufbau von Zeilen nach einem L&ouml;schvorgang gen&uuml;gt einfaches Einblenden nicht &#8211; der vollst&auml;ndige Zustand einer Zeile inklusive aktiviertem Bedingungsfeld und sichtbarem Wertfeld muss &uuml;ber eine dedizierte Wiederherstellungsprozedur rekonstruiert werden.<\/p>\n<h2>Downloads zu diesem Beitrag<\/h2>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>FilterformularFuerAlleFaelleDieProgrammierung.accdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/CB11197D-1CF7-4B9F-B20B-04BD6C84C816\/aiu_1604.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Im ersten Teil dieser Beitragsreihe haben wir gezeigt, wie das Filterformular in eine eigene Datenbank eingebaut und bedient wird. In diesem zweiten Teil werfen wir einen Blick hinter die Kulissen: Wie ist das Filterformular aufgebaut, wie erkennt es den Feldtyp aus dem Recordset, wie baut es den SQL-WHERE-String zusammen, wie funktioniert die dynamische H&ouml;henanpassung &#8211; und welche technischen Besonderheiten mussten gel&ouml;st werden. Einige dieser L&ouml;sungen sind nicht auf den ersten Blick offensichtlich, liefern aber wertvolle Denkanst&ouml;&szlig;e f&uuml;r eigene Projekte.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[662026,66032026,44000052],"tags":[],"class_list":["post-55001604","post","type-post","status-publish","format-standard","hentry","category-662026","category-66032026","category-Formulare_und_Steuerelemente"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.9 (Yoast SEO v27.5) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Ein Filterformular f&uuml;r alle F&auml;lle: Die Programmierung - Access im Unternehmen<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/access-im-unternehmen.de\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Ein Filterformular f&uuml;r alle F&auml;lle: Die Programmierung\" \/>\n<meta property=\"og:description\" content=\"Im ersten Teil dieser Beitragsreihe haben wir gezeigt, wie das Filterformular in eine eigene Datenbank eingebaut und bedient wird. In diesem zweiten Teil werfen wir einen Blick hinter die Kulissen: Wie ist das Filterformular aufgebaut, wie erkennt es den Feldtyp aus dem Recordset, wie baut es den SQL-WHERE-String zusammen, wie funktioniert die dynamische H&ouml;henanpassung - und welche technischen Besonderheiten mussten gel&ouml;st werden. Einige dieser L&ouml;sungen sind nicht auf den ersten Blick offensichtlich, liefern aber wertvolle Denkanst&ouml;&szlig;e f&uuml;r eigene Projekte.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2026-05-06T11:11:33+00:00\" \/>\n<meta name=\"author\" content=\"Andr\u00e9 Minhorst\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andr\u00e9 Minhorst\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"22\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Ein Filterformular f&uuml;r alle F&auml;lle: Die Programmierung\",\"datePublished\":\"2026-05-06T11:11:33+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\\\/\"},\"wordCount\":3700,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"articleSection\":[\"2026\",\"3\\\/2026\",\"Formulare und Steuerelemente\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\\\/\",\"name\":\"Ein Filterformular f&uuml;r alle F&auml;lle: Die Programmierung - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"datePublished\":\"2026-05-06T11:11:33+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Ein Filterformular f&uuml;r alle F&auml;lle: Die Programmierung\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"name\":\"Access im Unternehmen\",\"description\":\"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access\",\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/access-im-unternehmen.de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\",\"name\":\"Andr\u00e9 Minhorst Verlag\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"contentUrl\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"width\":370,\"height\":111,\"caption\":\"Andr\u00e9 Minhorst Verlag\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\",\"name\":\"Andr\u00e9 Minhorst\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"caption\":\"Andr\u00e9 Minhorst\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Ein Filterformular f&uuml;r alle F&auml;lle: Die Programmierung - Access im Unternehmen","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/access-im-unternehmen.de\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\/","og_locale":"de_DE","og_type":"article","og_title":"Ein Filterformular f&uuml;r alle F&auml;lle: Die Programmierung","og_description":"Im ersten Teil dieser Beitragsreihe haben wir gezeigt, wie das Filterformular in eine eigene Datenbank eingebaut und bedient wird. In diesem zweiten Teil werfen wir einen Blick hinter die Kulissen: Wie ist das Filterformular aufgebaut, wie erkennt es den Feldtyp aus dem Recordset, wie baut es den SQL-WHERE-String zusammen, wie funktioniert die dynamische H&ouml;henanpassung - und welche technischen Besonderheiten mussten gel&ouml;st werden. Einige dieser L&ouml;sungen sind nicht auf den ersten Blick offensichtlich, liefern aber wertvolle Denkanst&ouml;&szlig;e f&uuml;r eigene Projekte.","og_url":"https:\/\/access-im-unternehmen.de\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\/","og_site_name":"Access im Unternehmen","article_published_time":"2026-05-06T11:11:33+00:00","author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"22\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Ein Filterformular f&uuml;r alle F&auml;lle: Die Programmierung","datePublished":"2026-05-06T11:11:33+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\/"},"wordCount":3700,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"articleSection":["2026","3\/2026","Formulare und Steuerelemente"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\/","url":"https:\/\/access-im-unternehmen.de\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\/","name":"Ein Filterformular f&uuml;r alle F&auml;lle: Die Programmierung - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"datePublished":"2026-05-06T11:11:33+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Ein_Filterformular_fuer_alle_Faelle_Die_Programmierung\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Ein Filterformular f&uuml;r alle F&auml;lle: Die Programmierung"}]},{"@type":"WebSite","@id":"https:\/\/access-im-unternehmen.de\/#website","url":"https:\/\/access-im-unternehmen.de\/","name":"Access im Unternehmen","description":"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access","publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/access-im-unternehmen.de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/access-im-unternehmen.de\/#organization","name":"Andr\u00e9 Minhorst Verlag","url":"https:\/\/access-im-unternehmen.de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/","url":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","contentUrl":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","width":370,"height":111,"caption":"Andr\u00e9 Minhorst Verlag"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f","name":"Andr\u00e9 Minhorst","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","caption":"Andr\u00e9 Minhorst"}}]}},"_links":{"self":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001604","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/comments?post=55001604"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001604\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001604"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001604"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001604"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}