1:n-Daten mit HTML

Es gibt einige Darstellungen, die sich mit Access-Bordmitteln nicht oder nur mit sehr hohem Aufwand realisieren lassen. Dazu gehören beispielsweise alle Konstellationen aus 1:n-Beziehungen mit Unterformularen, bei denen man gern mehr als einen Datensatz im Hauptformular anzeigen würde. Zum Glück erlaubt Access einen Blick über den Tellerrand, und mit dem Webbrowser-Steuerelement und HTML erreichen Sie wohl alle denkbaren Darstellungen – in diesem Fall die Endlosanzeige von 1:n-Beziehungen.

Im vorliegenden Fall sollen alle Abteilungen einer Firma samt den dazugehörigen Mitarbeitern dargestellt werden, und zwar inklusive eines Kontrollkästchens je Abteilung und Mitarbeiter, mit dem man komplette Abteilungen oder einzelne Mitarbeiter aus- und abwählen kann. Das Beispielformular aus Bild 1 gehört zu der Lösung aus dem Beitrag Fehlzeiten verwalten (www.access-im-unternehmen.de/850).

pic001.png

Bild 1: 1:n-Beziehung im Webbrowser-Steuerelement

Das Formular soll die Daten der beiden Tabellen tblAbteilungen und tblMitarbeiter anzeigen, die per 1:n-Beziehung über das Fremdschlüsselfeld AbteilungID der Tabelle tblMitarbeiter miteinander verknüpft sind (s. Bild 3 aussieht. Das Webbrowser-Steuerelement erhält den Namen ctlWebbrowser.

pic237.png

Bild 2: Tabellen der Beispielanwendung

pic002.png

Bild 3: Das Beispielformular in der Entwurfsansicht

Um ein HTML-Dokument mit den Methoden der MSHTML-Bibliothek zu bestücken, benötigen Sie zunächst einen Verweis auf diese Bibliothek. Diesen legen Sie im Verweise-Dialog (Menüeintrag Optionen|Verweise im VBA-Editor) fest (s. Bild 4). Danach können Sie bereits loslegen. Die Prozedur, die durch das Ereignis Beim Laden des Formulars ausgelöst wird, ruft wiederum die Prozedur MitarbeiterAnzeigen auf:

pic003.png

Bild 4: Verweis auf die MSHTML-Bibliothek

Private Sub Form_Load()
    MitarbeiterAnzeigen
End Sub

Der weitere Aufbau wird von der Prozedur MitarbeiterAnzeigen gesteuert (s. Listing 1). Die Prozedur legt zunächst mit der Funktion DokumentAnlegen (s. Listing 2) ein Dokument im Webbrowser-Steuerelement an und speichert einen Verweis darauf in der Variablen objDocument.

Listing 1: Mitarbeiter im Webbrowser-Steuerelement anzeigen

Private Sub MitarbeiterAnzeigen()
    Dim db As DAO.Database
    Dim rstAbteilungen As DAO.Recordset
    Dim rstMitarbeiter As DAO.Recordset
    Dim objDocument As MSHTML.HTMLDocument
    Dim objTable As MSHTML.HTMLTable
    Dim objInnerRow As MSHTML.HTMLTableRow
    Dim objInnerCell As MSHTML.HTMLTableCell
    Dim objCheckbox As MSHTML.HTMLInputElement
    Set colCheckboxes_Mitarbeiter = New Collection
    Set colCheckboxes_Abteilungen = New Collection
    Set objDocument = DokumentHolen
    Set objTable = objDocument.createElement("Table")
    objDocument.Body.appendChild objTable
    objDocument.Body.Style.fontFamily = "calibri"
    objDocument.Body.Style.FontSize = "8px"
    objTable.Style.borderCollapse = "collapse"
    Set db = CurrentDb
    Set rstAbteilungen = db.OpenRecordset("SELECT * FROM qryAbteilungenMitMitarbeitern", dbOpenDynaset)
    Do While Not rstAbteilungen.EOF
        Set objInnerRow = ZeileZelleTabelleAnlegen(objDocument, objTable)
        Set objInnerCell = objInnerRow.insertCell
        objInnerCell.appendChild CheckboxAbteilungErstellen(objDocument, rstAbteilungen!AbteilungID)
        Set objInnerCell = objInnerRow.insertCell
        objInnerCell.innerText = rstAbteilungen!Abteilung
        Set rstMitarbeiter = db.OpenRecordset("SELECT * FROM qryMitarbeiterauswahl " _
            & "WHERE AbteilungID " & rstAbteilungen!AbteilungID, dbOpenDynaset)
        Do While Not rstMitarbeiter.EOF
            Set objInnerRow = ZeileZelleTabelleAnlegen(objDocument, objTable)
            Set objInnerCell = objInnerRow.insertCell
            objInnerCell.Width = "30px"
            Set objInnerCell = objInnerRow.insertCell
            Set objCheckbox = CheckboxMitarbeiterErstellen(objDocument, _
                rstMitarbeiter!MitarbeiterID)
            objInnerCell.appendChild objCheckbox
            objCheckbox.Checked = Not IsNull(rstMitarbeiter!MitarbeiterAnzeigenID)
            Set objInnerCell = objInnerRow.insertCell
            With objInnerCell
                .innerText = rstMitarbeiter!Bezeichnung
                .Style.fontFamily = "calibri"
                .Style.FontSize = "12px"
            End With
            rstMitarbeiter.MoveNext
        Loop
        rstAbteilungen.MoveNext
    Loop
    Set db = Nothing
End Sub

Listing 2: Browser mit leerem Dokument füllen

Private Function DokumentHolen() As MSHTML.HTMLDocument
    Dim objWebbrowser As SHDocVw.WebBrowser
    Dim objDocument As MSHTML.HTMLDocument
    Set objWebbrowser = GetWebbrowser
    DoEvents
    objWebbrowser.Navigate "about:blank"
    Do
        DoEvents
    Loop Until objWebbrowser.ReadyState = READYSTATE_COMPLETE
    Set objDocument = objWebbrowser.Document
    Set DokumentHolen = objDocument
End Function

Außerdem erstellt die Prozedur zwei Collection-Objekte, welche später Verweise auf Wrapper-Klassen für die Checkbox-Steuerelemente zum Auswählen der Abteilungen und der Mitarbeiter aufnehmen – dazu später mehr. Wichtig ist erstmal, dass die Collection-Elemente wie folgt im Kopf des Klassenmoduls des Formulars deklariert werden:

Dim colCheckboxes_Mitarbeiter As Collection
Dim colCheckboxes_Abteilungen As Collection

Auch die Wrapper-Klassen werden im Kopf des Moduls deklariert:

Dim objCheckboxWrapper_Mitarbeiter As clsCheckboxWrapper_Mitarbeiter
Dim objCheckboxWrapper_Abteilung As clsCheckboxWrapper_Abteilung

Nun geht erstmal der Aufbau der Tabellenstruktur im HTML-Dokument weiter. Die Prozedur erstellt ein HTMLTable-Objekt und hängt es als Kind-Element an das HTMLDocument-Element an. Einige Einstellungen bezüglich der Schriftart sorgen später für ein einheitliches Bild.

Danach kommt auch schon die Datenbank mit ihren Tabellen tblAbteilungen und tblMitarbeiter ins Spiel. Genau genommen öffnet die Prozedur zunächst eine Datensatzgruppe auf Basis der Abfrage qryAbteilungenMitMitarbeitern (s. Bild 5). Diese liefert alle Datensätze der Tabelle tblAbteilungen, denen mindestens ein Mitarbeiter angehört. Diese Abteilungen werden in einer Do While-Schleife durchlaufen. Dabei erstellt die Prozedur MitarbeiterAnzeigen zunächst innerhalb einer Funktion namens ZeileZelleTabelleAnlegen einige Elemente im HTMLTable-Objekt, und zwar zunächst ein HTMLRow-Element, darin ein HTMLCell-Element, darin ein HTMLTable-Element und schließlich noch ein HTMLRow-Element (s. Listing 3).

Listing 3: Unabhängige Zeilen anlegen

Public Function ZeileZelleTabelleAnlegen(objDocument As MSHTML.HTMLDocument, _
        objTable As MSHTML.HTMLTable) As MSHTML.HTMLTableRow
    Dim objRow As MSHTML.HTMLTableRow
    Dim objCell As MSHTML.HTMLTableCell
    Dim objInnerTable As MSHTML.HTMLTable
    Dim objInnerRow As MSHTML.HTMLTableRow
    Set objRow = objTable.insertRow
    objRow.Style.verticalAlign = "top"
    Set objCell = objRow.insertCell
    Set objInnerTable = objDocument.createElement("Table")
    objInnerTable.Style.borderCollapse = "collapse"
    objCell.appendChild objInnerTable
    Set objInnerRow = objInnerTable.insertRow
    Set ZeileZelleTabelleAnlegen = objInnerRow
End Function

pic005.png

Bild 5: Datenherkunft für die äußere Schleife

Wozu das Ganze – würde denn nicht eine einfache Zeile reichen Eigentlich ja, wenn wir alle Zellen-Elemente später genau untereinander anordnen wollten. Die Mitarbeiter-Elemente sollen jedoch etwas eingerückt sein, weshalb wir in einer in einer Zelle eingebetteten Tabelle eine weitere Zeile anlegen. Und da wir später das Gleiche für die Mitarbeiter-Zeilen erledigen, können wir darin nach Lust und Laune Tabellenzellen unterbringen, ohne dass deren Positionen vom Raster anderer Zeilen abhängen.

Checkbox für Abteilung einfügen

In diese neue Zeile, die mit objInnerRow referenziert wird, fügt die Prozedur nun zwei Zellen ein: eine für die Checkbox und eine für die Bezeichnung der Abteilung. Die erste Zelle wird durch die Funktion CheckboxAbteilungErstellen gefüllt (s. Listing 4). Diese Prozedur erwartet das HTMLDocument-Element und den Wert des Feldes AbteilungID der Abteilung als Parameter. Damit ausgestattet legt sie zunächst ein neues Checkbox-Element an. Danach folgt direkt die Erstellung einer Instanz der Klasse clsCheckboxWrapper_Abteilung, welche die Funktionen enthält, die beim Anklicken der Checkbox ausgelöst werden. Die Eigenschaft Checkbox der Wrapper-Klasse wird mit einem Verweis auf das Checkbox-Steuerelement gefüllt. Außerdem erhält die Eigenschaft AbteilungID den entsprechenden Wert der aktuell durchlaufenen Abteilung. Schließlich fügt die Prozedur der Eigenschaft Mitarbeiter einen Verweis auf die Collection colCheckboxes_Mitarbeiter hinzu. Dieser wird später benötigt, um beim Aktivieren oder Deaktivieren der kompletten Abteilung alle enthaltenen Mitarbeiter aus- oder abzuwählen.

Listing 4: Hinzufügen einer Checkbox zum Aktivieren einer Abteilung

Public Function CheckboxAbteilungErstellen(objDocument As MSHTML.HTMLDocument, _
        lngAbteilungID As Long) As MSHTML.HTMLInputElement
    Dim objCheckbox As MSHTML.HTMLInputElement
    Set objCheckbox = objDocument.createElement("input")
    objCheckbox.Type = "Checkbox"
    Set objCheckboxWrapper_Abteilung = New clsCheckboxWrapper_Abteilung
    With objCheckboxWrapper_Abteilung
        Set .Checkbox = objCheckbox
        .AbteilungID = lngAbteilungID
        Set .Mitarbeiter = colCheckboxes_Mitarbeiter
    End With
    colCheckboxes_Abteilungen.Add objCheckboxWrapper_Abteilung, "a" & lngAbteilungID
    Set CheckboxAbteilungErstellen = objCheckbox
End Function

Schließlich wird die Wrapper-Klasse der Collection zum Aufbewahren der Abteilungs-Wrapper-Klassen hinzugefügt, damit diese auch nach dem Beenden dieser Prozedur noch existiert.

Mitarbeiter-Checkboxen hinzufügen

Zurück zur Prozedur MitarbeiterAnzeigen. Diese legt nach der Checkbox für die Abteilungen auch noch eine weitere Zelle an, die den Namen der jeweiligen Abteilung aufnimmt. Danach folgen die Mitarbeiter: Ein weiteres Recordset namens rstMitarbeiter wird mit allen Datensätzen der Abfrage qryMitarbeiterauswahl gefüllt, deren AbteilungID mit der aktuellen AbteilungID der äußeren Do While-Schleife übereinstimmt (s. Bild 7). Hier tritt erstmals die Tabelle tblMitarbeiterAusgewaehlt in Aktion: Diese enthält zwei Felder, nämlich ein Primärschlüsselfeld namens MitarbeiterAusgewaehltID und ein Feld zur Aufnahme der MitarbeiterID aller aktuell ausgewählten Mitarbeiter. Wird ein Mitarbeiter im Formular per Checkbox ausgewählt, wird seine MitarbeiterID zu einem neuen Datensatz der Tabelle tblMitarbeiterAusgewaehlt hinzugefügt, wird er abgewählt, wird der Datensatz mit seiner MitarbeiterID gelöscht. Somit kann die Auswahl erfolgen, ohne dass dazu ein zusätzliches Feld in der Tabelle tblMitarbeiter angelegt werden muss.

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