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

Achtung: Dies ist nicht der vollständige Artikel, sondern nur ein paar Seiten davon. Wenn Sie hier nicht erfahren, was Sie wissen möchten, finden Sie am Ende Informationen darüber, wie Sie den ganzen Artikel lesen können.

Kompletten Artikel lesen?

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

E-Mail:

Gedrucktes Heft

Diesen Beitrag finden Sie in Ausgabe 4/2012.

Unser Angebot für Sie!

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

Diesen Beitrag twittern

Zusammenfassung

Erfahren Sie am Beispiel von Fehlzeiten, wie sich Daten in Form von HTML-Tabellen darstellen und steuern lassen.

Techniken

Formulare, VBA, HTML

Voraussetzungen

Access 2000 und höher

Beispieldateien

Fehlzeitenverwaltung.mdb

Shortlink

www.access-im-unternehmen.de/850

Fehlzeiten verwalten

André Minhorst, Duisburg

Das Verwalten von Fehlzeiten bezieht sich auf Urlaub, Krankheit, Fortbildung et cetera und erfordert keine Wunderwerke der Datenmodellierung. Eine Tabelle mit Mitarbeitern, eine mit den Fehlzeiten - mehr benötigen Sie nicht. Interessant wird es jedoch, die Fehlzeiten einzugeben und diese übersichtlich darzustellen. Und zwar so, dass nicht nur die Fehlzeiten eines einzigen Mitarbeiters, sondern möglichst die aller Mitarbeiter sichtbar sind. Nur so lässt sich effektiv vorausplanen.

Die Musterlösung dieses Beitrags soll es erlauben, mehrere Mitarbeiter zu verwalten und für jeden Tag Fehlzeiten in Form halber Tage festzuhalten - es kann ja durchaus geschehen, dass ein Mitarbeiter mal einen halben Tag Urlaub nimmt, an einer Fortbildung teilnimmt oder sich nach dem Mittagessen mit einer Magenverstimmung verabschiedet.

Datenmodell

Das Datenmodell besteht aus einer einfachen Mitarbeitertabelle und einer Tabelle zur Verwaltung von Abteilungen. Außerdem gibt es eine Tabelle zum Speichern der verschiedenen Arten von Fehlzeiten und eine zum Aufnehmen der Fehlzeiten selbst.

Die Tabelle tblMitarbeiter ist recht einfach gehalten und enthält neben dem Primärschlüsselfeld nur zwei Felder zur Angabe von Vor- und Nachname, ein Feld zum Eintragen einer Bezeichnung, die in der Regel die Form <Nachname>, <Vorname> aufweist, und ein Nachschlagefeld zur Auswahl der Abteilung des Mitarbeiters (s. Abb. 1). Die damit referenzierte Tabelle tblAbteilung ist eine schlichte Lookup-Tabelle, die nur das Primärschlüsselfeld AbteilungID sowie das Feld mit der Bezeichnung der Abteilung enthält.

pic004.png

Abb. 3: Fehlzeiten werden jeweils als halbe Tage erfasst

Gleiches gilt für die Tabelle tblFehlzeitarten, die nur aus den beiden Feldern FehlzeitartID und Fehlzeitart besteht. Sie nimmt in der aktuellen Version die drei Werte Krankheit, Urlaub und Weiterbildung auf (s. Abb. 2).

pic001.png

Abb. 1: Die Tabelle tblMitarbeiter in der Entwurfsansicht

Fehlt noch die Tabelle tblFehlzeiten, welche die tatsächlichen Fehlzeiten aufnimmt. Sie enthält ein Primärschlüsselfeld namens FehlzeitID und ein Datumsfeld namens Fehlzeitdatum. Weitere zwei Felder sind Fremdschlüsselfelder und referenzieren die Datensätze der Tabellen tblMitarbeiter und tblFehlzeitarten.

Fehlt noch eine wichtige Information, die davon abhängt, ob Sie nur ganze Fehltage aufnehmen möchten oder auch halbe Fehltage (man könnte dies natürlich auch stundenweise erledigen, aber wenn jemand zwei Stunden frei haben möchte, um mit seinem Kind zum Arzt zu gehen, hängt er die zwei Stunden halt irgendwo hinten an).

Wie aber bekommen wir die halben Fehltage in den Griff? Bei einem ganzen Fehltag wäre dies kein Problem: Sie legen einen Eintrag in der Tabelle tblFehltage an und der Fall ist erledigt. Bei halben Tagen brauchen wir aber zumindest noch ein weiteres Feld, mit dem wir die Fehlzeitdauer aufnehmen. Aber macht es Sinn, tatsächlich nur die Dauer, also einen Wert wie 1 oder 1/2 zu speichern? Vielleicht brauchen wir noch weitere Informationen, zum Beispiel, ob der halbe Tag vormittags oder nachmittags beziehungsweise die erste oder die zweite Hälfte der Schicht betrifft?

Dies wird nämlich beispielsweise interessant, wenn immer eine bestimmte Anzahl Mitarbeiter gebraucht wird. Und auch für eine grafische Darstellung der Fehlzeiten wäre es sinnvoll, die Position des halben Fehltages anzeigen zu können. Welche Möglichkeiten haben wir nun?

  • Sie könnten ein Fremdschlüsselfeld und eine entsprechende Lookup-Tabelle anlegen, welche die beiden Werte Vormittags und Nachmittags enthält. Dann brauchen Sie aber auch noch einen Wert für Kompletter Tag.
  • Sie könnten zwei Ja/Nein-Felder namens Vormittag und Nachmittag anlegen und entweder eines von beiden oder, im Falle eines kompletten Fehltages, beide Felder auf Ja einstellen.
  • Sie legen grundsätzlich einen neuen Datensatz pro halben Fehltag in der Tabelle an und verwenden dabei nur ein Feld namens Vormittags, um den Zeitraum festzulegen. Ist Vormittags aktiviert, liegt der halbe Fehltag in der ersten Tageshälfte, sonst in der zweiten Tageshälfte. Für einen kompletten Fehltag legen Sie also zwei Datensätze an, bei denen das Feld Vormittags einmal aktiviert ist und einmal nicht.

Wir entscheiden uns für die letzte Variante. Die Tabelle tblFehlzeiten sieht somit wie in Abb. 3 aus. Das gesamte Datenmodell umfasst nun vier Tabellen (s. Abb. 4).

pic002.png

Abb. 2: Tabelle zum Speichern der Fehlzeitarten

pic005.png

Abb. 4: Datenmodell der Musterlösung

Darstellung der Fehlzeiten im Formular

Für die Darstellung der Fehlzeiten gibt es mehrere Möglichkeiten. Grundsätzlich sollen die Spaltenköpfe die Datumsangaben enthalten und die Zeilenköpfe die Namen der Mitarbeiter. Die einzelnen Zellen erhalten dann eine die jeweilige Fehlzeitart repräsentierende Farbe (diese legen Sie übrigens in der Tabelle tblFehlzeitarten fest - später lernen Sie ein Formular zur Bearbeitung der Fehlzeitarten kennen). Aber da wir uns nun schon darauf eingeschossen haben, auch halbtägige Fehlzeiten zu vergeben: Wie stellen wir diese in einer Übersicht wie oben beschrieben dar? Eigentlich gibt es nur zwei Möglichkeiten: entweder die Fehlzeit am Vormittag landet oben und die am Nachmittag unten oder die Fehlzeiten werden links und rechts im Kasten für den jeweiligen Tag angezeigt. Für ganztägige Fehlzeiten wird der komplette Kasten mit der entsprechenden Farbe gefüllt.

Abb. 5 zeigt, wie der zweite Vorschlag aussieht. Dieser ist die bessere Lösung, weil man die Fehlzeiten für den Vor- und den Nachmittag durch Prüfen einer einzigen Spalte überblicken kann. Diese Ansicht lässt sich mit Bordmitteln nicht realisieren. Zwar könnten Sie die Abwesenheiten aller Mitarbeiter untereinander mit einer ganzen Menge Steuerelemente und in der Endlosansicht anzeigen, aber wenn Sie wie in diesem Beispiel auch noch nach einer übergeordneten Kategorie wie etwa Abteilungen gruppieren möchten, haben Sie keine Chance. Daher stellen wir einmal dar, wie dies mit dem Webbrowser-Steuerelement und HTML funktioniert.

pic007.png

Abb. 5: Formular zum Hinzufügen von Fehlzeiten

Wie werden die Fehlzeiten hinzugefügt oder entfernt? Dazu enthält jedes einzelne Element in der Fehlzeitenübersicht ein Kontextmenü (s. Abb. 6). Klicken Sie mit der rechten Maustaste auf den Tag für den entsprechenden Mitarbeiter und wählen Sie die gewünschte Fehlzeitart samt Dauer aus.

pic009.png

Abb. 6: Fehlzeit per Kontextmenü

Formular anlegen

Das Formular sieht in der Entwurfsansicht wie in Abb. 7 aus und enthält zunächst ein Webbrowser-Steuerelement. Dieses fügen Sie in den Access-Versionen bis Access 2007 als ActiveX-Steuerelement hinzu, ab Access 2010 verwenden Sie das eingebaute Webbrowser-Steuerelement.

pic008.png

Abb. 7: Das Formular zur Anzeige der Fehlzeitenübersicht in der Entwurfsansicht

Das Webbrowser-Steuerelement soll den Namen ctlWebbrowser erhalten.

Im Kopfbereich des Formulars finden Sie folgende Steuerelemente:

  • txtStart: Textfeld zur Anzeige beziehungsweise Eingabe eines Tages, der in der ersten Woche der vierwöchigen Anzeige liegt
  • cmdNaechsteWoche: Schaltfläche, welche die Anzeige um eine Woche in die Zukunft verschiebt
  • cmdVorherigeWoche: Schaltfläche, welche die Anzeige um eine Woche in die Vergangenheit verschiebt
  • cmdMitarbeiterFiltern: Öffnet einen Dialog, mit dem Sie einstellen können, für welche Mitarbeiter die Fehlzeiten angezeigt werden sollen
  • cmdFuellen: Aktualisiert die Anzeige
  • cmdFehlzeitarten: Öffnet einen Dialog zum Bearbeiten der Fehlzeitarten

Füllen des Webbrowser-Steuerelements

Die aufwendigste Aufgabe dieser Beispielanwendung ist natürlich das Füllen des Webbrowser-Steuerelements. Dies erledigt die Prozedur FehlzeitenAnzeigen - mehr dazu weiter unten.

Diese Prozedur wird zu verschiedenen Gelegenheiten aufgerufen, als Erstes natürlich beim Anzeigen des Formulars - hier in der Prozedur, die durch das Ereignis Beim Laden ausgelöst wird:

Private Sub Form_Load()

    Dim datMontag As Date

    datMontag = MontagDerWoche(Date)

    Me!txtStart = datMontag

    FehlzeitenAnzeigen

End Sub

Diese Prozedur leistet allerdings noch ein wenig Vorarbeit: Sie ermittelt mit der Date-Funktion das aktuelle Datum und daraus mit der Funktion MontagDerWoche den Montag der Woche, in der das Datum liegt:

Public Function MontagDerWoche(dat As Date) As Date

    Dim intWeekday As Integer

    intWeekday = Weekday(dat, vbMonday)

    MontagDerWoche = DateAdd("d", -(intWeekday - 1), dat)

End Function

MontagDerWoche holt sich mit der Weekday-Funktion eine Zahl, welche den Tag der Woche repräsentiert, also beispielsweise den Wert 3, wenn der Tag von Date ein Mittwoch ist (Montag entspricht 1). Um den Montag der Woche zu ermitteln, in der sich das aktuelle Datum befindet, muss man nur noch die Differenz der Tage vom Montag bis zum aktuellen Tag ermitteln und vom aktuellen Tag abziehen.

Dieses Datum trägt Form_Load in das Textfeld txtStart ein und ruft dann die Prozedur FehlzeitenAnzeigen auf. Die Prozedur wird auch aufgerufen, wenn der Benutzer eine der beiden Schaltflächen cmdNaechsteWoche oder cmdVorherigeWoche anklickt. Die dadurch ausgelösten Prozeduren fügen entweder sieben Tage zum Datum im Textfeld txtStart hinzu oder ziehen diese ab, um den Startzeitpunkt der Fehlzeitenanzeige um eine Woche nach vorn oder hinten zu verschieben:

Private Sub cmdNaechsteWoche_Click()

    Me!txtStart = Me!txtStart + 7

    FehlzeitenAnzeigen

End Sub

Private Sub cmdVorherigeWoche_Click()

    Me!txtStart = Me!txtStart - 7

    FehlzeitenAnzeigen

End Sub

Das Anklicken der Schaltfläche cmdFuellen dient allein der Aktualisierung der Anzeige:

Private Sub cmdFuellen_Click()

    FehlzeitenAnzeigen

End Sub

Außerdem wird die Anzeige noch nach dem Bearbeiten der anzuzeigenden Mitarbeiter und der Fehlzeitarten aktualisiert - mehr dazu weiter unten.

Die Prozedur FehlzeitenAnzeigen

Diese Prozedur, die Sie im Klassenmodul des Formulars frmFehlzeiten finden (und in Listing 1), bildet das Grundgerüst zum Füllen der Fehlzeiten-Übersicht. Einige weitere Prozeduren übernehmen die Kleinarbeit wie das Anlegen und Ausstatten der einzelnen Tabellenelemente wie Zeilen, Zellen, in Zellen enthaltene Tabellen und so weiter.

Listing 1: Füllen des Webbrowser-Steuerelements

Private Sub FehlzeitenAnzeigen()

    Dim objDocument As MSHTML.HTMLDocument

    Dim db As DAO.Database

    Dim rstMitarbeiter As DAO.Recordset

    Dim rstAbteilungen As DAO.Recordset

    Dim datAktuell As Date

    Dim objTable As MSHTML.HTMLTable

    Dim objRow As MSHTML.HTMLTableRow

    Set colTableCells = New Collection

    Set db = CurrentDb

    Set rstAbteilungen = db.OpenRecordset("SELECT * FROM qryAbteilungenMitMitarbeitern", dbOpenDynaset)

    Set objDocument = DokumentHolen

    Set objTable = TabelleErstellen(objDocument)

    Set objRow = objTable.insertRow

    objRow.insertCell

    objRow.insertCell

    For datAktuell = Me!txtStart To DateAdd("d", 27, Me!txtStart)

        ZelleTagAnlegen datAktuell, objRow

    Next datAktuell

    Do While Not rstAbteilungen.EOF

        ZeileAbteilungEinfuegen objTable, rstAbteilungen!Abteilung

        Set rstMitarbeiter = db.OpenRecordset("SELECT * FROM qryAusgewaehlteMitarbeiter " _

            & "WHERE AbteilungID = " & rstAbteilungen!AbteilungID, dbOpenDynaset)

        Do While Not rstMitarbeiter.EOF

            Set objRow = ZeileMitarbeiterEinfuegen(objTable)

            ZeilenkopfMitarbeiterEinfuegen objRow, rstMitarbeiter!Bezeichnung

            For datAktuell = Me!txtStart To DateAdd("d", 27, Me!txtStart)

                Set objTableCell = ZelleTagMitarbeiterHinzufuegen(datAktuell, objRow, _

                    rstMitarbeiter!MitarbeiterID, objDocument)

                colTableCells.Add objTableCell, CLng(rstMitarbeiter!MitarbeiterID) & "|" _

                    & CLng(datAktuell)

            Next datAktuell

            rstMitarbeiter.MoveNext

        Loop

        rstAbteilungen.MoveNext

    Loop

    FehlzeitenEintragen Me!txtStart, DateAdd("d", 27, Me!txtStart)

End Sub

Im Deklarationsteil finden Sie unter anderem zwei Recordset-Objekte, mit denen erst die Abteilungen und dann die Mitarbeiter der Abteilungen durchlaufen werden, und außerdem einige HTML-Objekte.

Danach folgt direkt die Instanzierung eines Collection-Objekts, das im Kopf des Klassenmoduls des Formulars deklariert wird:

Dim colTableCells As Collection

Wozu das? Jeder Tag soll ja zwei Zellen enthalten, die nach einem Mausklick ein Kontextmenü anzeigen. Dies gelingt nur, wenn Sie eine entsprechende Ereignisprozedur für die entsprechenden HTMLTableCell-Elemente anlegen. Nun wäre es aber ein wahnsinniger Aufwand, vorab für viele hundert HTMLTableCell-Elemente Ereignisprozeduren anzulegen.

Daher verwenden wir eine kleine Wrapper-Klasse, der wir einen Bezug auf das jeweilige HTMLTableCell-Element mitgeben. Diese Wrapper-Klasse enthält die für die Anzeige des Kontextmenüs nötigen Ereignisprozeduren. Vorteil: Sie brauchen die Klasse nur einmal zu programmieren und können sie für jedes der Kalender-Elemente, das ein Kontextmenü anzeigen soll, neu instanzieren.

Wie das genau funktioniert, erfahren Sie weiter unten. An dieser Stelle ist erstmal wichtig, dass es eine Collection gibt, welche Verweise auf alle Instanzen der Wrapper-Klasse aufnimmt, damit diese nach dem Erzeugen nicht im Nirwana verschwinden.

Abteilungen durchlaufen

Das Recordset rstAbteilungen basiert auf einer Abfrage, die nicht alle Abteilungen der Tabelle tblAbteilungen zurückliefert, sondern nur diejenigen, die mindestens einen Mitarbeiter enthalten. Die dabei verwendete Abfrage qryAbteilungenMitMitarbeitern sieht wie in Abb. 8 aus. Die Abfrage gibt zwar nur Felder der Tabelle tblAbteilungen zurück, aber dadurch, dass diese in der Abfrage mit der Tabelle tblMitarbeiter verknüpft ist, werden die Datensätze ohne Bezug zu einem Mitarbeiter-Datensatz ignoriert.

pic010.png

Abb. 8: Abfrage zur Ermittlung aller Abteilungen mit mindestens einem Mitarbeiter

Danach liefert die Funktion DokumentHolen einen Verweis auf ein HTML-Dokument, das innerhalb dieser Funktion erstellt wird:

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

Die Funktion verwendet zunächst eine weitere Funktion namens GetWebbrowser, um eine Referenz auf das im Formular enthaltene Webbrowser-Steuerelement zu erhalten. Diese wird nach dem Erstellen in der modulweit deklarierten Variablen gespeichert:

Dim WithEvents m_Webbrowser As WebBrowser

Die Funktion GetWebbrowser prüft, ob m_Webbrowser schon gefüllt ist, und holt dies gegebenenfalls nach. In jedem Fall gibt GetWebbrowser einen Verweis auf dieses Steuerelement zurück:

Private Function GetWebbrowser() As SHDocVw.WebBrowser

    If m_Webbrowser Is Nothing Then

        Set m_Webbrowser = _

        Me!ctlWebbrowser.Object

    End If

    Set GetWebbrowser = m_Webbrowser

End Function

Die Funktion DokumentHolen lädt dann zunächst eine leere Seite (about:blank) und wartet, bis diese Seite geladen ist. Wichtig ist der vorherige Aufruf der DoEvents-Anweisung, da das Webbrowser-Steuerelement sonst gegebenenfalls noch nicht bereit ist. Danach gibt die Funktion den Verweis auf das HTMLDocument-Objekt zurück.

Haupttabelle erstellen

Die Funktion TabelleErstellen erzeugt dann die Tabelle, die alle anderen Elemente enthält, und liefert diese an die Objektvariable objTable zurück. Als Parameter erwartet diese Prozedur einen Verweis auf das HTMLDocument-Objekt:

Private Function TabelleErstellen(objDocument As MSHTML.HTMLDocument) As MSHTML.HTMLTable

    Dim objTable As MSHTML.HTMLTable

    Set objTable = objDocument.createElement("Table")

    objDocument.Body.appendChild objTable

    objDocument.Body.Style.fontFamily = "calibri"

    objDocument.Body.Style.FontSize = "8px"

    With objTable

        .Style.borderCollapse = "collapse"

    End With

    Set TabelleErstellen = objTable

End Function

Die Prozedur erstellt ein neues HTMLTable-Element im Kontext des HTMLDocument-Objekts und hängt dieses dann an das HTMLDocument-Objekt an, sodass es Teil des Dokuments wird. Außerdem stellt es einige Schrift-Eigenschaften ein und legt mit borderCollapse fest, dass die Rahmen zweier benachbarter Tabellenzellen miteinander verschmelzen sollen.

Spaltenköpfe erstellen

Wir haben nun ein Tabellen-Objekt, das wir mit einigen Zeilen ausstatten wollen. Die erste Zeile soll die Spaltenköpfe, also die Datumsangaben anzeigen, weshalb wir dieser Spalte 28 Zellen für je einen Tag zuweisen. Vorher werden jedoch noch zwei leere Zellen eingefügt, weil die folgenden Zeilen auch zwei Zellen zur Anzeige der Mitarbeiternamen und zum Herstellen eines Abstands zwischen Mitarbeiternamen und den Zellen zur Anzeige der Fehlzeiten enthalten (Abb. 9 veranschaulicht die beiden leeren Zellen).

pic011.png

Abb. 9: Zwei leere Zellen in der Zeile mit den Spaltenköpfen

Danach durchläuft eine Schleife 28 Tage ausgehend von dem im Textfeld txtStart angegebenen Tag. Der aktuelle Tag wird dabei in der Laufvariablen datAktuell gespeichert. Innerhalb der Schleife wird lediglich die Prozedur ZelleTagAnlegen aufgerufen (siehe Listing 2). Die Prozedur ermittelt zunächst per DLookup-Anweisung, mit welcher Farbe der aktuelle Tag hinterlegt werden soll, und wandelt den Farbwert in ein Format um, das mit HTML dargestellt werden kann (die dazu verwendeten Funktionen finden Sie im Modul mdlTools).

Listing 2: Zelle für die Spaltenüberschrift mit Datum anlegen

Private Function ZelleTagAnlegen(dat As Date, objRow As MSHTML.HTMLTableRow)

    Dim objCell As MSHTML.HTMLTableCell

    Dim strBGColorCurrent As String

    strBGColorCurrent = RGBToHTML(HexFromLong(DLookup("KalenderFarbeAktuellerTag", "tblOptionen")))

    Set objCell = objRow.insertCell

    With objCell

        .Style.fontFamily = "Calibri"

        .Style.FontSize = "12px"

        .innerHTML = Format(dat, "ddd.") & "<br>" & Format(dat, "dd.mm.")

        .Style.TextAlign = "center"

        .Style.padding = "0px"

        .Style.margin = "0px"

        .Style.Width = "40px"

    End With

    Select Case Weekday(dat, vbMonday)

        Case 6, 7

            objCell.Style.FontWeight = "bold"

            objCell.Style.backgroundColor = "#eeeeee"

    End Select

    If dat = Date Then

        objCell.Style.backgroundColor = strBGColorCurrent

    End If

Sie haben das Ende des frei verfügbaren Teils des Artikels erreicht. Lesen Sie weiter, um zu erfahren, wie Sie den vollständigen Artikel lesen und auf viele hundert weitere Artikel zugreifen können.

Sind Sie Abonnent?Jetzt einloggen ...
 

Kompletten Artikel lesen?

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

E-Mail:

© 2003-2015 André Minhorst Alle Rechte vorbehalten.