Termine in Berichten darstellen

Was die Darstellung von Terminen und Kalendern angeht, ist Outlook der Platzhirsch. Es gibt kaum eine Ansicht, die sich damit nicht verwirklichen lässt. Oder doch Nun: Ihre Kunden oder Sie selbst haben bestimmt Ideen, die selbst Outlooks Berichts-Engine an die Leistungsgrenze bringen. Und hier kommt Access ins Spiel: Verwenden Sie Berichte und bringen Sie Access dazu, Ihre Termine ganz nach Wunsch anzuzeigen.

Die Anzeige von Terminen in Access-Berichten ist seit jeher mit Bastelei verbunden – außer Sie begnügen sich damit, die Termine einfach tabellarisch aufzulisten. Dann brauchen Sie wirklich nicht viel mehr als einen handelsüblichen Bericht, in dessen Detailbereich Sie die vorliegenden Termine eintragen. Anspruchsvoller wird es dann schon, wenn der Bericht die Termine und deren Zeitaufwand hübsch grafisch aufbereitet darstellen soll – genau so, wie es auch beispielsweise in der Tagesübersicht von Outlook der Fall ist (siehe Bild 1).

pic001.png

Bild 1: Anzeige der Termine in der Tagesübersicht von Outlook

Auch erfahrenen Berichtsdesignern dürfte der Nachbau eines solchen Layouts leichte Sorgenfalten auf die Stirn legen, ist man doch normalerweise gewohnt, die Daten schön untereinander anzuordnen und eventuelle Feinheiten durch Gruppierungen oder Unterberichte abzudecken. Wer schon in unseren Beitrag Berichte manuell füllen (Shortlink 659) hineingeschaut hat, ahnt bereits, dass es hier nicht mit rechten Dingen – äh, Steuerelementen – zuging, sondern dass wir hier von Berichtsmethoden wie Print und Line Gebrauch gemacht haben.

Das allein reicht allerdings längst nicht aus, um Termine so wie unter Outlook darzustellen (genau genommen werden wir nicht jedes Detail nachbilden, doch die wichtigsten Elemente berücksichtigen wir natürlich).

Beginnen wir jedoch bei den Daten, die unserem Bericht zugrunde liegen – und die sind bei Weitem das am wenigsten Komplizierte dieser Lösung.

Die Tabelle tblTermine sieht wie in Bild 2 aus und enthält die folgenden Felder:

pic002.png

Bild 2: Die Tabelle tblTermine in der Entwurfsansicht

  • ID: Primärschlüsselfeld
  • Termin: Beschreibung des Termins
  • Termindatum: Datum, an dem der Termin stattfindet
  • Startzeit: Zeit, zu welcher der Termin beginnt
  • Endzeit: Zeit, zu welcher der Termin endet
  • FarbeID: Fremdschlüsselfeld zur Tabelle tblFarben; legt die Hintergrundfarbe für den Termin im Bericht fest
  • Bereich: Für das Layout des Kalenderberichts ist es wichtig zu wissen, ob Termine ein- oder mehrspaltig angezeigt werden. Termine, die in einem mehrspaltigen Bereich liegen, sind durch den gleichen Wert in diesem Feld zu erkennen.
  • Spalte: Gibt an, in welcher Spalte sich dieser Termin befindet.
  • Spaltenzahl: Gibt an, wieviele Spalten der Bereich umfasst, in dem sich dieser Termin befindet.

Die Funktion der letzten drei Felder wird weiter hinten erläutert, außerdem erfahren Sie dort, wie diese Felder gefüllt werden. Aktuell reicht es, wenn Sie wissen, dass die in diesem Beitrag vorgestellte Lösung Ihnen diese Aufgabe abnimmt.

Den Aufbau der Tabelle tblFarben entnehmen Sie Bild 3. Sie enthält neben dem Primärschlüsselfeld ID ein Feld zur Bezeichnung der Farbe (Farbe) sowie ein Feld mit dem Zahlenwert, der die Farbe repräsentiert (Farbwert).

pic003.png

Bild 3: Diese Tabelle speichert die Hintergrundfarben für die Termine im Bericht.

Das war schon fast alles, was Sie zum Modellieren eines Berichts wie in Bild 5 benötigen – es fehlen nur noch ein paar Hilfstabellen, Abfragen und einige Zeilen VBA-Code, um den Bericht zu füllen.

pic004.png

Bild 5: Beispiel für einen Terminbericht mit überlappenden Terminen

Termineingabe

Auf die Beschreibung von Formularen zur Eingabe der Termine verzichten wir in diesem Beitrag aus Platzgründen. Sie können die Termine allerdings in ein handelsübliches Formular eingeben oder diese auch aus Outlook importieren. Wie das geht, erfahren Sie beispielsweise im Beitrag Outlook-Termine im Griff (Shortlink 439).

Grundlagen des Kalenderberichts

Glücklicherweise gibt es auch Tage ohne Termine, und daher enthält ein Kalender auch nicht für jeden Tag Aufzeichnungen oder Einträge. Der Terminbericht sollte aber trotzdem für jeden Tag eine eigene Seite anzeigen – allein, damit man nicht immer auf das Datum schauen muss, wenn man den Kalender am Bildschirm oder auch in ausgedruckter Form durchblättert. Nach Montag soll Dienstag folgen und nicht schon der Mittwoch, wenn es am Dienstag keine Termine gibt.

Wie aber bringen wir Access dazu, ganz banal einen 365-seitigen Bericht als Grundlage für den Terminkalender eines Jahres zu liefern – ohne dass für jeden Tag ein Termin angelegt wäre

Nun, das geht ganz einfach: Wir brauchen einfach nur eine Datenherkunft, die tatsächlich alle 365 Tage liefert. Die Tabelle tblKalenderdaten ist eine solche Datenherkunft: Sie enthält für jeden Tag des Jahres 2009 einen eigenen Datensatz mit einem Primärschlüssel und einem Datumsfeld (siehe Bild 4). Wenn Sie möchten, so können Sie diese Tabelle durch weitere Spalten wie Werktag oder Feiertag ergänzen und solche Tage später im Bericht anders formatieren.

pic005.png

Bild 4: Diese Tabelle liefert die Grundlage für einen ganzjährigen Kalenderbericht.

Für Letzteres sollten Sie unbedingt einen eindeutigen Schlüssel festlegen, damit die Tabelle kein Datum doppelt enthalten kann. Damit das Füllen der Tabelle nicht soviel Arbeit macht, übernimmt die Routine aus Listing 1 diese Aufgabe. Sie erwartet das Start- und das Enddatum als Parameter und legt Datensätze für den angegebenen Zeitraum an.

Listing 1: Füllen der Tabelle tblKalenderdaten

Public Sub KalenderdatenFuellen(datStart As Date, datEnde As Date)
Dim dat As Date
Dim db As DAO.Database
Dim rst As DAO.Recordset
Set db = CurrentDb
db.Execute "DELETE FROM tblKalenderdaten", dbFailOnError
Set rst = db.OpenRecordset("tblKalenderdaten", dbOpenDynaset)
For dat = CDate(datStart) To CDate(datEnde)
 rst.AddNew
    rst!Kalenderdatum = dat
    rst.Update
Next dat
End Sub

Bericht anlegen

In den folgenden Absätzen erfahren Sie, wie Sie den Bericht aus Bild 5 anlegen und diesen mit Inhalt füllen. Damit Sie den Bericht Ihren Bedürfnissen anpassen können, bauen wir ihn Stück für Stück auf.

Den Start macht ein leerer Bericht, dem Sie übergangsweise die Tabelle tblKalenderdaten als Datenherkunft zuweisen.

Eine Seite pro Tag

Der Kalender soll jeden Tag auf einer eigenen Seite anzeigen. Daher reicht es nicht aus, einfach nur das Feld Kalenderdatum aus der Datenherkunft in den Detailbereich des Berichts zu ziehen. Sie müssen auch noch dafür sorgen, dass nach (oder vor) jedem Datensatz ein Seitenwechsel erfolgt. Theoretisch würde es reichen, den Detailbereich einfach groß genug zu gestalten.

Allerdings soll der Detailbereich möglichst nur den Kalender selbst enthalten, daher benötigen wir einen anderen Berichtsbereich als Unterkunft für das Feld Kalenderdatum (das ja im Bericht auch das Datum anzeigen soll).

Der Seitenbereich wäre eine gute Wahl, aber auch diesen sparen wir für eventuelle Elemente wie eine Überschrift auf. Die Kalenderdaten sollen in der richtigen Reihenfolge angezeigt werden, was Sie durch Anlegen einer Sortierung für das Feld Kalenderdatum im Bericht erreichen.

Und wenn Sie schon einmal dabei sind, können Sie auch gleich eine Gruppierung daraus machen und das Feld Kalenderdatum im Gruppenkopf unterbringen.

Dem Gruppenkopfbereich weisen Sie dann noch über das Setzen des Werts Vor Bereich für die Eigenschaft Neue Seite einen Seitenumbruch hinzu und stellen so sicher, dass jedes Datum auf einer neuen Seite angezeigt wird (siehe Bild 6).

pic006.png

Bild 6: Dieser Bericht zeigt jeden Tag auf einer neuen Seite an.

In der Seitenansicht sieht der Bericht nun wie in Bild 7 aus und liefert die erwarteten Datumsangaben auf je einer eigenen Seite.

pic007.png

Bild 7: Die Grundlage für den Terminkalender mit Tagesübersicht

Stundenplan hinzufügen

Im nächsten Schritt fügen wir das Raster hinzu, das den Tag beziehungsweise den Detailbereich in 24 Elemente aufteilt und diese mit den entsprechenden Stundenangaben versieht (siehe Bild 8).

pic008.png

Bild 8: Der Terminkalender besitzt nun ein Raster, in das man nur noch die Termine einfügen muss.

Dies geschieht in einer Routine, die im Ereignis Beim Drucken des Detailbereichs des Berichts ausgelöst wird:

Private Sub Detailbereich_Print(Cancel As Integer, PrintCount As Integer)
    UhrzeitrasterAnlegen
    End Sub

Die Routine UhrzeitrasterAnlegen (Listing 2) deklariert zunächst vier Variablen namens sngRepX1, sngRepX2, sngRepY1 und sngRepY2. Darin speichert sie die Koordinaten des Detailbereichs relativ zur linken oberen Ecke, die sie aus den Eigenschaften ScaleTop, ScaleWidth, ScaleLeft und ScaleHeight erhält.

Listing 2: Herstellen eines Rasters zur optischen Unterstützung der anschließend hinzugefügten Termine

Private Sub UhrzeitrasterAnlegen
    'Koordinaten für den Bericht
    Dim sngRepX1 As Single
    Dim sngRepX2 As Single
    Dim sngRepY1 As Single
    Dim sngRepY2 As Single
    Dim i As Integer
    Dim lngColor As Long
    With Me
    sngRepY1 = .ScaleTop
    sngRepX1 = .ScaleLeft
    sngRepX2 = .ScaleWidth
    sngRepY2 = .ScaleHeight
    'Raster mit Zeiten bauen
    For i = 1 To 25
        lngColor = RGB(225, 225, 225)
        Me.Line (sngRepX1, _
        sngRepY1 + (i - 1) * (sngRepY2 - sngRepY1) / 24) _
        -(sngRepX2, _
        sngRepY1 + (i - 1) * (sngRepY2 - sngRepY1) / 24), _
        lngColor
        CurrentX = 0
        If i < 25 Then
            lngColor = RGB(0, 0, 0)
            Me.ForeColor = lngColor
            Me.FontSize = 9
            Me.Print Format(DateAdd("h", i - 1, 0), "hh");
            Me.FontSize = 5
            Me.Print Format(DateAdd("h", i - 1, 0), " nn")
        End If
    Next i
    End With
    End Sub

Danach baut die Routine auch schon die Linienstruktur und die darin enthaltenen Stundenzahlen auf, und zwar in einer Schleife, welche die Werte 1 bis 25 durchläuft – immerhin gibt es 24 Stunden, von denen jede oben und unten durch eine Linie begrenzt werden möchte.

Beschriftungen brauchen wir aber ebenfalls nur 24, weshalb der untere Teil der Schleife nur ausgeführt wird, solange die Zählervariable einen Wert kleiner 25 aufweist. Man hätte auch zwei Schleifen aufbauen können – das ist aber sicher Geschmackssache.

Innerhalb der Schleife sorgt die Line-Anweisung zunächst dafür, dass 25 Linien sorgfältig auf die Höhe des Detailbereichs aufgeteilt werden. Die Breite wird dabei durch den ersten und dritten Parameter angegeben, wobei der erste den Wert 0 und der dritte einen Wert aufweist, welcher der Breite des Detailbereichs entspricht.

Die vertikale Position der Linie geben der zweite und der vierte Parameter der Line-Funktion an, welche die gleiche Formel enthalten. Diese liefert den dem Wert von i entsprechenden Anteil der Gesamthöhe des Detailbereichs in der aktuellen Einheit, standardmäßig also in Twips.

Termine vorbereiten

Nun geht es an den Teil des Berichts, in dem eine Menge Gehirnschmalz steckt – zumindest, wenn man dem Benutzer ermöglichen möchte, auch sich überschneidende Termine in den Kalender einzutragen. Man könnte nun sagen, dass dies keinen Sinn macht, da man normalerweise immer nur eine Tätigkeit zur gleichen Zeit betreibt – aber möglicherweise möchte der Benutzer ja auch weitere Ereignisse im Terminkalender unterbringen.

Schauen wir uns also an, was man braucht, um zwei oder mehr Termine im Bericht unterzubringen, die sich zumindest teilweise überschneiden. Die erste wichtige Erkenntnis ist dabei, dass die Termine nur dort auf mehrere Spalten aufgeteilt werden, wo auch mehrere sich überschneidende Termine angezeigt werden sollen. Dabei kann es sich um zwei Termine handeln, die sich ganz oder teilweise überlappen, vielleicht sind es aber auch drei oder mehr. Dabei gibt es dann auch wieder Varianten: Wenn der erste, zweite und dritte Termin einen gemeinsamen Zeitpunkt haben, dann brauchen Sie auch drei Spalten. Wenn sich hingegen der erste und der zweite Termin überschneiden, der dritte Termin aber erst nach dem ersten Termin beginnt, reichen zwei Zeilen aus – der dritte Termin kann dann ja in der ersten Zeile hinter dem ersten Termin eingefügt werden.

Danach folgen dann möglicherweise weitere Termine, die sich mit keinem anderen Termin überschneiden und die deshalb wieder über die ganze Breite des Berichts gestreckt werden sollen.

Das ist der erste Schritt zur Lösung: Wir müssen herausfinden, welche Bereiche es für den aktuellen Tag gibt und wie viele Spalten jeder Bereich aufweist. Einen Bereich mit x Spalten und einen Bereich mit y Spalten trennt ein, wenn auch noch so kleiner, Bereich ohne Termin. Und zusätzlich müssen wir ermitteln, wie viele Spalten der jeweilige Bereich benötigt.

Bei diesem Schritt hilft die Abfrage qryZeitSpaltenanzahl mit der Entwurfsansicht aus Bild 9. Sie führt das Ergebnis der Abfragen qryZeit und qryTermine zusammen.

pic009.png

Bild 9: Die Abfrage qryZeitSpaltenanzahl in der Entwurfsansicht

Die Abfrage qryZeit (siehe Bild 10) kombiniert den Inhalt der beiden Tabellen tblStunden und tblMinuten so, dass das Ergebnisfeld der Abfrage alle Minuten von 0:00 bis 23:59 inklusive Stundenangabe liefert. Die Abfrage qryTermine entspricht weitgehend der Tabelle tblTermine, enthält aber ein zusätzliches Feld namens Endzeit1 mit folgendem Inhalt:

pic010.png

Bild 10: Die Abfrage qryZeit liefert alle Zeiten von 0:00 bis 23:59 im Minutenabstand.

Endzeit1: Wenn([Endzeit]>DatAdd("n";60;[Startzeit]);[Endzeit];DatAdd("n";60;[Startzeit]))

Dieser Ausdruck liefert den gleichen Wert wie das Feld Endzeit, wenn der Termin 60 Minuten oder länger dauert, sonst erhält Endzeit1 einen Wert, welcher der Startzeit plus 60 Minuten entspricht. Somit ergibt sich aus Startzeit und Endzeit1 immer ein Termin, der mindestens eine Stunde dauert – warum, erfahren Sie später.

Welches Ergebnis aber liefert die Abfrage qryZeitSpaltenanzahl Betrachten wir zunächst die ersten beiden Felder. Diese liefern alle Kombinationen aus den Minuten eines Tages (von 0:00 bis 23:59) und den Terminen.

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