Kalendersteuerelement, Teil 1

Lies diesen Artikel und viele weitere mit einem kostenlosen, einwöchigen Testzugang.

Zur Ein- oder Ausgabe eines Datums macht sich ein geeignetes Kalendersteuerelement im Formular besser als ein schnödes Textfeld. Das kann fest im Formular integriert sein, oder als Popup zur Auswahl erscheinen. In Buchungssystemen im Web sind solche Kalenderelemente allgegenwärtig. Auch Access wurde mit der Version 2007 ein solches Popup-Element spendiert, welches sich aber leider in keiner Weise steuern lässt. Grund genug also, um sich nach Alternativen umzuschauen.

Existierende Kalenderelemente

Einst war ein Kalendersteuerelement in Form der ActiveX-Datei mscal.ocx optionaler Bestandteil der Office-Installation. Das hat sich seit einiger Zeit geändert. Man ist nunmehr allein auf das Popup angewiesen, das bei Datumsfeldern zur Auswahl erscheint, wenn in das zugehörige Textfeld geklickt wird. Eine dauerhafte Ansicht des Kalenders ist somit verwehrt.

Bild 1 zeigt das alte Kalender-ActiveX-Steuerelement links oben im Formular. Aus irgendeinem Grund befand es sich, möglicherweise aus älteren Office-Installationen, noch bei uns in englischer Version im System. Seine Darstellung lässt sich im Formularentwurf oder auch per VBA steuern. Das betrifft die Schriftarten und die Farbgebung. Es löst bei einigen Aktionen ein Ereignis aus und spiegelt in der Eigenschaft Value das markierte Datum wieder. Sollte auf ihrem System die ActiveX-Datei nicht installiert sein, so meldet Access beim Aufruf des Formulars der Beispieldatenbank den etwas eigenartigen Fehler In diesem Formular befindet sich kein Steuerelement. Entfernen Sie in diesem Fall im Entwurf des Formulars frmCalendarTest einfach den Platzhalter des Steuerelements.

Demo einiger Datums- und Kalendersteuerelemente im Testformular

Bild 1: Demo einiger Datums- und Kalendersteuerelemente im Testformular

Da von diesem Fall auszugehen ist, fragt sich, wie mit anderen Mitteln eine ähnliche Darstellung und Funktion zu erreichen wäre. Testweise haben wir ein Listenfeld (rechts unten in der Abbildung) und ein Microsoft Listview Control (links unten) als Kalender zu gestalten versucht. Mit einigen Zeilen Code und den entsprechenden Einstellungen für die Steuerelemente gelingt dies auch. Der inakzeptable Nachteil der Lösungen besteht darin, dass sich in diesen Steuerelementen nur ganze Zeilen markieren lassen, nicht aber einzelne Zellen. Eine Auswahl per Maus scheidet deshalb aus. Derlei ließe sich also lediglich zur Anzeige der Tage eines Monats verwenden. Doch das brauchen Sie wohl höchst selten. Bleiben noch zwei Alternativen: das Microsoft DateTime Picker Control und die Access-Textbox mit Datumsformatierung. Ersteres zeigt sich im Formular rechts oben. Es entspringt der ActiveX-Datei mscomct2.ocx, die früher ebenfalls mit Office installiert wurde (s. Bild 2).

Das Microsoft DateTime-Picker- ActiveX-Steuerelement

Bild 2: Das Microsoft DateTime-Picker- ActiveX-Steuerelement

Wir erwähnen es nur der Vollständigkeit halber, denn außer einer abweichenden Gestalt bietet es gegenüber der dem Popup von Access (s. Bild 3) keine sonderlichen Vorteile. Nur die Schriftarten und Farben können hier zusätzlich eingestellt werden. Die Funktionalität ist bei beiden jedoch gleich.

Das Popup-Element zur Datumsauswahl bei Access-Textboxen

Bild 3: Das Popup-Element zur Datumsauswahl bei Access-Textboxen

Normalerweise öffnet sich das Kalenderelement einer Textbox mit Datumsformatierung dann, wenn Sie auf das Symbol rechts neben dem Textfeld klicken. Es gibt aber eine Anweisung, die die Anzeige des Kalender-Popups erzwingt. Möchten Sie etwa, dass es sofort beim Eintritt in das Feld erscheint, oder auch beim Klicken in das Textfeld, so schreiben Sie die folgende Zeile in die entsprechenden Ereignisprozeduren:

Private Sub txtDate_Click()
     RunCommand acCmdShowDatePicker
End Sub
Private Sub txtDate_Enter()
     RunCommand acCmdShowDatePicker
End Sub

Diese RunCommand-Anweisung ermöglicht das öffnen des Popups per Code, falls die Datums-Textbox gerade den Fokus besitzt. Andernfalls ereignet sich eine Fehlermeldung. Verwenden Sie sie deshalb besser nur in den Ereignissen der Textbox selbst.

Sicher finden Sie auch noch weitere Fremdsteuerelemente in Form von ActiveX-Dateien bei Drittanbietern. Es gilt jedoch die Maxime, wegen der möglichen Registrierungsprobleme solche ActiveX-Komponenten nur dann einzusetzen, wenn es absolut notwendig ist. Und das ist hier nicht gegeben, denn ein Kalendersteuerelement lässt sich gut auch mit Access-Bordmitteln selbst erstellen!

Kalender im Eigenbau

Natürlich könnte man einfach 31 Steuerelemente, etwa Buttons oder Textboxen, in einem Formular so anordnen, dass diese einen Kalender ergäben. Um auf eine Datumsauswahl zu reagieren, bräuchte es dafür dann eben auch 31 Ereignisprozeduren. Das wäre zwar recht unkompliziert, ist aber wenig elegant.

Dabei reicht es im Prinzip ja für die Tage der Woche nur sieben Steuerelemente im Detailbereich zu platzieren und diese Wochen untereinander zu wiederholen. Damit sind wir allerdings auf eine Tabelle angewiesen, die die Datensätze für alle Wochen eines Monats bereitstellt. Nur eine Tabelle oder Abfrage als Datensatzherkunft kann bewirken, dass sich der Detailbereich wiederholt.

Bevor es Schritt für Schritt an die Entwicklung des Kalenderformulars geht, sollte noch definiert werden, was der Ausdruck Steuerelement in unserem Zusammenhang bedeutet. Ein wirklich neues Steuerelement kann nun mal mit Access nicht erzeugt werden. Sie können lediglich aus bestehenden Elementen eine neue Funktionsgruppe erstellen, die den Anschein eines einzelnen Steuerelements erweckt. Damit das Ganze wiederverwendbar ist, sollte diese Funktionsgruppe möglichst in einem Formular ohne weitere Abhängigkeiten untergebracht werden, welches Sie später als Unterformular in andere einsetzen. Dieses Unterformular stellt dann quasi ein Pseudo-Steuerelement dar. In der Beispieldatenbank nennt es sich sfrmCalendar. Das Präfix s steht für Subform.

Wochentabelle für einen Monat

Die Basistabelle tblCalendar für das Kalenderformular in Bild 4 weist lediglich die sieben Datenfelder D1 bis D7 von Typ Long für die einzelnen Tage der Woche auf. Für die Namen der Felder kann man eher nicht Kürzel für Wochentage, wie Mo bis So, verwenden, da der Erste eines Monats eben selten ein Montag ist.

Die Tabelle tblCalendar als Basis für den Kalender im Eigenbau

Bild 4: Die Tabelle tblCalendar als Basis für den Kalender im Eigenbau

Die Nummerierung in den Namen der Felder ermöglicht später den gezielten Zugriff auf die Spalten der Tabelle. Ein Blick auf Bild 5 zeigt, was dahinter steckt.

Die gefüllte Tabelle tblCalendar im Datenblatt

Bild 5: Die gefüllte Tabelle tblCalendar im Datenblatt

Die Datensätze füllt nämlich eine VBA-Routine des Formulars zur Laufzeit unter Angabe des gewünschten Jahrs und Monats. Sie ordnet die Zahlen für die Tage so an, dass sich links immer die Montage befinden. Der Erste des Monats wiederum soll in der ersten Zeile auftauchen. Ist das kein Montag, so sind links von diesem Feld die Tage des Vormonats einzusetzen. Hier sind das der 28. bis 31.. ähnliches gilt für den Letzten des Monats, welcher sich im letzten Datensatz befinden soll, aber in der Regel kein Sonntag ist. Folglich ergeben sich in der letzten Zeile rechts vom Monatsletzten meist noch weitere Tage des Folgemonats. Je nach Monat und Jahr sind in der Tabelle mindestens vier, maximal sechs, Datensätze zu erzeugen.

Meist sind die Tage, welche nicht zum angezeigten Monat gehören, also jene des Vor- und Folgemonats, in Kalendern ausgegraut dargestellt. Um dieses Feature auch unserem Kalender zu spendieren, verwenden wir für die Textfelder im Formular eine Bedingte Formatierung. Denn per VBA-Code kann zwar die Hintergrundfarbe einer Textbox über die Eigenschaft BackgroundColor gesteuert werden, doch das betrifft dann sämtliche Zellen einer Spalte, die sich von diesem Datenfeld ableiten, nicht jedoch einzelne Zellen.

Um Bedingte Formatierung kommt man hier also nicht herum. Da diese einen Vergleichsausdruck für die Steuerung des Formats verwendet, benötigen wir irgendein Indiz, welches die nicht zum Monat gehörigen Tage definiert. Und das ist hier das Minuszeichen. Alle auszugrauenden Tage haben einen negativen Wert. Damit ist auch klar, weshalb hier keine Datumstypen für die Felder der Tabelle zum Einsatz kommen, denn deren Werte können nicht negativ sein. Ein Long– oder ein Integer-Wert reichen zur Kennzeichnung aus.

Der direkte Bezug des Kalenders zu einer Tabelle bringt allerdings einen Nachteil mit sich. Benötigen Sie etwa zwei Kalendersteuerelemente in Ihrem Hauptformular, so wären diese ohne weiteres Zutun beide an dieselbe Tabelle gebunden und zeigten in der Folge auch die gleichen Daten an. In diesem Fall erstellen Sie eine Kopie der Tabelle und bezeichnen diese etwa mit tblCalendar2. Die folgend beschriebene Routine zum Füllen der Tabelle kann unterschiedliche Tabellen berücksichtigen.

Füllen der Tabelle per VBA

Die dafür verantwortliche Routine nennt sich FillCalendar und ist in Listing 1 abgebildet, wobei hier einige Teile entfernt wurden, die nicht unmittelbar zum Erzeugen der Datensätze gehören.

Private m_Table As String
Property Get Table() As String
     Table = m_Table
End Property
Property Let Table(ByVal Value As String)
     m_Table = Value
     FillCalendar
End Property 
Private Sub FillCalendar()
     Dim rs As DAO.Recordset
     Dim i As Long, j As Long
     Dim n As Long
     Dim StartDate As Date
     Me.RecordSource = m_Table
     n = Weekday(DateSerial(m_Year, m_Month, 1), vbMonday)
     StartDate = DateSerial(m_Year, m_Month, 1) - n
     CurrentDb.Execute "DELETE FROM " & m_Table
     Set rs = CurrentDb.OpenRecordset("SELECT * FROM " & m_Table, dbOpenDynaset)
     For j = 0 To 5
         rs.AddNew
         For i = 0 To 6
             StartDate = StartDate + 1
             n = VBA.Month(StartDate)
             rs.Fields("D" & CStr(1 + i)).Value = Day(StartDate) * IIf(n <> m_Month, -1, 1)
         Next i
         rs.Update
         If n <> m_Month Then Exit For
     Next j
     rs.Close
     Me.Requery
End Sub

Listing 1: Füllen einer Kalendertabelle über VBA-Code

Der Name der zu füllenden Tabelle steht im Kopf in der Eigenschaftsvariablen m_Table des Formularmoduls. Dieser Variablen muss also erst ein Wert zugewiesen werden, was die Property-Let-Prozedur Table übernimmt:

frm.Table = "tblCalendar"

Erst dann kann die eigentliche Routine aufgerufen werden. Sie weist dem Formular selbst zunächst als Datensatzherkunft (RecordSource) die nun in m_Table stehende Tabelle zu. Das Formular ist im Entwurf also noch nicht zwingend an eine Tabelle gebunden, sondern das geschieht hier zu Laufzeit.

Nun benötigen wir jenes Datum, welches in der linken oberen Ecke des Kalenders steht. über Property-Prozeduren (hier nicht im Listing), wurde in den Variablen m_Month und m_Year der gewünschte Monat und das Jahr für den Kalender abgespeichert. Den Ersten dieses Monats erhalten Sie über die VBA-Funktion DateSerial:

DateSerial(m_Year, m_Month, 1)

Das ist indessen noch nicht das Datum, welches links oben steht. Um zu ermitteln, wie viele Tage des Vormonats zu berücksichtigen sind, kommt die VBA-Funktion Weekday zum Einsatz. Sie gibt eine Zahl zurück, die den Wochentag symbolisiert. Die 1 entspricht dabei Montag, die 7 dem Sonntag. Nun muss vom Ersten des Monats lediglich diese Zahl subtrahiert werden, und schon steht das Datum links oben fest. Es wird der Variablen StartDate vom Type Date zugewiesen. Nach diesen Vorarbeiten kann die Tabelle m_Table über zwei verschachtelte Schleifen mit Daten versehen werden.

Doch vorher muss die Tabelle noch über die Execute-Anweisung und den SQL-DELETE-Ausdruck geleert werden. Anschließend öffnet ein Recordset rs die Datensätze zum Beschreiben.

Die Zählervariable für die Schleife zum Hinzufügen von Datensätzen ist j. Da maximal sechs Zeilen im Kalender stehen können, ist ihr Bereich auf 0 bis 5 eingestellt. Nach jedem Durchlauf wird per AddNew ein neuer Datensatz erzeugt. Für den Zugriff auf die Feldwerte eines Datensatzes gibt es dann die folgende Schleife auf den Zähler i, deren Bereich sich für die einzelnen Wochentage von 0 bis 6 erstreckt. In dieser wird fortlaufend der Wert des Kalenderdatums in StartDate um eins erhöht. Das ist statthaft, weil ein Date-Type imgrunde ein Double-Wert ist, wobei die Nachkommastellen die Tageszeit angeben, die Vorkommastellen die Tage. Also führt Addition von 1 zum nächsten Tag.

Der Zugriff auf die Datenfelder geschieht namentlich über das Präfix D und den Zahler i, zu dem noch 1 addiert werden muss, weil die Felder von 1 bis 7 nummeriert sind, nicht von 0 bis 6. Der Wert eines Felds errechnet sich aus dem Tagesanteil des Datums, welchen die VBA-Funktion Day zurückgibt.

Im Prinzip wäre es das auch schon, wollten wir nicht die Tage außerhalb des Zielmonats mit einem Minuszeichen versehen werden. Um jene zu eruieren, wird in der Variablen n der Monatsanteil des Datums über die Funktion Month() zwischengespeichert. Weicht dieser Wert vom Zielmonat ab (n <> m_Month), so greift die IIf-Bedingungsfunktion (entspricht Wenn() in Abfragen), die entweder eine –1 oder eine 1 als Ergebnis zeitigt. Und das ist eben der Multiplikator für den Tageswert.

Da die äußere Schleife immer von 0 bis 5 zählt, würden auch immer sechs Zeilen im Kalender stehen. Um das etwa für den Monat Februar zu verhindern, vergleicht die Zeile nach der inneren Schleife die Monate abermals und verlässt die äußere, sobald der Folgemonat erreicht ist.

Damit ist das Werk vollbracht. Die Requery-Anweisung auf das Formular (Me) baut die Ansicht auf Grundlage der neuen Datensätze nun neu auf.

Entwurf des Kalenderformulars

Neben den eigentlichen Tagen des Kalenders im Detailbereich soll das Formular noch in den Spaltenköpfen die Wochentage anzeigen. Außerdem dienen zwei Kombinationsfelder im Formularkopf der Auswahl des gewünschten Jahres und Monats. Als zusätzliche Navigationselemente können die Werte dieser Comboboxen über darunter liegende Buttons jeweils um eins vor oder zurückgeschaltet werden. Bild 6 demonstriert den Aufbau.

Entwurfsansicht des Endlosformulars sfrmCalendar

Bild 6: Entwurfsansicht des Endlosformulars sfrmCalendar

Die Spaltenüberschriften sind durch Labels realisiert, die hier mit festen Wochentagen versehen sind. Im Detailbereich gibt es sieben Textboxen, die anfänglich an die Datenfelder D1 bis D7 der Tabelle tblCalendar gebunden waren. Das aber muss umgangen werden, weil sonst ja negative Tageswerte erschienen. Der Ausdruck Abs aber macht aus negativen Zahlen positive, lässt positive aber unverändert. Also ist der Steuerelementinhalt der ersten beiden Textboxen dieser:

= Abs([D1])
= Abs([D2])
...

Damit werden die Tage korrekt ausgegeben. Fehlt nur noch die graue Hinterlegung der nicht zum Monat gehörigen Tage des Kalenders über die Bedingte Formatierung.

Bedingte Formatierung der Tageswerte

Klicken Sie auf die erste Textbox im Detailbereich rechts und wählen im Kontextmenü den Eintrag Bedingte Formatierung…. Das ruft einen Dialog, wie in Bild 7, auf den Plan. Hier sind zunächst eine oder mehrere Bedingungsregeln anzulegen. Uns reicht eine. Mit Klick auf Neue Regel öffnet sich ein zweiter Dialog. Im Bereich für den Vergleichsausdruck dürfen Sie nicht die Option Feldwert wählen, welche sich auf den Wert bezöge, den das Textfeld gerade anzeigt. Und das ist ja über die Abs-Funktion immer ein positiver. Stattdessen wählen Sie Ausdruck und setzen in das nebenstehende Feld den String [D1]<0.

Dialoge zur Bedingten Formatierung der Datumtextfelder

Bild 7: Dialoge zur Bedingten Formatierung der Datumtextfelder

Immer dann, wenn das Datenfeld D1 kleiner ist, als 0, greift die Bedingung und damit die darunter eingestellte Formatierung. Für den Hintergrund wurde ein leichtes Grau genommen und für die Schriftfarbe ein Dunkelgrau, damit diese Tageswerte zur Laufzeit erscheinen, wie deaktivierte Steuerelemente. Nach dem Schließen beider Dialoge ist die Bedingte Formatierung abgeschlossen.

Den Vorgang wiederholen Sie für die weiteren Textboxen des Detailbereichs, wobei hier natürlich jeweils das richtige Datenfeld im Vergleichsausdruck gewählt werden muss. Also die Felder D2 bis D7. Das Ergebnis der Angelegenheit können Sie schon einmal in Bild 8 begutachten.

Das Formular sfrmCalendar zur Laufzeit ist hier standalone aufgerufen

Bild 8: Das Formular sfrmCalendar zur Laufzeit ist hier standalone aufgerufen

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

Testzugang

eine Woche kostenlosen Zugriff auf diesen und mehr als 1.000 weitere Artikel

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar