Word bietet als Textverarbeitung ganz andere Möglichkeiten als Access-Berichte, wenn es um die Ausgabe von Texten geht. Natürlich liegen die Schwerpunkte bei Word auch ganz anders – so ist es grundsätzlich dafür ausgelegt, seine Texte nacheinander zu erfassen und nicht, wie etwa ein Access-Bericht, in einer durch die verschiedenen Berichtsbereiche vorgegebenen Struktur. Wir schauen uns in diesem Beitrag an, wie Sie Texte von Access nach Word bewegen und umgekehrt.
Im Gegensatz etwa zu Outlook ist Word eine Anwendung, von der man mehrere Instanzen erzeugen kann. Diese Erkenntnis ist wichtig, wenn Sie entscheiden, ob Sie für den VBA-gesteuerten Zugriff auf Word eine bestehende Instanz verwenden oder eine neue Instanz erstellen möchten. Dazu müssen Sie natürlich erst einmal herausfinden, ob auf dem Rechner bereits eine Word-Instanz geöffnet ist.
Da man grundsätzlich sagen kann, dass eine eigene Instanz einfacher zu handhaben ist, legen wir uns gleich darauf fest, jeweils eine eigene Instanz zu erzeugen. So können Sie diese Instanz bei der Erzeugung referenzieren und, wenn Sie diese nicht mehr benötigen, auch wieder beenden.
Wenn Sie von Access aus eine bestehende Instanz nutzen, müssten Sie sich diese gegebenenfalls mit einer anderen Anwendung teilen, die darauf zugreift, oder auch mit dem Benutzer, der gerade vor dem Rechner sitzt. Um eventuelle Seiteneffekte zu vermeiden, rufen wir für die Automation von Access aus lieber gleich eine eigene Instanz auf. Der einzige offensichtliche Nachteil ist, dass dies mehr Ressourcen benötigt als eine einzige Instanz.
Early Binding oder Late Binding
Die immerwährende Frage, ob Sie mit Early oder Late Binding arbeiten, kann man nur unter Betrachung des jeweiligen Kontext beurteilen. Wer eine Automation einer Office-Anwendung programmieren möchte, die unter allen gängigen Anwendungen läuft, sollte dies ohne Verwendung eines Verweises und dementsprechend mit Late Binding erledigen. Auf diese Weise verhindern Sie Probleme, die sonst beim Antreffen der falschen Version des Verweises auftreten.
Allerdings nehmen Sie sich damit auch die Möglichkeit, auf die Ereignisse zu reagieren, die vom Word-Objekt oder vom Document-Objekt ausgelöst werden. Interessant ist dies beispielsweise, wenn Sie beobachten wollen, ob der Benutzer ein Dokument öffnet oder schließt oder dieses speichert.
Sollten Sie solche Funktionen nutzen wollen, arbeiten Sie mit Early Binding, also unter Verwendung eines fest integrierten Verweises auf die jeweils aktuelle Version der Word-Objektbibliothek.
Diesen stellen Sie im Verweise-Dialog ein, den Sie über den Menüpunkt Extras|Verweise des VBA-Editors (Strg + G) öffnen – s. Bild 1. Anschließend können Sie mit der folgenden Anweisung eine Word-Instanz erstellen und mit einer entsprechenden Objektvariablen referenzieren:
Bild 1: Verweis auf die Word-Bibliothek
Dim objWord As Word.Application Set objWord = New Word.Application Sleep 3000 objWord.Quit Set objWord = Nothing
Dies erstellt eine Word-Instanz und schließt diese nach drei Sekunden wieder. Die gleiche Variante sieht beim Late Binding so aus:
Dim objWord As Object Dim objDocument As Object Set objWord = _ CreateObject("Word.Application") Set objDocument = objWord.Documents.Add
Um das Word-Fenster anzuzeigen und in den Vordergrund zu holen, sind ein paar Zeilen mehr nötig. Wir erzeugen zusätzlich ein neues, leeres Word-Dokument, machen die Word-Instanz mit der Visible-Eigenschaft sichtbar und holen das Anwendungsfenster mit der AppActivate-Methode nach vorn:
Dim objWord As Word.Application Dim objDocument As Word.Document Set objWord = New Word.Application Set objDocument = objWord.Documents.Add objWord.Visible = True AppActivate objDocument.Name Sleep 3000 objWord.Quit Set objWord = Nothing
Auch diese Instanz soll nach drei Sekunden wieder verschwinden. Sie können auch ohne explizites Erzeugen eines Word-Objekts ein Dokument in Word erzeugen oder öffnen. Das geht schlicht so:
Dim objDocument As Word.Document Set objDocument = New Word.Document objDocument.Parent.Visible = True AppActivate objDocument.Name Sleep 3000 objDocument.Close
Nach dem Schließen bleibt die Word-Instanz allerdings bestehen. Sie müssen diese dann manuell schließen oder per Code über die Parent-Eigenschaft des Word-Dokuments:
objDocument.Parent.Quit
Dazu muss das Dokument natürlich noch geöffnet sein. Wenn das Dokument vom Erzeugen beziehungsweise öffnen bis zum Schließen noch bearbeitet wird, erscheint beim Versuch, Word zu schließen, ein Dialog, der fragt, ob das Dokument gespeichert werden soll (s. Bild 2).
Bild 2: Rückfrage vor dem Schließen eines Dokuments
Dies können Sie verhindern, indem Sie festlegen, was beim Schließen geschehen soll: Soll Word das Dokument ohne Rückfrage speichern Oder die änderungen verwerfen Dies legen Sie mit einem der folgenden Werte für den ersten Parameter der Close-Methode des Document-Objekts fest:
- wdDoNotSaveChanges: Schließt das Dokument, ohne zu speichern.
- wdPromptToSaveChanges: Fragt den Benutzer, ob das Dokument gespeichert werden soll (Standardwert).
- wdSaveChanges: Speichert die änderungen.
Wenn Sie das Speichern-Verhalten auf diese Weise beeinflussen möchten, müssen Sie die Word-Instanz allerdings explizit instanzieren.
Anderenfalls könnten Sie diese nach dem Schließen des Document-Objekts nicht mehr refenzieren und müssten Word manuell schließen (sofern sichtbar) oder über den Task-Manager terminieren (wenn das Word-Fenster nicht eingeblendet wurde).
Wenn Sie selbst das Dokument per VBA mit Text füllen oder vorhandenen Text bearbeiten, werden Sie die änderungen vor dem Schließen speichern wollen:
Dim objWord As Word.Application Dim objDocument As Word.Document Set objWord = New Word.Application Set objDocument = objWord.Documents.Add objWord.Visible = True AppActivate objDocument.Name objDocument.Range = "Hallo" Sleep 3000 objDocument.Close, objWord.Quit Set objWord = Nothing
Haben Sie ein neues Dokument erstellt, wurde dies allerdings zuvor noch nicht gespeichert – ein Versuch, das Dokument zu schließen und automatisch zu speichern, schlägt fehl, da Word noch keinen Speicherort kennt. In diesem Fall speichern Sie es zuvor manuell unter dem gewünschten Namen. Dabei geben Sie optimalerweise direkt das gewünschte Format an, hier als Word-Dokument:
objDocument.SaveAs2 CurrentProject.Path & "\word.doc", wdFormatDocument
Dies verwendet das ältere .doc-Format. Wenn Sie das neue XML-Format verwenden möchten (Dateiendung .docx), verwenden Sie diese Anweisung:
objDocument.SaveAs2 CurrentProject.Path & "\word.docx", wdFormatXMLDocument
Beide Anweisungen speichern das Dokument im Verzeichnis der aktuellen Access-Datenbank.
Bestehendes Dokument öffnen
Sie können auch auf ein bereits bestehendes Dokument zugreifen. Dazu verwenden Sie die Open-Methode der Documents-Auflistung des Word-Objekts:
Set objDocument = objWord.Documents.Open(CurrentProject.Path & "\word.doc")
Auch dies gelingt mit Late Binding – in diesem Fall ohne vorherige Instanzierung eines Word-Objekts:
Set objDocument = GetObject( CurrentProject.Path & "\word.doc")
Sie müssen also einfach nur den Namen des zu öffnenden Dokuments als Parameter der GetObject-Methode angeben.
Text einfügen
Nachdem Sie ein Word-Dokument erstellt oder geöffnet haben, möchten Sie Text einfügen.
Dies gelingt am einfachsten, indem Sie dem mit der Range-Eigenschaft geliefernten Objekt des Word-Dokuments einen Text zuweisen:
objDocument.Range.Text = "Range-Objekt gefüllt"
Was aber ist dieses Range-Objekt überhaupt
Ein Range-Objekt markiert verschiedene Bereiche eines Dokuments. Das Range-Objekt des Document-Objekts etwa markiert den kompletten Inhalt. Wenn Sie der Text-Eigenschaft dieses Range-Objekts einen Text zuweisen, wird der komplette Bereich mit dem angegebenen Text überschrieben.
Sie können dies einfach experimentell prüfen, indem Sie eine Objektvariable für ein Document-Objekt im Kopf eines Standardmoduls deklarieren:
Public objDoc As Word.Document
Dieses füllen Sie dann mit der folgenden Prozedur:
Public Sub TextExperimente() Dim objWord As Word.Application Set objWord = New Word.Application Set objDoc = objWord.Documents.Add objWord.Visible = True AppActivate objDoc.Name End Sub
Das Dokument bleibt geöffnet und Sie können über das Direktfenster mit der Variablen objDoc auf das Dokument zugreifen.
Wenn Sie dort beispielsweise die folgende Zeile eingeben, wird der entsprechende Text angelegt (s. Bild 3):
Bild 3: Einfügen von Texten über den Direktbereich
objDoc.Range.Text = "Text aus dem Direktbereich"
Um zu erkennen, welchen Bereich die Range-Eigenschaft jeweils zurückgibt, können Sie den Bereich per VBA markieren:
objDoc.Range.Select
Die Markierung ist nicht direkt sichtbar, also aktivieren Sie entweder manuell das Word-Fenster oder Sie aktivieren dieses mit dieser Anweisung:
AppActivate objDoc.Name
Tippen Sie einmal einige Absätze in das Dokument und wiederholen das Markieren des Range-Objekts von objDoc, erkennen sie, dass dieses Range-Objekt immer den kompletten Dokumentinhalt betrifft.
Beim Experimentieren werden Fehler auftreten, was dazu führt, dass die Objektvariable objDoc ihren Inhalt verliert.
Mit der folgenden kleinen Prozedur füllen Sie diese wieder – vorausgesetzt, während der Experimente gibt es nur eine Word-Instanz mit nur einem Dokument (s. Listing 1).
Public Sub AktuellesDokumentReferenzieren() Dim objWord As Word.Application Set objWord = GetObject(, "Word.Application") Set objDoc = objWord.Documents(1) End Sub
Listing 1: Referenzieren des aktuellen Dokuments
Dokumentinhalt einlesen
Andersherum können Sie den Inhalt eines Range-Objekts auch über das Direktfenster auslesen. Dazu geben Sie beispielsweise mit der Debug.Print-Anweisung den Inhalt der Eigenschaft Text des aktuellen Range-Objekts aus.
Absätze
Ein Absatz in einem Word-Dokument ist ein Text, der mit dem Zeilenumbruch abgeschlossen wird, also dem Zeichen, das Sie unter VBA mit vbCr beziehungsweise Chr(13) darstellen können. Die durch dieses Zeichen voneinander getrennten Absätze können Sie mit der Paragraphs-Auflistung erfassen und mit einer Objektvariablen des Typs Paragraph referenzieren. In Bild 4 haben wir beispielsweise den Inhalt des zweiten Absatzes im Direktfenster ausgegeben.
Bild 4: Zugriff auf einen Absatz per Paragraphs-Auflistung
Wollen Sie den Text eines Absatzes verändern, können Sie diesen gezielt referenzieren und anpassen – und zwar so:
objdoc.Paragraphs(2).Range.Text = "Neuer Text in Absatz 2"
Das Ergebnis fällt allerdings nicht wie gewünscht aus: Der Text des zweiten Absatzes wird zwar ersetzt, aber der zweite Absatz verschmilzt mit dem dritten Absatz. Der Grund ist einfach: Sie haben einen Absatz, dessen Text mit einem Zeilenumbruch abgeschlossen wurde, durch einen Text ohne Zeilenumbruch ersetzt. Die folgende Anweisung ersetzt den Absatz schließlich wie gewünscht:
objDoc.Paragraphs(2).Range.Text = "Neuer Text in Absatz 2" & vbCr
Nun wird genau der gewünschte Absatz ersetzt.
Absätze durchlaufen
Wenn Sie in objDoc eine Referenz auf ein geöffnetes Word-Dokument gespeichert haben, können Sie die Absätze des Dokuments über die Auflistung Paragraphs durchlaufen:
Dim p As Word.Paragraph For Each p In objDoc.Paragraphs Debug.Print p.Range.Text Next p
Dies gibt alle Absätze im Direktfenster aus, wobei jeweils eine Zeile frei bleibt – dies deshalb, weil Debug.Print ohnehin immer eine neue Zeile beginnt und jede Zeile mit vbCr einen eigenen Zeilenumbruch mitbringt. Die Paragraphs-Auflistung bringt auch die Möglichkeit mit, die Anzahl auszugeben:
objDoc.Paragraphs.Count
Dies ist wichtig, wenn Sie einmal alle oder bestimmte Absätze löschen möchten. In diesem Fall können Sie die Paragraphs-Auflistung nicht mit For Each durchlaufen, sondern rückwärts mit der For…Next-Schleife:
Dim p As Word.Paragraph Dim i As Integer For i = objDoc.Paragraphs.Count To 1 Step -1 Set p = objDoc.Paragraphs(i) Debug.Print p.Range.Text Next i
Wollen Sie den Absatz nicht ausgeben, sondern löschen, verwenden Sie diese Zeile:
p.Range.Delete
Sie löschen also nicht das Paragraph-Objekt, sondern das dadurch markierte Range-Objekt.
Absatz hinzufügen
Dass Sie einen Absatz nicht über die Range-Eigenschaft des Dokuments hinzufügen können, wurde bereits weiter oben deutlich – dieses Objekt markiert nämlich das komplette Dokument.
Wenn wir beispielsweise einen Absatz hinter dem letzten bestehenden Absatz einfügen möchten, müssen wir ein Range-Objekt definieren, das bis zum Ende des Ziels reicht und dann den gewünschten Text mit der Methode InsertAfter einfügen.
In einem ersten, naiven Ansatz probieren wir es so:
Dim p As Word.Paragraph Dim i As Integer Dim rng As Word.Range i = objDoc.Paragraphs.Count Set rng = objDoc.Paragraphs(i) rng.InsertAfter "Test"
Das heißt, wir ermitteln den Index des letzten Absatzes, erstellen ein Range-Objekt auf Basis dieses Absatzes und fügen dann mit der Methode Insert-After einen neuen Text ein.
Damit hängen wir allerdings keinen neuen Absatz an, sondern nur ein paar Zeichen an den letzten Absatz. Außerdem gibt es eine elegantere Methode, um den letzten Absatz zu ermitteln. Zusammen sieht die verbesserte Variante so aus:
Dim rng As Word.Range Set rng = objDoc.Paragraphs.Last.Range rng.InsertAfter vbCr & "Test"
Wir referenzieren den letzten Absatz direkt mit dem Range des mit der Eigenschaft Last ermittelten letzten Absatzes. Schließlich stellen wir dem neuen Text einen Zeilenumbruch voran, wodurch der Text gleich in einem neuen Absatz landet.
Daten aus Tabelle in Dokument schreiben
Nutzen wir die bisherigen Erkenntnisse doch, um den Inhalt einer Access-Tabelle in ein Word-Dokument zu schreiben – und zwar Zeile für Zeile.
In diesem Beispiel wollen wir wie in Bild 5 den Inhalt der Felder ArtikelID und Artikelname der Tabelle tblArtikel in je eine Zeile eines neu zu erstellenden Word-Dokuments eintragen.
Bild 5: Per VBA eingetragene Absätze
Dies gelingt in einer einfachen Schleife, wobei mit jedem Durchlauf der letzte Absatz als Range-Objekt referenziert wird und die gewünschten Werte dahinter eingefügt werden.
Da wir in diesem Fall gleich mit dem ersten Absatz beginnen, hängen wir den Zeilenumbruch (vbCr) hinten an den anzufügenden Ausdruck an (s. Listing 2).
Public Sub Artikelliste() Dim objDocument As Word.Document Dim rng As Word.Range Dim db As DAO.Database Dim rst As DAO.Recordset Set objDocument = New Word.Document AppActivate objDocument.Name Set db = CurrentDb Set rst = db.OpenRecordset("SELECT ArtikelID, Artikelname FROM tblArtikel", _ dbOpenDynaset) Do While Not rst.EOF Set rng = objDocument.Paragraphs.Last.Range rng.InsertAfter rst!ArtikelID & vbTab & rst!Artikelname & vbCr rst.MoveNext Loop End Sub
Listing 2: Einfügen der Datensätze einer Tabelle in ein Word-Dokument
Zeichenformat festlegen
Nun möchten wir vielleicht jeweils die ArtikelID fett drucken oder den Artikelnamen kursiv. Gerade die Hervorhebung einzelner Textelemente ist ja ein großer Vorteil von Word gegenüber her-kömmlichen Textfeldern in Access-Berichten.
Dazu liefert das Range-Objekt einige interessante Eigen-schaften – mehr dazu weiter unten.
Mit Textmarken arbeiten
Normalerweise versteht man unter dem Arbeiten mit Textmarken, dass diese in einer Dokumentvorlage festgelegt und dann später etwa per VBA mit konkreten Inhalten gefüllt werden.
In unserem Fall sollen die Textmarken etwas flexibler eingesetzt werden – beispielsweise, um einen Text nachträglich zu formatieren.
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