SCHWERPUNKT

RTF im Praxiseinsatz

15.09.2006
Will man formatierte Texte in einem einzelnen Formular- oder Berichtsfeld unterbringen, bietet sich als eine der wenigen Optionen das RTF2-Steuerelement von Stephen Lebans an. Probleme gibt es unter Umständen bei längeren Texten. Sie werden zwar umbrochen, aber nicht unbedingt an der gewünschten Stelle. Wir zeigen Ihnen in diesem Artikel das Handling langer formatierter Texte unter Access.

In den Ausgaben 7/2006 und 8/2006 von Expert´s inside Access wurde das RTF2-Steuerelement von Stephen Lebans und seine Einsatzmöglichkeiten in Access-Formularen und -Berichten vorgestellt. Wer einmal Blut geleckt hat, kommt von diesem Steuerelement nicht so schnell wieder los: Immerhin gibt es ausreichend Anwendungen, in denen es nützliche Dienste verrichten kann.

Zuletzt war die Rede davon, wie man das Steuerelement in Berichten einsetzt. Eignet es sich aber auch für längere Texte? Füllt man ein einfaches Memofeld mit einem langen Text und zeigt dessen Inhalt, der länger als eine Seite sein sollte, in einem Bericht an, bricht Access den Text exakt hinter der letzten auf eine Seite passenden Zeile um und setzt ihn auf der Folgeseite fort.

Schnipp, schnapp

Mit dem RTF2-Steuerelement ist das leider Glückssache: Seine Höhe wird genau an den vorhandenen Platz angepasst und der Rest auf der folgenden Seite ausgegeben.

Bild 1: Langen Text, der nicht mehr auf die aktuelle Seite passt, schneidet das RTF2-Steuerelement rigoros ab.

Dummerweise schneidet das RTF2-Steuerelement den Inhalt dabei genau an der untersten Kante des Steuerelements ab, was manchmal dazu führt, dass die letzte Zeile auf die aktuelle und die nächste Berichtsseite verteilt wird (Bild 1).

Absatz für Absatz

Damit das nicht passiert, wendet man einen zugegebenermaßen aufwendigen Trick an. Die Grundidee ist, den Text in mehrere Datensätze aufzuteilen, in einer temporären Tabelle zu speichern und in einem modifizierten Bericht anzuzeigen. Offen ist, wie und an welcher Stelle man den Text am einfachsten trennt.

Die nachfolgende Methode geht davon aus, dass der Text aus mehreren Absätzen besteht – eine Aufteilung des Textes in die einzelnen Absätze scheint daher sinnvoll. Passt der letzte Absatz nicht mehr auf eine Seite, soll er einfach auf der folgenden Seite ausgegeben werden.

So weit, so gut: Doch wie teilt man den RTFText in seine einzelnen Absätze auf? Sehen Sie sich zunächst den zugrunde liegenden RTF-Code an. Er sieht für ein kleines RTF-Dokument nicht allzu kompliziert aus (Bild 2), aber bei größeren Dokumenten ändert sich das.

Bild 2: Der Quellcode eines einfachen RTF-Dokuments.

In der Tat ist das Aufteilen des Dokuments mit einer Menge Arbeit verbunden, denn man muss zunächst jeden einzelnen Absatz identifizieren und diesen zusammen mit dem Kopf und dem Fuß des Ausgangsdokuments in ein neues Dokument schreiben – das komplette Verständnis des RTF-Formats ist dafür übrigens Voraussetzung.

Aus der Not geboren

Ich habe es ausprobiert und bin gescheitert. Aber Not macht bekanntlich erfinderisch: Ein Absatz eines im RTF2-Steuerelement enthaltenen RTFDokuments lässt sich schnell markieren und kopieren. Fügt man ihn anschließend in ein anderes RTF2-Steuerelement ein, entsteht daraus ein neues RTF-Dokument. Perfekt! Das Ganze muss nur noch automatisiert werden.

Vorher ist allerdings noch zu klären, was mit den einzelnen Absätzen geschehen soll. Da sie hintereinander in einem Bericht angezeigt werden sollen, liegt es nahe, sie in Form einzelner Datensätze in einer Tabelle zu speichern und den Bericht mit dieser Tabelle als Datenherkunft zu bestücken.

Vorbereitungen

Damit Sie das Beispiel nachvollziehen können, zunächst ein Blick auf die Grundausstattung:

Eine Tabelle namens tblRTFTexte enthält zwei Felder namens RTFTextID und RTFText. Letzteres ist als Memofeld angelegt, da es das RTF-Dokument enthalten soll (Bild 3). Die Eingabe der Daten erfolgt in einem Formular namens frm- RTFTexte. Das Formular ist an die Tabelle tbl-RTFTexte gebunden, enthält aber aus dieser Tabelle nur das Feld RTFTextID (Bild 4).

Den Inhalt des eigentlichen wichtigen Feldes der Tabelle tblRTFTexte soll ein RTF2-Steuerelement anzeigen, dementsprechend stellen Sie dessen Eigenschaft Steuerelementinhalt auf den RTFText ein.

Nun wird es interessant: Die im RTF2-Steuerelement enthaltenen Absätze sollen gefunden und in einer temporären Tabelle gespeichert werden. Dazu brauchen Sie zunächst eine passende Tabelle, die wie in Bild 5 aussieht – also nicht viel anders als die Tabelle mit dem ursprünglichen Text. Ein Feld zum Verknüpfen der Datensätze mit dem passenden Datensatz aus der Tabelle tblRTFTexte ist unnötig, da die Tabelle vor dem Speich rn der Absätze jeweils geleert wird.

Bild 3: Diese Tabelle enthält die Ausgangs-RTFDokumente.
Bild 4: Das Formular zur Eingabe von RTF-Dokumenten.
Bild 5: Entwurfsansicht der Tabelle zum temporären Speichern der Absätze eines RTF-Dokuments.

Zum Erzeugen eines neuen RTF-Dokuments aus einem kopierten Absatz fehlt im Formular noch ein weiteres RTF2-Steuerelement, das Sie ctlRTF2Temp nennen. Mit der Eigenschaft RTFText lässt sich der Quellcode des RTF-Dokuments auslesen, der anschließend in der Tabelle mit den einzelnen Absätzen gespeichert werden soll.

Einmal aufteilen, bitte!

Die komplette Funktion zum Aufteilen des RTFDokuments befindet sich in der Prozedur, die das Ereignis Beim Klicken der Schaltfläche cmdBerichtOeffnen des Formulars auslöst (Listing 1).

Private Sub cmdBerichtOeffnen_Click()
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim posStart As Long
Dim posEnd As Long
Dim posEndRTF As Long
Dim i As Integer
Dim strRTF As String
Set db = CurrentDb
db.Execute "DELETE FROM tblRTFTexteTemp", dbFailOnError
Set rst = db.OpenRecordset("tblRTFTexteTemp", dbOpenDynaset)
posStart = 1
posEnd = 1
With Me.ctlRTF2
strRTF = .PlainText
Do While Not posEnd = 0
posEnd = InStr(posStart, strRTF, vbCrLf)
If posEnd = 0 Then
posEndRTF = Len(strRTF) + 1
Else
posEndRTF = posEnd
End If
.SelStart = posStart - 1 - i
.SelLength = posEndRTF - posStart
.Copy
Me.ctlRTF2Temp.SetFocus
Me.ctlRTF2Temp.RTFText = ""
Me.ctlRTF2Temp.Paste
rst.AddNew
rst!RTFTextTemp = Me.ctlRTF2Temp.RTFText
rst.Update
i = i + 1
posStart = posEnd + 2
Loop
End With
DoCmd.OpenReport "rptRTFMitAbsaetzen", View:=acViewPreview
End Sub

Nach der Deklaration der benötigten Variablen löscht die Routine zunächst alle Datensätze der Tabelle tblRTFTexteTemp:

db.Execute "DELETE FROM tblRTFTexteTemp", dbFailOn-Error

Damit ist die im Anschluss erzeugte Datensatzgruppe rst auf Basis derselben Tabelle leer:

Set rst = db.OpenRecordset("tblRTFTexteTemp",dbOpenDynaset)

Die Routine initialisiert dann die beiden Variablen posStart und posEnd jeweils mit dem Wert 1 und speichert den reinen Text des RTF2-Steuerelements mit dem aufzuteilenden RTFDokument in der Variablen strRTF.

Die folgende Do-While-Loop-Schleife durchläuft die Routine so lange, bis die Variable posEnd den Wert 0 enthält. Die Variable enthält ihren Wert über die folgende Anweisung:

posEnd = InStr(posStart, strRTF, vbCrLf)

Diese Anweisung ist sehr wichtig: Sie sucht in der Zeichenkette strRTF nach dem Steuerzeichen vbCrLf, was einem Absatzende entspricht. Findet sie kein passendes Zeichen, ist das Ende des in der Zeichenkette strRTF gespeicherten Textes erreicht. Damit diese Anweisung die Zeichenkette nicht immer wieder von vorne durchsucht, beginnt die Suche jeweils an der durch die Variable posStart festgelegten Position.

Gibt es noch einen Zeilenumbruch, dann wird dessen Position in der Variablen posEndRTF zwischengespeichert, andernfalls erhält diese Variable einen Wert, der der letzten Position der Zeichenkette aus strRTF entspricht:

If posEnd = 0 Then
posEndRTF = Len(strRTF) + 1
Else
posEndRTF = posEnd
End If

Direkt im Anschluss passiert das, was sonst manuell geschehen müsste: Mit den zwei folgenden Methoden des RTF2-Steuerelements markiert die Routine den Text von der aktuellen Position bis zur Position des folgenden Zeilenumbruchs:

SelStart = posStart - 1 - i
.SelLength = posEndRTF - posStart

Die Copy-Methode des RTF2-Steuerelements kopiert dessen Inhalt, und die drei folgenden Anweisungen bereiten das temporäre RTF2-Steuerelement vor und fügen den Inhalt der Zwischenablage dort ein:

Me.ctlRTF2Temp.SetFocus
Me.ctlRTF2Temp.RTFText = ""
Me.ctlRTF2Temp.Paste

Nun fehlt nur noch das Speichern des Inhalts des temporären RTF2-Steuerelements in einem neuen Datensatz der Tabelle tblRTFTexteTemp. Dies erledigen diese drei Anweisungen:

rst.AddNew
rst!RTFTextTemp = Me.ctlRTF2Temp.RTFText
rst.Update

Geeigneten Bericht öffnen

Nachdem die Do-While-Loop-Schleife alle vorhandenen Datensätze durchlaufen hat, sieht der Inhalt der Tabelle mit den temporären Datensätzen wie in Bild 6 aus.

Bild 6: Die Absätze eines großen RTF-Dokuments als einzelne RTF-Dokumente.

Die Routine muss nun nur noch einen geeigneten Bericht öffnen, der auf der Tabelle tblRTFTexteTemp basiert. Im Entwurf sieht er wie in Bild 7 aus und enthält die Prozedur aus Listing 2, die durch das Ereignis Beim Formatieren des Detailbereichs ausgelöst wird.

Bild 7: Der Bericht zur Ausgabe der einzelnen Absätze eines RTFDokuments.

Private Sub Detailbereich_Format(Cancel As Integer, FormatCount As Integer)
Dim intHeightRTF As Integer
Dim intMaxHeightRTF As Integer
intMaxHeightRTF = 0
With Me!ctlRTF2
intHeightRTF = Me!ctlRTF2.Object.RTFheight
If intHeightRTF > 0 Then
If intHeightRTF < 32000 Then
.Height = intHeightRTF
Me.Section(acDetail).Height = .Height + .Top
If intMaxHeightRTF > .Height + .Top Then
intMaxHeightRTF = .Height + .Top
End If
End If
End If
End With
Me.Section(acDetail).Height = intMaxHeightRTF
End Sub

Beachten Sie das kleine unscheinbare Kästchen im rechten oberen Bereich des RTF2-Steuerelements: Es handelt sich um ein mit einem Punkt gefülltes Bezeichnungsfeld mit weißer Textfarbe, das erforderlich ist, weil Access sonst nicht mit dem Rendern des Inhalts des RTF2-Steuerelements zurechtkommt. Ohne dieses Bezeichnungsfeld, das unbedingt über dem RTF2-Steuerelement liegen muss, zerschießt Access den enthaltenen Text mehr oder weniger – dies hängt unter anderem von den gewählten Schriftarten und Formaten ab.

Den Bericht öffnet schließlich die folgende Anweisung:

DoCmd.OpenReport "rptRTFMitAbsaetzen", View:=acView-Preview

Bild 8: Die einzelnen Absätze des Originaldokuments werden sauber hintereinander ausgegeben.

Das Ergebnis kann sich sehen lassen, wie Bild 8 zeigt. Die Inhalte der einzelnen RTF2-Steuerelemente sind durch einen Rahmen hervorgehoben.

Zusammenfassung

Der Umgang mit längeren Texten ist mit dem RTF2-Steuerelement und ein wenig Fantasie gar nicht so schwer. Interessant wird es, wenn eine 1:n-Beziehung ins Spiel kommt und beispielsweise die Artikelbeschreibung eines Angebots in die einzelnen Absätze aufgeteilt werden muss. Probieren Sie es in der Zwischenzeit einmal aus, bis wir in einer der nächsten Ausgaben auf das Thema noch einmal zurückkommen.