1. Ich möchte ein Haltbarkeitsdatum als dynamisch berechnendes Feld in eine Dokumentenvorlage einbringen. Wie sieht die Syntax des Feldcodes dazu aus?
2. Eine Feldfunktion Quartal kennt MS Word nicht. Wie lässt sich eine solche Feldfunktion - Dank Bedingungsfeldern - dennoch implementieren?
3. Eine Feldfunktion Kalenderwoche kennt MS Word nicht. Wie kann eine entsprechende benutzerdefinierte Funktion implementiert werden?
4. Ich möchte mit Textmarken Datumsberechnungen durchführen. Wie stelle ich das an?
5. Ich möchte mit Textformularfeldern Datumsberechnungen durchführen. Wie stelle ich das an?
6. Ich möchte meine Präsenszeiten in einer Word-Tabelle führen und berechnen. Wie muss ich dabei vorgehen?
7. Ich möchte für eine UserForm die Werktage (ohne Samstage, Sonntage und Feiertage) berechen. Wie sieht eine entsprechende Ereignisprozedur aus?
Szenario I - Benutzerdefinierte Feldfunktion: Heute in 14 Tage
Szenario II - Benutzerdefinierte Feldfunktion: Kalenderwoche
Ausweichlösung mit einer benutzerdefinierten Dokumenteigenschaft
Word-Felder beherrschen die Datumsarithmetik nicht. Mit Biegen und Brechen ist es aber dennoch möglich, einfache Datumsberechnungen durchzuführen. Mit monströsen Felddefinitionen ist es auch möglich, immerhin bis zu 28 Tage nach vorne zu rechnen, wie die Arbeit von Chris Woodman beweist. Chris stellt auch gleich eine komfortable Benutzeroberfläche zur Verfügung, welche es erlaubt, solche Monsterfelddefinitionen vollautomatisch zu erstellen. Dieses Skript wird darauf noch zurückkommen.
Falls das Dokument keine weiteren datumsspezifischen Felder enthält, können Sie auch erwägen, das Systemdatum unter MS Windows für die Zeit des Ausdruckes des Word-Dokumentes zu verändern. Damit umgehen Sie die Problematik mit dem Vor- oder Nachdatieren.
Auch mit Textmarkenfeldern und Textformularfeldern gelingen mit vernünftigem Aufwand nur sehr einfache Datumsberechnungen. Dieselbe Aussage trifft auf Datumseingaben in Tabellenzellen zu. Sobald zwei unterschiedliche Monate bei der Berechnung involviert sind, hört es mit der Herrlichkeit bereits auf. Die Berechnung liefert dann nämlich unsinnige Ergebnisse. Für komplexere Berechnungen in diesem Bereich, sollten Sie deshalb auf VBA (Makros) zurückgreifen, um vom Komfort und der konsistenten Syntax einer veritablen Programmiersprache, profitieren zu können. Dieses Skript zeigt auf, wie Sie dabei vorgehen können.
Tippen Sie die geschweiften Klammern nicht konventionell ein, das bringt gar nichts! Sie können die besprochenen Feldfunktionen wie folgt in das Word-Dokument einbringen:
Betätigen Sie die Tastenkombination [Alt + F9] um die Ansicht der Feldcodes zu aktivieren.
Generieren Sie das Gerüst für den neuen Feldcode, indem Sie die Tastenkombination [Strg + F9] betätigen. Sie sollten nun ein Pärchen geschweifter Klammern sehen. Wiederholen Sie diesen Vorgang allenfalls, wenn die Feldfunktion verschachtelt ist.
Schreiben Sie nun in die generierten Klammern die Gleichung, wie in diesem Skript abgebildet.
Betätigen Sie abermals die Tastenkombination [Alt + F9] um den Feldcode auszublenden und in die Normalansicht der Felder zurückzukehren, damit Sie das Resultat der Feldfunktion würdigen können.
Bei jeder Änderung des Feldcodes, müssen Sie die Feldfunktion markieren und die Taste [F9] betätigen, damit sich das Resultat der Feldfunktion aktualisiert.
Sollten Sie noch wenig Übung haben, was das Eingeben von komplexen Feldfunktionen direkt in ein Word-Dokument betrifft, dann lesen Sie bitte zuerst das folgende Kapitel: Wie gibt man eine Feldfunktion in MS Word ein?
Folgender Feldcode berechnet das Datum: Heute in zwei Jahre. Die Schaltjahrproblematik bleibt dabei unberücksichtigt. Statt der Feldfunktion TIME (bzw. ZEIT) könnten Sie 1:1 auch die Feldfunktion DATE (bzw. AKTUALDAT in Word 97) einsetzen.
{ TIME \@ "d.M." }{ = { TIME \@ "yyyy" } + 2}
{ ZEIT \@ "t.M." }{ = { ZEIT \@ "jjjj" } + 2}
Folgender Feldcode berechnet das Datum des letzen Monats (ohne Tag). Mit dieser Funktion könnten Sie einen sich dynamisch anpassenden Brief im folgenden Sinne schreiben: Heute ist der 1. Februar 1999. Ich erlaube mir, Ihnen die Abrechnung für den Monat Januar 1999 zukommen zu lassen. Falls Sie ausschliesslich den Monat (ohne Jahr) anzeigen wollen, dann ersetzen Sie am Ende des Codes MMMM yyyy (bzw. MMMM jjjj) mit MMMM.
{ SET RP20030806 { IF { TIME \@ "M" } = 1 "1/12/{ = { TIME \@ "yyyy" } - 1 }" "1/{ = { TIME \@ "M" } - 1}/{ TIME \@ "yyyy" }" } }{ REF RP20030806 \@ "MMMM yyyy"}
{ BESTIMMEN RP20030806 { WENN { ZEIT \@ "M" } = 1 "1/12/{ = { ZEIT \@ "jjjj" } - 1 }" "1/{ = { ZEIT \@ "M" } - 1}/{ ZEIT \@ "jjjj" }" } }{ REF RP20030806 \@ "MMMM jjjj"}
Wie schon in der Einleitung erwähnt wurde, gibt es eine Person, welche genug Können und Geduld aufgebracht hat, den Feldcode für eine Funktion heraus zu tüfteln, welche es erlaubt bis zu 28 Tagen zum aktuellen Datum hinzuzurechnen. Mehr noch, diese Person hat auch eine komfortable Oberfläche entwickelt, welche diesen Feldcode, automatisch generiert. Neben der Verzögerung in Tagen, können Sie auch das Format, in dem das mittels Feldfunktion errechnete Datum erscheinen soll, bestimmen.
Damit Sie sich ein Bild davon machen können, was Chris da geleistet hat: der Feldcode für eine Verzögerung von 14 Tagen sieht ab MS Word2000 wie folgt aus:
{ QUOTE { SET Delay "14" }{ SET "NextPrevMonth" { IF { DATE \@ "MM" } = 12 "1/97" "{ = { DATE \@ "MM" } + 1 }/97" } }{ SET "DaysInMonth" { IF { DATE \@ "MM" } <> 2 { = INT(30575*{ DATE \@ "MM" }/1000+1/2)-INT(30575*{ = { DATE \@ "MM" } - 1 }/1000+1/2) }{ IF { = INT({ DATE \@"yy" }/4 ) } = { = { DATE \@"yy" }/4 }"29" "28" } } }{ IF { = { DATE \@ "dd" } + { REF Delay } } <= { DaysInMonth }{ QUOTE { = { DATE \@ "dd" } + { REF Delay } \# "0" }" - "{ DATE \@ "MMM" }" - "{ DATE \@ "yy" }"" }{ QUOTE { = { DATE \@ "dd" } + { REF Delay } - { DaysInMonth } \# "0" }" - "{ NextPrevMonth \@ "MMM" }" - "{ IF { DATE \@ "MM" } <> 12 { DATE \@ "yy" }{ = { DATE \@ "yyyy" } + 1 \# "xx" } }"" } } }
Sie können die Lösung von Chris über den folgenden Link herunter laden:
Die Implementierung gestaltet sich einfach. Gehen Sie konkret wie folgt vor:
Laden Sie die ZIP-Datei herunter, welche Sie auf der genannten Webseite vorfinden.
Entzippen Sie die heruntergeladene Datei. Sie enthält eine einzige Datei mit dem Namen DelayDat.dot.
Führen Sie einen Doppelklick auf diese Dokumentenvorlage aus.
Starten Sie über die Symbolleiste, welche angezeigt wird, den Dialog.
Bestimmen Sie wie viele Tage dazu gerechnet werden sollen und geben Sie das Format vor, indem das Datum angezeigt werden soll.
Wenn Sie nun die Schaltfläche Ok betätigen, wird die Feldfunktion bei der Einfügemarke im Dokument eingefügt.
Falls Sie oft solche Feldcodes erstellen möchten, dann kopieren Sie die DelayDat.dot doch einfach in das AutoStart-Verzeichnis von MS Word (Extras - Optionen... -Speicherort für Dateien - AutoStart), damit das Tool immer zur Verfügung steht.
Wesentlich flexibler und auch konsistenter kriegen Sie diese Sache in den Griff, wenn Sie VBA-Code einsetzen. Optional können Sie den VBA-Code nachdem Sie ihn getestet habe, in einem Prozess mit dem reservierten Name AutoOpen bzw. AutoNew einsetzen. Damit steht einer automatisierten Lösung eigentlich nichts mehr im Wege. Wie Sie dabei vorgehen müssen, erfahren Sie im folgenden Kapitel: Allgemeine Felder (Platzhalter).
Im Folgenden finden Sie den Feldcode, welcher benötigt wird, um in den Monaten Januar - März den Wert 1, zwischen April - Juni den Wert 2, zwischen Juli - September den Wert 3 und in den Monaten Oktober - Dezember den Wert 4, anzeigen zu lassen:
{ IF { TIME \@ "M" } < 4 "1" "{ IF { TIME \@ "M" } < 7 "2" "{ IF { TIME \@ "M" } < 10 "3" "4" }" }" }
{ WENN { ZEIT \@ "M" } < 4 "1" "{ WENN { ZEIT \@ "M" } < 7 "2" "{ WENN { ZEIT \@ "M" } < 10 "3" "4" }" }" }
Folgender Feldcode stammt von Thomas Rauner und hat denselben Effekt, wie das oben gezeigte:
{ =INT(({ Date \@ "M" } -1) /3) + 1 \#"0" }
Ersetzen Sie in Word 97 =INT mit =Ganzzahl und Date mit AktualDat.
Da das Einbringen der Feldfunktion, wegen der nötigen Verschachtelungen etwas kniffelig sein könnte, hier der VBA-Code, welche diese Feldfunktion an der Stelle der Einfügemarke in das Dokument oder in die Dokumentvorlage einbringt.
Sub QuartalFeldcodeEinbringen() Q = Chr(34) Dim oRange As Range, F1 As Field Set oRange = ActiveDocument.Range(Selection.Start, Selection.End) For i = 1 To 3 Set F1 = ActiveDocument.Fields.Add(oRange, wdFieldIf, "", False) oRange.SetRange F1.Code.End - 1, F1.Code.End - 1 ActiveDocument.Fields.Add oRange, wdFieldTime, "\@ " & Q & "M" & Q, False If i > 1 Then F1.Select: Selection.InsertAfter Q End If oRange.SetRange F1.Code.End - 1, F1.Code.End - 1 oRange.InsertAfter " < " & i * 3 + 1 & " " & Q & i & Q & " " & Q oRange.SetRange F1.Code.End - 1, F1.Code.End - 1 Next i oRange.InsertAfter "4" & Q: Selection.Collapse: F1.Update End Sub
In diesem Kapitel wird erklärt, wie Sie das Ergebnis einer Berechnung, welche mit einem Makro (VBA) erreicht wurde, an geeigneter Stelle in das Dokument einbringen können. Am einfachsten wäre dies mit der Selection.TypeText Methode zu erreichen. Diese Methode setzt dann das Ergebnis dort ein, wo sich die Einfügemarke befindet. Ein lauffähiges Makro würde dann also wie folgt aussehen:
Sub Vordatieren() Dim d As Date d = Date + 14 Selection.TypeText Text:=Format(d, "d. MMMM. yyyy") End Sub
Wenn das Dokument (bzw. die Dokumentvorlage) eine Art Maske darstellen soll, ist diese Methode allerdings nicht der Weisheit letzter Schluss. Viel mehr sind dann Platzhalter gefragt, welche Sie zur Entwurfszeit in die Dokumentvorlage einbringen können. Diese Platzhalter sollen dann später, z.B. wenn ein neues Dokument auf Basis der Vorlage erstellt wird, mit dem Ergebnis der VBA-Funktion versorgt werden.
Dazu eigenen sich Textmarkenfelder, welche in diesem Skript auch noch besprochen werden aber insbesondere auch Dokumentvariable oder Dokumenteigenschaften.
Dieses Kapitel beschränkt sich vorerst auf Dokumentvariable. Der Grund, wieso Dokumenteigenschaften in diesem Skript ebenfalls ins Spiel kommen werden, ist in einem Fehlverhalten von Word 97 zu suchen. Word 97 schmiert Ihnen nämlich reproduzierbar ab, sollten Sie versuchen, einen Verweis auf eine Dokumentvariable, statt in den Hauptteil des Dokumentes, in die Kopf-/Fusszeile oder in ein Textfeld einzubringen. Erst mit Word2000 wurde dieses krasse Fehlverhalten von Microsoft beseitigt.
Sollten Sie Word 97 benutzen und beabsichtigen Platzhalter ausserhalb des Hauptteils des Dokumentes einzusetzen, dann lesen Sie auch das Kapitel: Ausweichlösung mit einer benutzerdefinierten Dokumenteigenschaft. Die Mimik, welche eine Dokumenteigenschaft einsetzt, ähnelt der folgenden Lösung, welche eine Dokumentvariable benutzt sehr. Erwarten Sie deshalb nicht, dass im genannten Kapitel nochmals alles so ausführlich wie gleich anschliessend erklärt wird.
Sie können in Ihrem Dokument oder in Ihrer Dokumentvorlage eine beliebige Anzahl von Dokumentvariablen definieren. Tun Sie dies in der Dokumentvorlage, dann werden diese Variablen an die Dokumente, welche auf Basis dieser Vorlage erstellt werden, vererbt. Im weiteren brauchen Sie einen Platzhalter im Dokument oder in der Dokumentvorlage, welcher auf die Dokumentvariable verweist und schliesslich benötigen Sie noch ein Stück VBA-Code, welcher einerseits die Berechnung durchführt und andererseits diese Dokumentvariable versorgt.
Ich versuche diesen etwas komplexen, aber sehr flexibel einsetzbaren Mechanismus zusammenzufassen:
Sie fügen der Dokumentvorlage eine Dokumentvariable hinzu. Dabei handelt es sich vorerst einzig um eine Definition. Diese Variable wird beim Vorgang Datei - Neu... an das neu generierte Dokument vererbt. Das Definieren der Dokumentvariablen geschieht in der Regel genau einmal und ist nur unter Einsatz von VBA möglich. Wir sind nun so weit, dass die Dokumentvorlage (und später deren Kinder), von dieser Variable Kenntnis hat/haben. Sehen tun Sie noch nichts, es handelt sich lediglich um eine Veranlagung.
Nun fügen Sie der Dokumentvorlage an geeigneter Stelle einen Platzhalter (man kann auch sagen eine Referenz auf diese Dokumentvariable) hinzu. Auch dieser Platzhalter wird an das künftige Dokument vererbt. Auch dieser Vorgang geschieht in der Regel genau einmal und zwar indem Sie über Einfügen - Feld... der Dokumentvorlage das Feld mit der Bezeichnung DocVariable hinzufügen. Damit wird bestimmt, an welcher Stelle im Dokument die Früchte unserer Bemühungen erscheinen sollen. Nun müssen wir aber auch noch liefern.
Nun wird eine VBA-Routine (Makro), welche die Berechnung durchführt und die Dokumentvariable mit dem Ergebnis versorgt, angestossen. Damit der Platzhalter das (neue) Ergebnis der Dokumentvariablen auch annimmt, muss weiter sichergestellt werden, dass die Feldfunktion aktualisiert wird. Der Anstoss zu diesen beiden Vorgängen kann (bzw. soll) automatisch geschehen; typischerweise zum Zeitpunkt wenn ein neues Dokument generiert wird oder wenn ein bestehendes Dokument geöffnet wird. Im Gegensatz zu den ersten beiden Vorgängen ist dies ein sich ständig wiederholender Prozess, also eine dynamischer Angelegenheit. Dies ist auch das Ziel, denn wenn ich vierzehn Tage vom aktuellen Datum hinzu zähle, dann erhalte ich morgen schon ein anders Ergebnis als heute.
Mit diesen drei Schritten, haben wir genau das erreicht, was eine von MS Word intern zur Verfügung gestellte Feldfunktion auch tut. Der Unterschied liegt also beim Lösungsweg. Bei einer Word-integrierten Feldfunktion fallen die Schritte eins und drei weg.
Es scheint, dass ab Word2000 auf den ersten Schritt verzichtet werden kann. Sollte die nominierte Dokumentvariable noch nicht existieren, wird diese bei der Zuweisung eines Wertes implizit erstellt.
In diesem Szenario geht es um folgendes: Es soll eine benutzerdefinierte Feldfunktion implementiert werden, welche das Datum Heute in 14 Tagen berechnet und das Resultat dynamisch im Dokument anzeigen kann.
Wie Im Vorspann bereits erwähnt wurde, ist das Definieren einer Dokumentvariablen ausschliesslich unter Einsatz von VBA möglich. Dabei handelt es sich aber um keine grosse Sache. Zuerst einigen wir uns auf den Namen für diese Variable und nennen sie Verfallsdatum. Konkret gehen Sie wie folgt vor:
Öffnen Sie Ihre Dokumentvorlage explizit über den Dialog Datei - Öffnen....
Wechseln Sie mit der Tastenkombination [Alt+F11] in die VBA-Umgebung.
Im VBA-Editor wählen Sie ausgehend vom Hauptmenü Einfügen - Modul.
Kopieren Sie den nachfolgenden drei-Zeiler in das leere Codefenster.
Führen Sie das Makro mit dem Namen DokVariableHinzfuegen einmal aus.
Sub DokVariableHinzfuegen() ActiveDocument.Variables.Add Name:="Verfallsdatum" End Sub
Das Hinzufügen einer Dokumentvariablen hinterlässt keine sichtbaren Spuren. Wenn Sie überprüfen wollen, ob es geklappt hat, rufen Sie das Makro gleich nochmals auf, dann erfolgt nämlich eine Fehlermeldung, welche besagt, dass das Ding bereits vorhanden ist. Sie erkennen daran, dass dieser Aufruf nur einmal im Leben der Vorlage geschehen muss bzw. geschehen soll.
Die nächste Aktion, dürfte Ihnen geläufig sein. Sie sollen eine Feldfunktion an geeigneter Stelle (also an der Stelle in der Vorlage, an der das Verfallsdatum erscheinen soll) einbringen. Gehen Sie dabei im Einzelnen wie folgt vor:
Setzen Sie in der Dokumentvorlage die Einfügemarke an den Ort, an der das Verfallsdatum erscheinen soll.
Ausgehend vom Word-Hauptmenü wählen Sie den Dialog Einfügen - Feld....
Im Folgedialog markieren Sie in der linken Liste mit der Bezeichnung Kategorien: den Eintrag Dokumentautomation.
In der rechten Liste mit der Bezeichnung Feldnamen: markieren Sie den Eintrag DokVariable falls Sie noch mit Word 97 arbeiten oder aber DocVariable ab Word2000.
Als nächstes müssen Sie das Textfeld in diesem Dialog ergänzen. Setzen Sie also die Einfügemarke hinter den Feldnamen DokVariable (bzw. DocVariable), achten Sie darauf, dass ein Leerschlag vorhanden ist und tippen Sie "Verfallsdatum" und zwar inklusive Gänsefüsschen. Der ganze Ausdruck sieht nun also wie folgt aus: DokVariable "Verfallsdatum" Word 97 bzw. DocVariable "Verfallsdatum" ab Word2000.
Schliessen Sie nun den Dialog mit Ok ab und sichern Sie Ihre Dokumentvorlage.
Schliesslich folgt nun noch der VBA-Code, welcher die Dokumentvariable mit dem Ergebnis beliefert und auch dafür sorgt, dass die Feldfunktion aktualisiert wird.
Da das Starten des Makros automatisch erfolgen soll, verwenden wir die reservierten Namen AutoNew und AutoOpen. Damit ist sichergestellt, dass der Prozess automatisch anläuft, wenn ein neues Dokument auf Grundlage unserer Dokumentvorlage erstellt wird, oder wenn ein Dokument, welches auf dieser Vorlage basiert, geöffnet wird.
Die Funktion, um dem aktuellen Datum 14 Tage hinzuzuzählen, ist recht einfach und erscheint im folgenden Beispiel als Unterfunktion. Konkret gehen Sie bitte wie folgt vor:
Öffnen Sie Ihre Dokumentvariable explizit über den Dialog Datei - Öffnen....
Wechseln Sie mit der Tastenkombination [Alt+F11] in die VBA-Umgebung.
Heil im VBA-Editor angekommen, wählen Sie ausgehend vom Hauptmenü Einfügen - Modul.
Kopieren Sie den folgenden Code in das leere Codefenster.
Sie können den Datumsschalter (im Beispiel d. MMMM yyyy) Ihren Bedürfnissen anpassen.
Speichern und schliessen Sie die Vorlage.
Erstellen Sie ein neues Dokument auf Basis dieser Dokumentvorlage.
Sub AutoNew() DokVariableVersorgen End Sub Sub AutoOpen() DokVariableVersorgen End Sub Private Sub DokVariableVersorgen() ActiveDocument.Variables("Verfallsdatum").Value = Vordatieren(14) ActiveDocument.Fields.Update End Sub Private Function Vordatieren(d As Integer) As String Dim t As Date t = Date + d Vordatieren = Format(t, "d. MMMM yyyy") End Function
Statt der Zahl 14, wie im Beispiel, kann selbstverständlich auch ein anderer Wert angegeben werden. Verwenden Sie eine negative integere Zahl um zurück zu datieren.
Sollte sich der Platzhalter (also die Feldfunktion DocVariable) nicht im Hauptteil des Dokumentes befinden, sondern in der Kopf-/Fusszeile oder in einem Textfeld, dann muss das Makro erweitert werden. In diesem Fall kommt folgender Code zum Zuge:
Sub AutoNew() DokVariableVersorgen End Sub Sub AutoOpen() DokVariableVersorgen End Sub Private Sub DokVariableVersorgen() ActiveDocument.Variables("Verfallsdatum").Value = Vordatieren(14) Dim oStory As Range, oField As Field For Each oStory In ActiveDocument.StoryRanges For Each oField In oStory.Fields If oField.Type = wdFieldDocVariable Then oField.Update Next While Not (oStory.NextStoryRange Is Nothing) Set oStory = oStory.NextStoryRange For Each oField In oStory.Fields If oField.Type = wdFieldDocVariable Then oField.Update Next Wend Next End Sub Private Function Vordatieren(d As Integer) As String Dim t As Date t = Date + d Vordatieren = Format(t, "d. MMMM yyyy") End Function
Genau genommen sind zwei Unterschiede gegenüber der einfacheren Lösung auszumachen:
Die erweiterte Routine aktualisiert den Platzhalter unabhängig davon, ob sich dieser im Hauptteil des Dokumentes befindet oder nicht.
Im Gegensatz zum ersten Beispiel sind ausschliesslich Felder vom Typ Dokumentvariable von der Aktualisierung betroffen.
Diese Variante kann nicht angewendet werden, falls Sie mit Word 97 arbeiten und den Platzhalter in die Kopf-/Fusszeile oder in ein Textfeld einfügen wollen. In diesem Fall müssen Sie auf folgende Mimik ausweichen: Ausweichlösung mit einer benutzerdefinierten Dokumenteigenschaft. Der Grund ist der, dass MS Word 97 reproduzierbar abschmiert, falls Sie ein DokVariable-Feld in eine der genannten Dokumentteile einbringen wollen. (Great, Microsoft!)
Im zweiten Szenario geht es um folgendes: Es soll eine benutzerdefinierte Feldfunktion implementiert werden, welche die aktuelle Kalenderwoche berechnet und das Resultat dynamisch im Dokument anzeigen kann.
Grundsätzlich müssen dieselben Schritte wie bereits im ersten Szenario durchgespielt werden; nur der Teil im Makro, welcher die Berechnung durchführt, unterscheidet sich selbstverständlich vom ersten Beispiel. Hier nochmals in Kürze, was von Ihnen erwartet wird:
Öffnen Sie Ihre Dokumentvorlage explizit über den Dialog Datei - Öffnen....
Wechseln Sie mit der Tastenkombination [Alt+F11] in die VBA-Umgebung.
Heil im VBA-Editor angekommen, wählen Sie ausgehend vom Hauptmenü Einfügen - Modul.
Kopieren Sie den nachfolgenden VBA-Code in das leere Codefenster.
Führen Sie das Makro mit dem Namen DokVariableKalenderwochHinzfuegen einmal aus.
Setzen Sie in der Dokumentvorlage die Einfügemarke an den Ort, an der die Kalenderwoche erscheinen soll.
Ausgehend vom Word-Hauptmenü wählen Sie den Dialog Einfügen - Feld....
Im Folgedialog markieren Sie in der linken Liste mit der Bezeichnung Kategorien: den Eintrag Dokumentautomation.
In der rechten Liste mit der Bezeichnung Feldnamen: markieren Sie den Eintrag DokVariable falls Sie noch mit Word 97 arbeiten oder aber DocVariable ab Word2000.
Als nächstes müssen Sie das Textfeld in diesem Dialog ergänzen. Setzen Sie also die Einfügemarke hinter den Feldnamen DokVariable (bzw. DocVariable), achten Sie darauf, dass ein Leerschlag vorhanden ist und tippen Sie "Kalenderwoche" und zwar inklusive Gänsefüsschen. Der ganze Ausdruck sieht nun also wie folgt aus: DokVariable "Kalenderwoche" Word 97 bzw. DocVariable "Kalenderwoche" ab Word2000.
Schliessen Sie nun den Dialog mit Ok ab.
Speichern und schliessen Sie die Vorlage.
Erstellen Sie ein neues Dokument auf Basis dieser Dokumentvorlage.
Sub DokVariableKalenderwochHinzfuegen() ActiveDocument.Variables.Add Name:="Kalenderwoche" End Sub Sub AutoNew() DokVariableVersorgen End Sub Sub AutoOpen() DokVariableVersorgen End Sub Private Sub DokVariableVersorgen() ActiveDocument.Variables("Kalenderwoche").Value = KWoche(Date) Dim oStory As Range, oField As Field For Each oStory In ActiveDocument.StoryRanges For Each oField In oStory.Fields If oField.Type = wdFieldDocVariable Then oField.Update Next While Not (oStory.NextStoryRange Is Nothing) Set oStory = oStory.NextStoryRange For Each oField In oStory.Fields If oField.Type = wdFieldDocVariable Then oField.Update Next Wend Next End Sub Private Function KWoche(d As Date) As Integer 'Kalenderwochen nach DIN 1355 'Algorithmus von Christoph Kremer, Aachen Dim t& t = DateSerial(Year(d + (8 - Weekday(d)) Mod 7 - 3), 1, 1) KWoche = (d - t - 3 + (Weekday(t) + 1) Mod 7) \ 7 + 1 End Function
Diese Variante kann nicht angewendet werden, falls Sie mit Word 97 arbeiten und den Platzhalter in die Kopf-/Fusszeile oder in ein Textfeld einfügen wollen. In diesem Fall müssen Sie auf folgende Mimik ausweichen: Ausweichlösung mit einer benutzerdefinierten Dokumenteigenschaft. Der Grund ist der, dass MS Word 97 reproduzierbar abschmiert, falls Sie ein DokVariable-Feld in eine der genannten Dokumentteile einbringen wollen. (Great, Microsoft!)
Satt Dokumentvariable können auch benutzerdefinierte Dokumenteigenschaften, welche ein sehr ähnliches Ziel verfolgen, zum Einsatz kommen. Die Mimik unterscheidet sich dabei kaum von der Technik, welche auf Dokumentvariable setzt.
Arbeiten Sie mit benutzerdefinierten Dokumenteigenschaften, falls folgende beiden Bedingungen zutreffen:
Sie Arbeiten mit Word 97 oder in einem heterogenen Umfeld, wo Word 97 auch noch zum Einsatz kommt.
Sie möchten die benutzerdefinierte Feldfunktion ausserhalb des Hauptteils des Dokumentes, also zum Beispiel in der Kopf-/Fusszeile oder in einem Textfeld unterbringen.
Der Grund, wieso Sie in diesem Fall auf Dokumenteigenschaften ausweichen müssen ist der, dass MS Word 97 reproduzierbar abschmiert, falls Sie ein DokVariable-Feld in einen der genannten Dokumentteile einbringen wollen.
Der folgende Code arbeitet mit einer Dokumenteigenschaft, welche die Bezeichnung Verfallsdatum trägt.
Statt der im Code vorgehobenen Zahl 14, kann auch eine andere integere Zahl angegeben werden, auch eine negative, wenn statt vordatiert, zurück datiert werden soll.
Das Format des Datums - im Beispiel d. MMMM yyyy - kann selbstverständlich verändert werden.
Sollte die benötigte Dokumenteigenschaft mit der Bezeichnung Verfallsdatum noch nicht existieren, wird diese durch den folgenden Code erstellt. Im Gegensatz zu Dokumentvariablen können benutzerdefinierte Dokumenteigenschaften, alternativ, auch ohne VBA-Code erstellt werden. Dazu rufen Sie den Dialog Datei - Eigenschaften - Anpassen auf. Dieser Schritt ist allerdings fakultativ.
Sollten Sie die Referenz auf die Dokumenteigenschaft (Feld DocProperty) erstellen, bevor die Dokumenteigenschaft bekannt ist, dann wird die Feldfunktion folgende Meldung ausgeben: Fehler: Unbekannter Name für Dokument-Eigenschaft! Dabei handelt es sich vorerst um eine normale Erscheinung, welche ignoriert werden kann.
Die Prozesse AutoNew und AutoOpen werden automatisch ausgeführt, wenn ein neues Dokument auf Basis der fraglichen Dokumentvorlage erstellt bzw. geöffnet wird.
Wenn Sie ausgehend vom Word-Hauptmenü den Dialog Einfügen - Feld... aufrufen, dann verwenden Sie an der Stelle einer Dokumentvariablen die Feldfunktion Dokumentinformation - DokEigenschaft Word 97 bzw. DocProperty ab Word2000. Das übrige Vorgehen bleibt genau so, wie dies im Kapitel Szenario I - Benutzerdefinierte Feldfunktion: Heute in 14 Tage bereits beschrieben wurde.
Nachfolgend finden Sie den gesamten Code für die Funktion heute in 14 Tagen, falls Sie statt einer Dokumentvariablen eine benutzerdefinierte Dokumenteigenschaft einsetzen wollen oder müssen:
Sub AutoNew() DokEigenschaftVersorgen End Sub Sub AutoOpen() DokEigenschaftVersorgen End Sub Private Sub DokEigenschaftVersorgen() CheckExistance "Verfallsdatum" ActiveDocument.CustomDocumentProperties("Verfallsdatum").Value = Vordatieren(14) Dim oStory As Range, oField As Field For Each oStory In ActiveDocument.StoryRanges For Each oField In oStory.Fields If oField.Type = wdFieldDocProperty Then oField.Update Next While Not (oStory.NextStoryRange Is Nothing) Set oStory = oStory.NextStoryRange For Each oField In oStory.Fields If oField.Type = wdFieldDocProperty Then oField.Update Next Wend Next End Sub Private Function CheckExistance(vName As String) Dim v As DocumentProperty Flag = False For Each v In ActiveDocument.CustomDocumentProperties If v.Name = vName Then Flag = True Exit For End If Next If Flag = False Then ActiveDocument.CustomDocumentProperties.Add Name:=vName, _ LinkToContent:=False, Type:=msoPropertyTypeString, Value:=0 End If End Function Private Function Vordatieren(d As Integer) As String Dim t As Date t = Date + d Vordatieren = Format(t, "d. MMMM yyyy") End Function
Satt Dokumentvariable können auch benutzerdefinierte Dokumenteigenschaften, welche ein sehr ähnliches Ziel verfolgen, zum Einsatz kommen. Die Mimik unterscheidet sich dabei kaum von der Technik, welche auf Dokumentvariable setzt.
Arbeiten Sie mit benutzerdefinierten Dokumenteigenschaften, falls folgende beiden Bedingungen zutreffen:
Sie Arbeiten mit Word 97 oder in einem heterogenen Umfeld, wo Word 97 auch noch zum Einsatz kommt.
Sie möchten die benutzerdefinierte Feldfunktion ausserhalb des Hauptteils des Dokumentes, also zum Beispiel in der Kopf-/Fusszeile oder in einem Textfeld unterbringen.
Der Grund, wieso Sie in diesem Fall auf Dokumenteigenschaften ausweichen müssen ist der, dass MS Word 97 reproduzierbar abschmiert, falls Sie ein DokVariable-Feld in einen der genannten Dokumentteile einbringen wollen.
Der folgende Code arbeitet mit einer Dokumenteigenschaft, welche die Bezeichnung Kalenderwoche trägt.
Sollte die benötigte Dokumenteigenschaft mit der Bezeichnung Kalenderwoche noch nicht existieren, wird diese durch den folgenden Code erstellt. Im Gegensatz zu Dokumentvariablen können benutzerdefinierte Dokumenteigenschaften, alternativ, auch ohne VBA-Code erstellt werden. Dazu rufen Sie den Dialog Datei - Eigenschaften - Anpassen auf. Dieser Schritt ist allerdings fakultativ.
Sollten Sie die Referenz auf die Dokumenteigenschaft (Feld DocProperty) erstellen, bevor die Dokumenteigenschaft bekannt ist, dann wird die Feldfunktion folgende Meldung ausgeben: Fehler: Unbekannter Name für Dokument-Eigenschaft! Dabei handelt es sich vorerst um eine normale Erscheinung, welche ignoriert werden kann. Wie bereits erwähnt, wird die Dokumenteigenschaft von der AutoNew bzw. der AutoOpen Prozedur automatisch erstellt.
Die Prozesse AutoNew und AutoOpen werden automatisch ausgeführt, wenn ein neues Dokument auf Basis der fraglichen Dokumentvorlage erstellt bzw. geöffnet wird.
Wenn Sie ausgehend vom Word-Hauptmenü den Dialog Einfügen - Feld... aufrufen, dann verwenden Sie an der Stelle einer Dokumentvariablen die Feldfunktion Dokumentinformation - DokEigenschaft Word 97 bzw. DocProperty ab Word2000. Das übrige Vorgehen bleibt genau so, wie dies im Kapitel Szenario II - Benutzerdefinierte Feldfunktion: Kalenderwoche bereits beschrieben wurde.
Nachfolgend finden Sie den gesamten Code für die Funktion Kalenderwoche, falls Sie statt einer Dokumentvariablen eine benutzerdefinierte Dokumenteigenschaft einsetzen wollen oder müssen:
Sub AutoNew() DokEigenschaftVersorgen End Sub Sub AutoOpen() DokEigenschaftVersorgen End Sub Private Sub DokEigenschaftVersorgen() CheckExistance "Kalenderwoche" ActiveDocument.CustomDocumentProperties("Kalenderwoche").Value = KWoche(Date) Dim oStory As Range, oField As Field For Each oStory In ActiveDocument.StoryRanges For Each oField In oStory.Fields If oField.Type = wdFieldDocProperty Then oField.Update Next While Not (oStory.NextStoryRange Is Nothing) Set oStory = oStory.NextStoryRange For Each oField In oStory.Fields If oField.Type = wdFieldDocProperty Then oField.Update Next Wend Next End Sub Private Function CheckExistance(vName As String) Dim v As DocumentProperty Flag = False For Each v In ActiveDocument.CustomDocumentProperties If v.Name = vName Then Flag = True Exit For End If Next If Flag = False Then ActiveDocument.CustomDocumentProperties.Add Name:=vName, _ LinkToContent:=False, Type:=msoPropertyTypeNumber, Value:=0 End If End Function Private Function KWoche(d As Date) As Integer 'Kalenderwochen nach DIN 1355 'Algorithmus von Christoph Kremer, Aachen Dim t& t = DateSerial(Year(d + (8 - Weekday(d)) Mod 7 - 3), 1, 1) KWoche = (d - t - 3 + (Weekday(t) + 1) Mod 7) \ 7 + 1 End Function
Um das folgende Makro zu testen, sollten Sie die folgenden Voraussetzungen schaffen:
Erstellen Sie ein neues Dokument.
Schreiben Sie ein gültiges Datum z.B. 11.03.2001 an eine beliebige Stelle im Dokument.
Markieren Sie dieses Datum.
Vom Word-Hauptmenü wählen Sie den Befehl Einfügen - Textmarke....
Vergeben Sie der Textmarke den Namen Eingegangen und klicken Sie Hinzufügen.
Schreiben Sie an einer anderen Stelle ein weiteres gültiges Datum z.B. 01.11.2002.
Markieren Sie auch dieses Datum.
Wählen Sie wiederum den Befehl Einfügen - Textmarke....
Vergeben Sie der Textmarke den Namen Erledigt und klicken Sie Hinzufügen.
Schreiben Sie an einer anderen Stelle im Dokument z.B. das Wort Ergebnis. Was Sie hier schreiben ist eigentlich unwichtig. Diese Textstelle wird vom Code nämlich eh überschrieben.
Markieren Sie dieses Wort.
Wählen Sie wiederum den Befehl Einfügen - Textmarke....
Vergeben Sie der Textmarke den Namen Dauer und klicken Sie Hinzufügen.
Kopieren Sie den folgenden Code in der VBA-Umgebung in ein neues Modul und lassen Sie ihn ausführen.
Sub TMTageBerechnenStatisch() Dim oDoc As Document Set oDoc = ActiveDocument Ein = oDoc.Bookmarks("Eingegangen").Range.Text Aus = oDoc.Bookmarks("Erledigt").Range.Text If IsDate(Ein) And IsDate(Aus) Then oDoc.Bookmarks("Dauer").Range.Font.Color = wdColorBlack oDoc.Bookmarks("Dauer").Range.Text = _ DateDiff("d", DateValue(Ein), DateValue(Aus)) Else oDoc.Bookmarks("Dauer").Range.Font.Color = wdColorRed oDoc.Bookmarks("Dauer").Range.Text = "Fehler!" End If End Sub
Um die Position von Textmarken zu sehen, sollten Sie die Option Textmarken im Word-Dialog Extras - Optionen... - Ansicht - Anzeigen aktivieren.
Die hier zur Verwendung kommenden Textmarken, sollten sowohl links wie rechts mit einer eckigen Klammer begrenzt sein. Sehen Sie nur linksbündig die Klammer, dann liegt es daran, dass Sie vor dem Einfügen der Textmarke keinen Text markiert haben.
Bitte beachten Sie, dass die Zielmarke mit der Bezeichnung Dauer mit der eben präsentierten Mimik überschrieben wird. An ihrer Stelle steht nach Ablauf des Makros die Datumsdifferenz in Tagen. Die Textmarke selbst, wird dadurch zerstört. Ein zweites Mal kann dieser Prozess also nicht mehr korrekt ablaufen. Möchten Sie dieses Phänomen vermeiden, halten Sie sich bitte an das nun folgende Codebeispiel.
Sie dürfen auch andere gültige Datumsformate benutzen, als das Eingangs vorgeschlagene Format tt.mm.jjjj.
Sollte eine der involvierten Textmarken nicht existieren, dann bricht der Code mit einem Fehler ab. Deshalb ist es eine gute Praxis, eingangs zu prüfen, ob die Textmarken auch wirklich vorhanden sind. Sie erreichen diese mit einem Code wie der folgende:
Sub TMTageBerechnenStatisch2() If Not ActiveDocument.Bookmarks.Exists("Dauer") Then MsgBox "Die Textmarke mit dem Namen 'Dauer' existiert nicht." Exit Sub End If 'Weiterer Code End Sub
Das folgende Beispiel geht von den genau gleichen Voraussetzungen aus und verfolgt auch fast dasselbe Ziel, wie der eben diskutierte Code. Diesmal wird aber sichergestellt, dass die Zielmarke erhalten bleibt. Das Makro kann somit beliebig oft eingesetzt werden, falls entweder das Eingangs- oder das Erledigtdatum verändert wird.
Sub TMTageBerechnenDynamisch() Dim oDoc As Document, oRange As Range Set oDoc = ActiveDocument Ein = oDoc.Bookmarks("Eingegangen").Range.Text Aus = oDoc.Bookmarks("Erledigt").Range.Text Set oRange = oDoc.Bookmarks("Dauer").Range If IsDate(Ein) And IsDate(Aus) Then oRange.Text = DateDiff("d", DateValue(Ein), DateValue(Aus)) Else oRange.Text = "Fehler!" End If oDoc.Bookmarks.Add Name:="Dauer", Range:=oRange End Sub
Noch besser, als mit Textmarkenfelder, lässt es sich mit Textformularfelder rechnen, welche Sie über die Symbolleiste mit der Bezeichnung Formular in ein Dokument oder in eine Dokumentvorlage einbringen können.
Um das folgende Makro zu testen, sollten Sie die folgenden Voraussetzungen schaffen:
Erstellen Sie eine neue Dokumentenvorlage.
Kopieren Sie das unten abgebildete Makro mit dem Namen FFTageBerechnen im VBA-Editor in ein neues Modul.
Lassen Sie sich die Symbolleiste Formular anzeigen.
Fügen Sie an beliebiger Stelle ein Textformularfeld in das Dokument ein.
Führen Sie einen Doppelklick auf dieses Formularfeld aus, womit die Optionen des Formularfelds angezeigt werden.
Stellen Sie im Kombinationsfeld mit der Bezeichnung Typ den Wert Datum ein und passen Sie auf Wunsch auch das Datumsformat an.
Im selben Dialog unter Textmarke: sollten Sie dem Feld einen aussagekräftigen Namen vergeben. Nennen Sie das Feld CheckIn.
Im Kombinationsfeld mit der Bezeichnung Makro beim Verlassen Word 97 bzw. Makro ausführen bei Beenden ab Word2000 suchen Sie nun in der Liste nach dem Makro mit dem Namen FFTageBerechnen und stellen diesen Wert ein.
Aktivieren Sie schliesslich noch die Option Beim Verlassen berechnen.
Schliessen Sie den Dialog mit Ok ab.
Setzen Sie nun die Einfügemarke an eine andere Stelle im Dokument und fügen Sie dort ein weiteres Textformularfeld ein.
Führen Sie einen Doppelklick auf dieses Feld aus und profilieren Sie dieses Textformularfeld wie das erste, mit dem wichtigen Unterschied, dass dieses Feld CheckOut heissen soll. Auch dieses Feld soll die Exit-Prozedur mit dem Namen FFTageBerechnen aufrufen und vergessen Sie auch nicht die Option Beim Verlassen berechnen zu aktivieren.
Setzen Sie nun die Einfügemarke an eine andere Stelle im Dokument und fügen Sie dort ein weiteres Textformularfeld ein.
Führen Sie einen Doppelklick auf dieses Formularfeld aus und entfernen Sie im Eigenschaftsdialog (Optionen) das Häkchen bei Eingabe zulassen.
Nennen Sie dieses Feld Nächte und schliessen Sie den Dialog mit Ok ab.
Klicken Sie nun auf der kontextbezogenen Symbolleiste auf die Schaltfläche Formular schützen.
Speichern und Schliessen Sie die Vorlage.
Erstellen Sie ein neues Dokument auf der Basis dieser Vorlage und testen Sie Ihr Werk, in dem Sie in die ersten zwei Textformularfelder ein gültiges Datum eintragen.
Kopieren Sie die folgenden Makros in der VBA-Umgebung in ein neues Modul und zwar in die Dokumentvorlage, welche die Formularfelder enthält.
Sub FFTageBerechnen() Dim oDoc As Document Set oDoc = ActiveDocument CIn = oDoc.FormFields("CheckIn").Result COut = oDoc.FormFields("CheckOut").Result If IsDate(CIn) And IsDate(COut) Then _ oDoc.FormFields("Nächte").Result = DateDiff("d", DateValue(CIn), DateValue(COut)) End Sub
Bei den folgenden Beispielen wird aus zwei Zeitangaben die Differenz, ausgedrückt in Stunden und Minuten (erstes Beispiel) bzw. ausgedrückt in Stunden, Minuten und Sekunden (zweites Beispiel), berechnet. Diese Beispiele lehnen sich an das vorhergehende Exempel an. Diesmal lauten die Namen der Formularfelder allerdings anders, nämlich: Anfang, Ende und Arbeitszeit. Auch sollten Sie das Datumsformat der Formularfelder Anfang und Ende im Eigenschaftsdialog nach HH:mm bzw. HH:mm:ss anpassen. Zudem heissen die Feld-Exit-Prozeduren nun anders.
Sub FFSundenMinutenBerechnen() Dim oDoc As Document Set oDoc = ActiveDocument Ein = oDoc.FormFields("Anfang").Result Aus = oDoc.FormFields("Ende").Result If IsDate(Ein) And IsDate(Aus) Then mm = DateDiff("n", TimeValue(Ein), TimeValue(Aus)) hmm = Format(TimeSerial(0, mm, 0), "h:mm") oDoc.FormFields("Arbeitszeit").Result = hmm End If End Sub
Sub FFSundenMinutenSekundenBerechnen() Dim oDoc As Document Set oDoc = ActiveDocument Ein = oDoc.FormFields("Anfang").Result Aus = oDoc.FormFields("Ende").Result If IsDate(Ein) And IsDate(Aus) Then ss = DateDiff("s", TimeValue(Ein), TimeValue(Aus)) hmmss = Format(TimeSerial(0, 0, ss), "h:mm:ss") oDoc.FormFields("Arbeitszeit").Result = hmmss End If End Sub
Dank VBA lässt sich auch in Word-Tabellen ganz ordentlich rechnen, wenn vielleicht auch nicht ganz so Excellent. Das nachfolgende Beispiel sieht folgendes Szenario vor:
Erstellen Sie ein neues Word-Dokument.
Fügen Sie eine dreispaltige Tabelle in das Dokument ein.
Füllen Sie diese Tabelle etwa so aus, wie unten angezeigt.
Eingang Erledigt Dauer 11.11.97 24.12.99 14. Dezember 1999 23.01.00 02.05.26 02.05.27 11.11.11 12.11.11 Dauer Total
Fügen Sie nun den folgenden Code in der VBA-Umgebung in ein neues Modul und lassen Sie das Makro mit dem Namen TabelleTageBerechnen ablaufen. Dabei muss sich die Einfügemarke in der fraglichen Tabelle befinden. Testen Sie den Code zusätzlich, in dem Sie allenfalls die Werte in der Tabelle verändern oder auch zusätzliche, analoge Zeilen hinzufügen.
Die Teilergebnisse (Dauer) und das Grand Total (Dauer Total) wird jeweils in der dritten Spalte eingefügt. Das Ergebnis präsentiert sich somit etwa wie folgt:
Eingang Erledigt Dauer 11.11.97 24.12.99 773 14. Dezember 1999 23.01.00 40 02.05.93 02.05.94 365 11.11.2002 12.11.2002 1 Dauer Total 1179
Sie können die Datumsangaben in einem beliebigen, aber gültigen Format eingeben.
Das Grand Total wird immer in der letzten Tabellenzeile eingetragen und zwar in der dritten Spalte.
Möchten Sie nicht auf die erste Tabelle innerhalb der Markierung, sondern z.B. auf die erste Tabelle im Hauptteil des Dokumentes zielen, dann ersetzen Sie den Ausdruck Selection.Tables.Count mit ActiveDocument.Tables.Count und die Codezeile Set oTable = Selection.Tables(1) mit Set oTable = ActiveDocument.Tables(1).
Sub TabelleTageBerechnen() If Selection.Tables.Count = 0 Then MsgBox "Keine Tabelle in Sicht." End End If Dim oTable As Table Set oTable = Selection.Tables(1) If oTable.Columns.Count < 3 Or oTable.Rows.Count < 3 Then MsgBox "Die Tabelle sollte mindestens 3 Zeilen und 3 Spalten aufweisen." End End If s = 3 i = 0 For Each Zeile In oTable.Rows i = i + 1 If i > 1 And i < oTable.Rows.Count Then Ein = Left(Zeile.Cells(1), Len(Zeile.Cells(1)) - 2) Aus = Left(Zeile.Cells(2), Len(Zeile.Cells(2)) - 2) If Len(Ein) + Len(Aus) > 0 Then If IsDate(Ein) And IsDate(Aus) Then Zeile.Cells(s) = DateDiff("d", DateValue(Ein), DateValue(Aus)) Zeile.Cells(s).Range.Font.Color = wdColorBlack Else Zeile.Cells(s) = "Fehler!" Zeile.Cells(s).Range.Font.Color = wdColorRed End If Else Zeile.Cells(s) = "" Zeile.Cells(s).Range.Font.Color = wdColorBlack End If End If Next TotalTage = 0 i = 0 For Each Zelle In oTable.Columns(s).Cells i = i + 1 If i > 1 And i < oTable.Rows.Count Then Wert = Left(Zelle, Len(Zelle) - 2) If IsNumeric(Wert) Then TotalTage = TotalTage + Wert End If End If Next oTable.Rows(oTable.Rows.Count).Cells(s).Range.Text = TotalTage oTable.Rows(oTable.Rows.Count).Cells(s).Range.Font.Bold = True End Sub
Als Werktage gelten in der heutigen Zeit Tage, welche nicht auf einen Samstag oder Sonntag fallen und auch keine Feiertage darstellen.
Wogegen Samstage und Sonntag problemlos programmtechnisch eruiert werden können, ist dies bei Feiertagen nicht möglich. Zwar gibt es die berühmte Osterformel von C. F. Gauß, welche sich auch in VBA umsetzen liesse. Leider können damit aber nur jene beweglichen (und meist kirchlichen) Festtage berechnet werden, welche sich nach der Folge der Mondphasen richten. Andere, vor allem lokale Ruhetage, erfasst die Osterformel nicht und in einer weitgehend globalisierten Welt, muss eigentlich jeder Feiertag als lokal eingestuft werden.
Konkret bedeutet dies, dass Sie eine Liste unterhalten müssen, die alle Ruhetage aufzählt, welche nicht auf einen Samstag oder Sonntag fallen, damit im Programm ein Abgleich stattfinden kann.
MS Word ist bekanntlich keine Datenbankanwendung und die Möglichkeiten, intern eine Liste zu unterhalten sind damit eher begrenzt. Trotzdem lassen sich Orte finden, wo diese Liste abgelegt werden kann. Da sich meines Erachtens keiner dieser Orte als klar überlegen aufdrängt, finden Sie im Folgenden zwei Lösungsansätze, wobei, abhängig vom Ansatz, auf jeweils eine der folgenden Einrichtung bzw. Komponenten abstellt wird:
Eine AutoText-Kategorie, welche in der Dokumentvorlage erstellt und unterhalten wird.
Eine externe INI-Datei, welche sich im Programmverzeichnis von MS Office befinden sollte.
Um das folgende Beispiel zu testen, sind folgende Vorarbeiten angesagt:
Erstellen Sie eine neue Dokumentvorlage.
Erstellen Sie über Format - Formatvorlage... - Neu... eine neue Formatvorlage vom Typus Absatz mit dem Namen Feiertage.
Stellen Sie sicher, dass im Textteil der Dokumentvorlage die Formatvorlage, welche Sie eben erstellt haben, aktiv ist.
Tippen Sie z.B. 25/12/2005 in den Textteil der Dokumentvorlage ein und markieren Sie dieses Datum.
Ausgehend vom Word-Hauptmenü wählen Sie nun den Dialog Einfügen - AutoText - Neu....
Ändern Sie den vorgeschlagenen Namen des AutoText-Eintrages von 25/12/2005 nach Weihnachtstag2005 und klicken Sie Ok.
Wiederholen Sie die Schritte 4 - 6 für die Feiertage für die nächsten ca. 15 Monate in analoger Art und Weise. Festtage, welche eh auf einen Samstag oder Sonntag fallen, können Sie sich dabei schenken.
Wechseln Sie mit der Tastenkombination [Alt + F11] in den VBA-Editor.
Achten Sie darauf, dass im Projektexplorer Ihre ebenerstellte Dokumentvorlage markiert ist.
Wählen Sie nun ausgehend vom Hauptmenü Einfügen - UserForm.
Über die Werkzeugsammlung platzieren Sie folgende Komponenten auf dieser UserForm:
Drei Textfelder, welchen Sie im Eigenschaftsdialog unter (Name) die Bezeichnungen txtEntritt, txtAustritt bzw. txtWerktage vergeben.
Optional ein Listenfeld, welches die Bezeichnung lbFeiertage erhalten sollte.
Führen Sie nun einen Rechtsklick auf eine freie Stelle der UserForm aus und wählen Sie im Kontextmenü Code anzeigen.
Löschen Sie die beiden Zeilen, welche Ihnen MS Word automatisch generiert hat und kopieren Sie den unten abgebildeten VBA-Code in das nun leere Codefenster der UserForm.
Im Kopf des Codes können Sie auf Wunsch einige Konstanten an Ihre speziellen Bedürfnisse anpassen. Diese Konstanten haben die folgende Bedeutung:
Titel: Die Zeichenfolge, welche Sie hier definieren, erscheint transparent in der Titelleiste der UserForm.
FVName: Geben Sie hier den Namen der Formatvorlage an, unter welcher Sie die Feiertage aufgenommen haben.
KeinListenfeld: Weisen Sie dieser Konstante den Wert True zu, falls Sie kein Listenfeld mit der Bezeichnung lbFeiertage zur Abbildung der Feiertage auf der UserForm vorsehen.
MaxSpanne: Geben Sie hier die Periode (in Tage) an, für welche Sie die Feiertage als AutoText-Einträge aufgenommen haben.
Sie sind nun bereit, die UserForm aufzurufen und die Routine mit verschiedenen Datumswerten zu testen. Geben Sie dazu Datumsangaben z.B. im Format 22/05/2005 in die Textfelder ein.
'**** Start anpassbare Werte ***** Private Const Titel = "Werktage berechnen" Private Const FVName = "Feiertage" 'Name der Formatvorlage, unter welcher 'die AutoText-Einträge aufgenommen wurden Private Const KeinListenfeld = False 'True, wenn *kein* Listenfeld mit dem Namen '"lbFVName" zur Anzeige der Feiertage 'auf der UserForm vorhanden ist Private Const MaxSpanne = 366 'Spanne (in Tagen), 'für welche Feiertage definiert wurden '**** Ende Anpassbare Werte ***** Private FTListeN() As String Private FTListe() As String Private KeineFeiertage As Boolean Private Sub UserForm_Activate() Caption = Titel txtWerktage.Locked = True txtWerktage.TabStop = False txtWerktage.TextAlign = fmTextAlignRight FeiertageLaden If KeinListenfeld = False And KeineFeiertage = False Then ListenfeldLaden End Sub Private Sub txtEintritt_Exit(ByVal Cancel As MSForms.ReturnBoolean) If Not IsDate(txtEintritt.Text) Then Beep MsgBox "Das ist kein gültiges Datum." Cancel = True txtEintritt.SelStart = 0 txtEintritt.SelLength = txtEintritt.TextLength Else WerktageBerechnen End If End Sub Private Sub txtAustritt_Exit(ByVal Cancel As MSForms.ReturnBoolean) If Not IsDate(txtAustritt.Text) Then Beep MsgBox "Das ist kein gültiges Datum." Cancel = True txtAustritt.SelStart = 0 txtAustritt.SelLength = txtAustritt.TextLength Else WerktageBerechnen End If End Sub Private Sub WerktageBerechnen() Dim x As Date, y As Date, z As Integer, wt As Integer Anfang = txtEintritt.Text Ende = txtAustritt.Text If Not IsDate(Anfang) Or Not IsDate(Ende) Then Exit Sub x = Anfang y = Ende z = 1 'zählt die Tage für die max. Spanne While x <= y And z <= MaxSpanne flag = False If Weekday(x, vbMonday) > 5 Then flag = True 'wenn Samstag oder Sonntag If flag = False And KeineFeiertage = False Then 'wenn kein Sa. oder So. For i = 0 To UBound(FTListe) 'Abgleich gegen jedes Datum in der Liste If x = CDate(FTListe(i, 1)) Then flag = True 'Yep, das ist ein Feiertag Exit For 'Weitere Einträge sind jetzt irrelevant End If Next i End If If flag = False Then wt = wt + 1 'Weder Samstag, Sonntag noch Feiertag x = x + 1 'Nächster Tag z = z + 1 Wend If z >= MaxSpanne Then txtWerktage.Text = 0 MsgBox "Maximale Spanne von " & MaxSpanne & " Tagen überschritten!", _ vbExclamation, Titel Else txtWerktage.Text = wt End If End Sub Private Sub FeiertageLaden() Dim oAT As AutoTextEntry, tmp() As String i = -1 For Each oAT In ActiveDocument.AttachedTemplate.AutoTextEntries If oAT.StyleName = FVName Then 'Wenn die fragliche AutoText-Kategorie chk = oAT.Value If Right(chk, 1) = Chr(13) Then chk = Left(chk, Len(chk) - 1) 'strip ZS If Not IsDate(chk) Then MsgBox "Ungültiges Datum (" & chk & ") im AutoText-Eintrag " & oAT.Name, _ vbExclamation, Titel Else i = i + 1 ReDim Preserve tmp(i) tmp(i) = oAT.Name & Chr(1) & chk 'Name und Wert des AutoText-Eintrages End If End If Next If i = -1 Then KeineFeiertage = True 'Keine Feiertage definiert Exit Sub End If 'Jetzt, wo die Anzahl der Feiertage in der Liste bekannt ist... ReDim FTListe(i, 1) '...kann die Sache sauber strukturiert werden For i = 0 To UBound(FTListe) '1-dimensionaler Array nach 2-dimensionalen Array FTListe(i, 0) = Left(tmp(i), InStr(tmp(i), Chr(1)) - 1) 'Name des AT-Eintrages FTListe(i, 1) = Mid(tmp(i), InStr(tmp(i), Chr(1)) + 1) 'Wert des AT-Eintrages Next i End Sub Private Sub ListenfeldLaden() 'Diese Unterroutine ist nur dann nötig, wenn die Feiertage in einem Listenfeld 'dargestellt werden sollen lbFeiertage.TabStop = False: lbFeiertage.ColumnCount = 2 lbFeiertage.List = FTListe() End Sub
Um das folgende Beispiel zu testen, sind folgende Vorarbeiten angesagt:
Erstellen Sie in Notepad oder in einem vergleichbaren Tool eine Datei, welche die Feiertage für die ca. nächsten 15 Monate wie im Kapitel Die INI-Datei aufgezeigt, auflistet. Festtage, welche eh auf einen Samstag oder Sonntag fallen, können Sie sich dabei schenken.
Speichern Sie diese Datei im Programmverzeichnis von MS Office unter dem Namen Feiertage.ini ab. Beim angesprochenen Programmverzeichnis handelt es sich um jenen Ordner, in dem sich auch z.B. die winword.exe befindet.
Erstellen Sie nun eine neue Dokumentvorlage.
Wechseln Sie mit der Tastenkombination [Alt + F11] in den VBA-Editor.
Achten Sie darauf, dass im Projektexplorer Ihre ebenerstellte Dokumentvorlage markiert ist.
Wählen Sie nun ausgehend vom Hauptmenü Einfügen - UserForm.
Über die Werkzeugsammlung platzieren Sie folgende Komponenten auf dieser UserForm:
Drei Textfelder, welchen Sie im Eigenschaftsdialog unter (Name) die Bezeichnungen txtEntritt, txtAustritt bzw. txtWerktage vergeben.
Optional ein Listenfeld, welches die Bezeichnung lbFeiertage erhalten sollte.
Führen Sie nun einen Rechtsklick auf eine freie Stelle der UserForm aus und wählen Sie im Kontextmenü Code anzeigen.
Löschen Sie die beiden Zeilen, welche Ihnen MS Word automatisch generiert hat und kopieren Sie den unten abgebildeten VBA-Code in das nun leere Codefenster der UserForm.
Im Kopf des Codes können Sie auf Wunsch einige Konstanten an Ihre speziellen Bedürfnisse anpassen. Diese Konstanten haben die folgende Bedeutung:
Titel: Die Zeichenfolge, welche Sie hier definieren, erscheint transparent in der Titelleiste der UserForm.
INIDatei: Geben Sie hier den Dateiname der INI-Datei an, welche die Feiertage aufführt.
Kategorie: Geben Sie hier den Namen des Abschnittes ([...])an, in welchem die Feiertage in der INI-Datei aufgeführt sind.
KeinListenfeld: Weisen Sie dieser Konstante den Wert True zu, falls Sie kein Listenfeld mit der Bezeichnung lbFeiertage zur Abbildung der Feiertage auf der UserForm vorsehen.
MaxSpanne: Geben Sie hier die Periode (in Tage) an, für welche Sie die Feiertage in der INI-Datei aufgenommen haben.
Sie sind nun bereit, die UserForm aufzurufen und die Routine mit verschiedenen Datumswerten zu testen. Geben Sie dazu Datumsangaben z.B. im Format 22/05/2005 in die Textfelder ein.
Im Folgenden sehen Sie ein Beispiel, wie der Inhalt der INI-Datei aussehen sollte. Bedenken Sie dabei, dass die Nummerierung der Feiertage aufsteigend und lückenlos (1=..., 2=..., 3=..., etc.) sein muss. Es kann auf Wunsch auch eine anderes gültiges Datumsformat zur Anwendung kommen.
[Datumsangaben] 1=01/01/2004 2=02/01/2004 3=06/01/2004 4=01/03/2004 5=01/05/2004 6=01/08/2004 7=25/01/2004 8=26/01/2004 9=01/01/2005 10=02/01/2005
Finden Sie anschliessen den VBA-Code, welcher Sie in das Codefenster einer UserForm kopieren müssen. Welche Komponenten auf dieser UserForm enthalten sein müssen, wurde bereits erwähnt.
'**** Start anpassbare Werte ***** Private Const Titel = "Werktage berechnen" Private Const INIDatei = "Feiertage.ini" 'Name der INI-Datei, in welcher 'die Feiertage aufgeführt sind Private Const Kategorie = "Datumsangaben" 'Abschnitt der INI-Datei, in welcher 'die Feiertage aufgeführt sind Private Const KeinListenfeld = False 'True, wenn *kein* Listenfeld mit dem 'Namen "lbFeiertage" zur Anzeige der 'Feiertage auf der UserForm vorhanden ist Private Const MaxSpanne = 366 'Spanne (in Tagen), 'für welche Feiertage definiert wurden '**** Ende anpassbare Werte ***** Private FTListe() As String Private KeineFeiertage As Boolean Private Sub UserForm_Activate() Caption = Titel txtWerktage.Locked = True txtWerktage.TabStop = False txtWerktage.TextAlign = fmTextAlignRight FeiertageLaden If KeinListenfeld = False And KeineFeiertage = False Then ListenfeldLaden End Sub Private Sub txtEintritt_Exit(ByVal Cancel As MSForms.ReturnBoolean) If Not IsDate(txtEintritt.Text) Then Beep MsgBox "Das ist kein gültiges Datum." Cancel = True txtEintritt.SelStart = 0 txtEintritt.SelLength = txtEintritt.TextLength Else WerktageBerechnen End If End Sub Private Sub txtAustritt_Exit(ByVal Cancel As MSForms.ReturnBoolean) If Not IsDate(txtAustritt.Text) Then Beep MsgBox "Das ist kein gültiges Datum." Cancel = True txtAustritt.SelStart = 0 txtAustritt.SelLength = txtAustritt.TextLength Else WerktageBerechnen End If End Sub Private Sub WerktageBerechnen() Dim x As Date, y As Date, z As Integer, wt As Integer Anfang = txtEintritt.Text Ende = txtAustritt.Text If Not IsDate(Anfang) Or Not IsDate(Ende) Then Exit Sub x = Anfang y = Ende z = 1 'zählt die Tage für die max. Spanne While x <= y And z <= MaxSpanne flag = False If Weekday(x, vbMonday) > 5 Then flag = True 'wenn Samstag oder Sonntag If flag = False And KeineFeiertage = False Then 'wenn kein Sa. oder So. For i = 0 To UBound(FTListe) If x = CDate(FTListe(i)) Then flag = True 'Yep, das ist ein Feiertag Exit For 'weitere Einträge sind jetzt irrelevant End If Next i End If If flag = False Then wt = wt + 1 x = x + 1 'nächster Tag z = z + 1 Wend If z > MaxSpanne Then txtWerktage.Text = 0 MsgBox "Maximale Spanne von " & MaxSpanne & " Tagen überschritten", _ vbExclamation, Titel Else txtWerktage.Text = wt End If End Sub Private Sub FeiertageLaden() i = 1 j = -1 INIFile = Options.DefaultFilePath(wdProgramPath) & "\" & INIDatei If Dir(INIFile) = "" Then MsgBox "Die INI-Datei mit dem Namen " & INIFile & " existiert nicht.", _ vbExclamation, Titel KeineFeiertage = True End If tmp = System.PrivateProfileString(INIFile, Kategorie, CStr(i)) '1. Eintrag While Not tmp = "" 'solange bis Einträge vorhanden If Not IsDate(tmp) Then MsgBox "Ungültiges Datum (" & tmp & ") in INI-Datei " & INIFile, _ vbExclamation, Titel Else j = j + 1 ReDim Preserve FTListe(j) FTListe(j) = tmp 'Feiertag ins Datenfeld laden End If i = i + 1 tmp = System.PrivateProfileString(INIFile, Kategorie, CStr(i)) 'nächster E. Wend If j = -1 Then KeineFeiertage = True 'Keine Einträge erkannt End Sub Private Sub ListenfeldLaden() 'Diese Unterroutine ist nur dann nötig, wenn die Feiertage in einem Listenfeld 'dargestellt werden sollen lbFeiertage.TabStop = False lbFeiertage.List = FTListe() End Sub
http://mypage.bluewin.ch/reprobst/WordFAQ/DatBer.htm
René Probst, September 2004
15. November 2000 - Arbeit von Chris Woodman (Delayed Date Field Code Generator)
25. November 2000 - Arbeiten mit Dokumentvariablen
25. November 2000 - Kalenderwochefunktion
7. August 2003 - Generell überarbeitet.
16. Februar 2004 - Werktagsberechnungen
16. März 2007 - Thomas Rauner zeigt einen eleganteren Feldcode zur dynamischen Anzeige des Quartals auf.