Kalendersteuerelement, Teil 2

Wenn Sie ein Abonnement des Magazins 'Access im Unternehmen' besitzen, können Sie sich anmelden und den kompletten Artikel lesen.
Anderenfalls können Sie das Abonnement hier im Shop erwerben.

Kalendersteuerelement, Teil 2

Das im ersten Teil dieser Ausgabe beschriebene Kalendersteuerelement eignet sich vornehmlich zur Auswahl eines Datums. Benötigen Sie aber eine Übersicht, wie die Termine des Outlook-Kalenders mit der Markierung von Datumsbereichen, etwa zur Darstellung Ihrer Urlaubsplanung, so sind die Anforderungen ganz andere. Auch für diesen Zweck stellen wir ein Pseudo-Steuerelement vor, das allein mit Access-Bordmitteln realisiert ist.

Terminkalender

Einsatzbereiche für solche kalendarischen Übersichten gibt es viele. Feiertage können darin eingetragen werden, Geburtstage und andere Jubilaren, die Buchung von Ferienwohnungen oder Autovermietungen, die Einsatzplanung von Mitarbeitern, oder Sie markieren hier Ihre Urlaubstage. Dabei geht es weniger um die Auswahl von Terminen, als um deren Darstellung. Bild 1 zeigt bereits, wie unser Bespiel aussieht.

Demo des Unterformularsteuerelements Monatskalender nach Einbau in ein Hauptformular mit Markierungen

Bild 1: Demo des Unterformularsteuerelements Monatskalender nach Einbau in ein Hauptformular mit Markierungen

Natürlich bauen wir hier keinen komplexen Terminkalender nach, wie den von Outlook. Das Kalendersteuerelement sfrmCalendarMonths ist eng an die einfache Version des Auswahlkalenders im ersten Teil dieser Ausgabe angelehnt, wie seine linke Seite schon vermuten lässt. Es dient in erster Linie der Visualisierung bereits vorliegender Termindaten. Beschreiben wir zunächst kurz seinen Aufbau.

Grundsätzlich werden hier drei Monate mit ihren Tagen angezeigt. Die Auswahl der Monate geschieht mit dem Kombinationsfeld links, die den mittleren Monat einstellt. Die übrigen Navigationselemente, wie die Jahres-Combo oder die Buttons zum Weiterschalten der Monate gleichen in ihrer Funktion genau der des einfachen Auswahlkalenders. Zusätzlich ist die Kalenderübersicht noch mit einer Titelzeile oben versehen, die Sie mit beliebigem Inhalt füllen können. Das eigentliche Feature aber sind die rot unterlegten Zellen, die durch die dem Steuerelement über eine öffentliche Funktion zugewiesene Datumsbereiche zustande kommen. Mehr gibt das Steuerelement in dieser Version nicht her. Es reagiert auch nicht auf Klicks in die Zellen. Das wäre eine Eigenschaft, die Sie mit dem Wissen aus dem ersten Teil dieser Ausgabe zum Auswahlkalender leicht selbst nachtragen könnten.

Basistabelle

Ähnlich, wie beim Auswahlkalender, kommt für die Darstellung der Tage eine Tabelle zum Einsatz, deren Felder dann von 21 Textboxen im Endlosformular ausgegeben werden. Hier reichen sieben Felder natürlich nicht aus. Für jede Spalte des Dreimonatskalenders muss ein eigenes Feld her, wenn nicht etwa eine umständliche Kreuztabellenabfrage verwendet werden soll.

Die Felder der Tabelle tblCalendarMonths sind ebenfalls vom Zahlentyp Long. Die Nummerierung geschieht über das Präfix D, gefolgt von der Nummer des Wochentags und der Position des Monats im Kalender. D62 bezeichnet also etwa den sechsten Tag (Sonnabend) und die 2 darin den mittleren Monat. Bild 2 verdeutlicht dies ausschnittsweise. Auch diese Tabelle wird vom Formular selbst mit Datensätzen gefüllt.

Ausschnitt der Monatskalendertabelle in der Entwurfsansicht

Bild 2: Ausschnitt der Monatskalendertabelle in der Entwurfsansicht

Allerdings speichern wir hier nicht jeweils die Tage der Monate ab, da diese ja mehrfach vorkommen –pro Monat einmal. Stattdessen nimmt ein Feld jeweils tatsächlich einen Datumswert auf. Warum ein Datum in einem Long-Feld? Sie ahnen es möglicherweise: Auch hier möchten wir Bedingte Formatierung zur farblichen Unterscheidung der Zellen benutzen und nehmen wieder negative Werte als Indiz für eine Markierung. Schauen Sie auf die gefüllte Tabelle in Bild 3.

So präsentiert sich die Tabelle tblCalendarMonths, nachdem sie über das Formular mit Daten gefüllt wurde.

Bild 3: So präsentiert sich die Tabelle tblCalendarMonths, nachdem sie über das Formular mit Daten gefüllt wurde.

Ein negativer Wert führt später zur roten Hervorhebung der Zelle. Der Absolutwert einer Zahl stellt das Datum dar. Wir erwähnten bereits, dass ein Access- oder VBA-Datum tatsächlich ein Double-Wert ist, dessen ganzzahliger Anteil den Tag angibt. Und diesen kann auch ein Long-Wert aufnehmen. Beispiel:

  Now()			-> Datum: 21.07.2017 08:33
  CDbl(Now())		-> Double: 42937,35625
  Clng(CDbl(Now()))		-> Long: 42937
  CDate(42938)		-> Datum: 21.07.2017

Benötigen Sie also den Zeitanteil eines Datums nicht, so reicht zu dessen Speicherung ein Long-Wert.

Die Tatsache, dass hier tatsächlich Datumswerte abgespeichert sind, macht es überdies später leichter, sie beim Klick auf die Textboxen zu ermitteln. Den Steuerelementinhalt unterziehen Sie dazu lediglich der Funktion CDate(). Eine Berechnung aus der Zellenposition, wie beim Auswahlkalender, ist hier nicht nötig. Schauen wir im Folgenden an, wie die Daten der Tabelle generiert werden. Auch hier nennt sich die verantwortliche Prozedur FillCalendar.

Daten der Basistabelle erzeugen

Die Prozedur FillCalendar wird im Formularmodul immer dann aufgerufen, wenn sich der Monat oder Tag ändern. Das kann durch Auswahl in den entsprechenden Kombinationsfeldern geschehen, durch Betätigung der Buttons zum Weiterschalten, oder durch Zuweisung an die Eigenschaftsprozeduren Year und Month. Listing 1 zeigt wieder eine gekürzte Version, in der nur die für die Erzeugung der Datensätze relevanten Teile abgebildet sind.

Private Sub FillCalendar()
     Dim rs As DAO.Recordset
     Dim i As Long, j As Long, n As Long, f As Long
     Dim StartDate As Date, EndDate As Date
     Dim MaxDate As Date, DTmp As Date
     
     StartDate = DateSerial(m_Year, m_Month - 1, 1)
     EndDate = DateSerial(m_Year, m_Month + 2, 0)
     
     CurrentDb.Execute "DELETE FROM tblCalendarMonths"
     Set rs = CurrentDb.OpenRecordset("tblCalendarMonths", dbOpenDynaset)
     For n = 0 To 4
         rs.AddNew
         For i = 1 To 3
             MaxDate = DateSerial(m_Year, m_Month + i - 1, 0)
             For j = 1 To 7
                 DTmp = DateAdd("m", i - 1, StartDate)
                 DTmp = DateAdd("d", n * 7 + j - 1, DTmp)
                 If DTmp > MaxDate Then Exit For
                 If IsInRanges(DTmp) Then f = -1 Else f = 1
                 rs.Fields("D" & CStr(j) & CStr(i)).Value = f * CLng(DTmp)
             Next j
         Next i
         rs.Update
     Next n
     
     rs.Close
     Me.Requery
End Sub

Listing 1: Befüllen der Tabelle tblCalendarMonths

Zunächst wird in StartDate der erste Tag des linken Monats ermittelt. Dazu wird vom Monat der Member-Variablen m_Month eins abgezogen und das Datum mit DateSerial berechnet. EndDate wiederum speichert den letzten Tag des rechten Monats. Da die Anzahl der Tage eines Monats variabel ist, kann für den Tag-Parameter in DateSerial kein Wert angegeben werden. Man behilft sich in diesem Fall mit dem Folgemonat (m_Month + 2) und dem Tag 0. Das entspricht dem ersten Tag des Monats minus eins.

Die Execute-Anweisung leert die Tabelle und OpenRecordset öffnet eine beschreibbare Datensatzgruppe auf sie. Die folgende Schleifenkonstruktion weicht von der des Auswahlkalenders ab. Wir benötigen hier drei ineinander verschachtelte Schleifen. Die äußere auf die Zählervariable n stellt die Zeilen der Kalender dar. Diese entspricht dann auch der Anzahl der Datensätze der Tabelle, weshalb der Durchlauf auch mit einem AddNew beginnt. Die nächste Schleife mit dem Zähler i betrifft die drei Monate, die innerste Schleife mit dem Zähler j deren Wochentage.

Zur Berechnung des Datums eines Datenfeldes wird hier StartDate nicht einfach fortlaufend erhöht. Stattdessen werden zu StartDate zweimal Werte über die DateAdd-Funktion addiert und das Ergebnis in der Date-Variablen DTmp zwischengespeichert. Die erste DateAdd-Funktion addiert die Nummer des Monats aus dem Monatsschleifenzähler i, wobei der Ausdruck m der Funktion erst sagt, dass Monate zu addieren sind. Das zweite DateAdd verwendet Tag-Werte (Ausdruck d) und berechnet über den Wochentag in j und das Siebenfache der betreffenden Woche n einen weiteren Offset. Im Prinzip könnte dieser Wert aus DTmp dann schon einem Tabellenfeld zugewiesen werden.

Da jedoch die Schleifendurchläufe dazu führen können, dass über die Addition der Tage ein über einen Monat hinausreichendes Datum ermittelt würde, gibt es eine Abbruchbedingung für die innere Schleife. Ist DTmp größer, als der letzte Tag des Monats, so wird die Schleife verlassen. Dieser letzte Tag ist in der Variablen MaxDate gespeichert und wird nach dem Beginn der zweiten Schleife jeweils neu berechnet.

Schließlich sind noch die Zeiträume zu berücksichtigen, die im Kalendersteuerelement rot zu unterlegen sind und durch negative Datumszahlen repräsentiert werden. Die Funktion IsInRanges, auf welche wir noch zu sprechen kommen, wird dazu befragt.

Befindet sich das Schleifendatum DTmp innerhalb eines der verabreichten Zeiträume, so gibt die Funktion True zurück. In diesem Fall nimmt die Variable f den Wert -1 an, andernfalls 1. Und f ist dann wieder der Multiplikator für den Datumswert, der dem Feld des Recordsets zugewiesen wird. Der Name des Felds ergibt sich aus dem Präfix D und den Zählvariablen j und i über String-Verkettung.

Zeiträume zuweisen

Dem Kalendersteuerelement können beliebig viele Datumsbereiche hinzugefügt werden, die dann in der Ansicht rot markiert werden. Sie können dabei auch außerhalb des dargestellten Bereichs liegen. Die Prozedur AddRange (s. Listing 2) bewerkstelligt deren Speicherung.

Dies war die Leseprobe dieses Artikels.
Melden Sie sich an, um auf den vollständigen Artikel zuzugreifen.

Bitte geben Sie die Zeichenfolge in das nachfolgende Textfeld ein

Die mit einem * markierten Felder sind Pflichtfelder.

Ich habe die Datenschutzbestimmungen zur Kenntnis genommen.