Eine Access-Textbox, in einem Formular an ein Memo-Feld gebunden, ist eine spröde Angelegenheit, wenn es darum geht, ihren Inhalt ansprechend zu gestalten. Denn alle Formatierungsmöglichkeiten beziehen sich immer nur auf das ganze Feld, den gesamten Text. Ein einzelnes Wort etwa fett hervorzuheben, ist nicht möglich. Erst mit Access 2007 kam Farbe ins Spiel und der Richtext-Modus für Textfelder hielt Einzug, über den man Texte rudimentär formatieren kann. In allen früheren Access-Versionen bleibt man für diesen Zweck jedoch auf Fremdsteuerelemente angewiesen.
Die Anforderung, Texte zu formatieren, stellt sich auch in Datenbanken oft. Es müssen nicht gleich komplette Dokumente sein, die mit einem Datensatz verknüpft sind – es reichen bereits die berüchtigten Notiz- und Anmerkungsfelder, die man gern für Zusatzinformationen in Formulare einbaut, damit der Anwender dort alles unterbringen kann, was nicht in das Raster der vorgegebenen Eingabefelder passt.
Von dieser Möglichkeit wird dann erfahrungsgemäß auch reichlich Gebrauch gemacht, was nicht selten zu unübersichtlichen Textwüsten führt. Dabei würde nur wenig Formatierung bereits ausreichen, um eine gegliederte Darstellung des Textes zu ermöglichen.
In der Regel kommt ein Richtext-ActiveX-Steuerelement (richtx32.ocx) zum Einsatz, um dem Mangel abzuhelfen. Abgesehen davon jedoch, dass der Entwickler dann noch viel Arbeit vor sich hat, um das Steuerelement zusätzlich etwa mit einer Symbolleiste auszustatten, die es dem Anwender ermöglicht, den Text komfortabel zu editieren, kommen hier mindestens zwei Probleme ins Spiel.
Einmal ist das entsprechende OCX von Microsoft eigentlich nicht frei erhältlich, sondern entweder mit der Developer Edition von Access gebundelt, oder mit verschiedenen Entwicklungsumgebungen, wie Visual Basic 6.
Allerdings gibt es auch freie Alternativen, wie etwa das Steuerelement von Stephen Lebans oder das von vbAccelerator [1]. Zweitens muss man solch ein OCX mit der Datenbank weitergeben und ruft im Zweifelsfall Scherereien mit seiner Registrierung und den Verweisen im VBA-Projekt hervor.
Microsoft wurde sich dieser Unzulänglichkeit zum Formatieren offenbar bewusst und stellte mit Access 2007 den sogenannten Richtext-Modus für Textboxen bereit.
Unter der Voraussetzung, dass ein Memofeld einer Tabelle als Datenherkunft des Steuerelements dient, das seinerseits bereits in den Richtext-Modus versetzt wurde, kann in einer beliebigen Textbox der Inhalt über eine Popup-Toolbar formatiert werden (siehe Bild 1).
Bild 1: Textformatierung in Access 2007 (Richtext) über Popup-Toolbar
Leider ist dieses Popup-Menü, das nur beim Markieren von Text erscheint, auch schon die einzige Möglichkeit, auf die Formatierung des Textes Einfluss zu nehmen, und seine Bedienung erfordert etwas Geschicklichkeit im Umgang mit der Maus. Eine programmgesteuerte Einflussnahme ist nicht gegeben und das Menü lässt sich auch nicht abschalten.
Intern wird die Formatierung dann über HTML-Tags gespeichert, weshalb die Bezeichnung Richtext irreführend ist. Theoretisch ist es möglich, diese HTML-Tags anschließend über VBA-Code zu verändern.
Indessen darf man allerdings nicht erwarten, dass im Richtext-Modus nun beliebige HTML-Auszeichnungen genauso angezeigt würden, wie in einem Webbrowser; die Access 2007-Textbox beherrscht tatsächlich nur jene, die sich auch über das Popup-Menü erzeugen lassen.
Genügend Gründe also, sich nicht nur für die Versionen vor Access 2007 Gedanken über alternative Lösungen zu machen …
Webbrowser-Steuerelement
Das Microsoft Webbrowser Control, welches Sie über die Liste der ActiveX-Steuerelemente in ein Formular einfügen können, stellt einen Mini-Browser für HTML-Dokumente bereit. Im kommenden Access 2010 wird ein solches Steuerelement übrigens standardmäßig zur Bordausstattung von Access gehören.
Es hat dort den Vorteil, sich direkt an ein Memofeld einer Tabelle binden zu lassen, was mit dem normalen Webbrowser-ActiveX zwar nicht möglich ist, uns andererseits aber, wie später noch ausgeführt wird, nicht gerade vor weltbewegende Programmierprobleme stellt.
Das Webbrowser-Steuerelement hat den entscheidenden Vorteil, dass es grundsätzlich auf jedem Windows-System vorhanden ist und mit der Installation von Access oder Office gar nichts zu tun hat. Der Internet-Explorer selbst stellt es zur Verfügung. Verweis- oder Registrierungsprobleme gibt es hier nicht.
Das Steuerelement ist tatsächlich identisch mit dem Anzeigebereich des Internet-Explorers und lässt sich daher auch auf gleiche Weise über dessen Objektmodell MSHTML, auch DOM genannt, steuern.
Dieses Document Object Model ist enorm umfangreich und leistungsfähig. Entwickler, die auch Webseiten mit Javascript erstellen, werden das wissen. Man kann auf jegliches Element eines HTML-Dokumentes sowohl zugreifen und dessen Eigenschaften ändern oder Aktionen darauf ausführen, wie auch neue Elemente erzeugen.
Stellen wir also fest, dass mit dem Webbrowser Control ein geeignetes und unkompliziertes Steuerelement bereitsteht, mit dem durchformatierter Text angezeigt werden kann.
Wie aber können wir den in diesem Steuerelement angezeigten Text editieren – der Internet Explorer bietet ja selbst auch keine Möglichkeit für manuelle änderungen
Webbrowser im Design-Mode
Die Tatsache, dass sich der Internet-Explorer mit nur einer Codezeile in einen Modus versetzen lässt, der das Dokument bearbeitbar macht, ist nur wenig bekannt.
Dabei kann Text nicht nur gelöscht oder hinzugefügt, sondern es können auch ohne viel Aufwand Formatierungen verändert werden. Der “echte” Internet-Explorer birgt aber natürlich keine Steuer- oder Menüelemente, über die sich das realisieren ließe. Mit wenigen Zeilen VBA erwecken wir jedoch das Webbrowser-Steuerelement unter Access zum Leben, wie die folgende Anleitung zeigt:
- Erstellen Sie ein neues Formular unter einer beliebigen Access-Version.
- Fügen Sie im Formularentwurf über die Menüleiste (Einfügen|ActiveX-Steuerelement…) oder über das Ribbon (Entwurf|Steuerelemente), ein Microsoft Webbrowser-Steuerelement ein und nennen es ctlHTML.
- Versionsabhängig: Bevor Sie das Formular speichern, sollten Sie das Steuerelement in Access-Versionen vor Access 2003 auf seine endgültigen Abmessungen bringen. Leider ist es in früheren Versionen nicht möglich, die Größe des Elements nachträglich zu ändern – weder im Entwurf noch zur Laufzeit. Zwar lässt sich durchaus der Container-Rahmen ändern, der enthaltene Browser zeigt sich davon jedoch unbeeindruckt und behält seine einmal gespeicherte Größe bei.
- Schreiben Sie in die Form_Load-Ereignisprozedur (Beim Laden) folgende Zeile:
Me!ctlHTML.Object.Navigate2 "http://www.access-im-unternehmen.de"
Nach dem Öffnen des Formulars zeigt dieses die gewünschte Webseite an. Die Navigate2-Methode ist dafür verantwortlich und erledigt das Gleiche, wie die Eingabe einer URL in die Adresszeile des Internet-Browsers.
Unser Ziel ist es jedoch nicht, eine x-beliebige Webseite anzuzeigen, sondern neue HTML-Dokumente zu erzeugen und mit HTML-Strings aus einem Tabellenfeld zu speisen. Um ein solches neues Dokument im Steuerelement aufzubauen, reicht diese einfache Anweisung:
Me!ctlHTML.Object.Navigate2 "about:blank"
Diese Zeile ist unbedingt erforderlich, denn ohne sie zeigt das Steuerelement schlicht eine weiße Fläche an. Erst ein Rechtsklick zur Laufzeit offenbart den Unterschied: Mit about:blank gibt es ein Kontextmenü, ohne gibt es keines.
Wählen Sie im Kontextmenü dann den Eintrag Quelltext anzeigen, tritt dieser Inhalt zutage: <HTML></HTML> und manchmal auch <BODY></BODY>.
Jetzt wird es interessant. Wie kann man das neue HTML-Dokument mit weiterem Inhalt, etwa aus einer Tabelle, füllen
Auch das lässt sich erstaunlich einfach realisieren: Man schreibt den HTML-Text über die write-Methode des Dokuments. Zuvor aber noch ein kurzer Ausflug in das DOM-Modell.
DOM und MSHTML
Auf das Webbrowser-Steuerelement selbst haben Sie, wie oben angeführt, über dessen Object-Eigenschaft Zugriff.
Öffnen Sie im VBA-Editor den Objektkatalog, so finden Sie dort unter der Bibliothek SHDocVw, auf welche automatisch beim Einfügen eines Webbrowser-Controls verwiesen wird, das Klassenmodell zum Steuerelement. Die Klasse, die dem Control entspricht, nennt sich hier Webbrowser.
Sie zeigt allerlei Eigenschaften und Methoden, mit denen sich etwa die Gestalt des Steuerelements selbst einstellen lässt – unter anderem die bereits erwähnte Navigate2-Methode. Augenscheinlich findet sich jedoch nichts, was einen direkten Bezug zum HTML-Inhalt des gehosteten Dokuments hätte.
Die Bibliothek enthält derlei tatsächlich nicht. Dafür ist eine andere Bibliothek zuständig, die Sie manuell in die Verweise aufnehmen sollten und die ebenfalls vom Internet-Explorer-System bereitgestellt wird: MSHTML.
Öffnen Sie die Liste der Verweise (VBA-Editor, Menü Extras|Verweise…) und aktivieren Sie die Bibliothek Microsoft HTML Object Library.
Wenn Sie sich diese Bibliothek im Objektkatalog zu Gemüte führen, dann steht vermutlich die Frage im Raum, wie deren erschlagender Umfang von Klassen zu bändigen sei und wie die Verbindung zwischen dem Webbrowser-Control und der MSHTML-Bibliothek zustande kommt.
Das geschieht über genau eine Eigenschaft des Web-Controls, nämlich Document. Diese gibt ein Objekt der MSHTML-Klasse HTMLDocument zurück, welche im Grunde die oberste Ebene des DOM-Modells darstellt. Ein HTMLDocument enthält dann zahlreiche Unterobjekte, die ihrerseits haufenweise Eigenschaften und Auflistungen beheimaten. Nur ein exemplarisches Beispiel:
Das HTMLDocument enthält eine body-Eigenschaft, welche ein HTMLBody-Objekt zurückgibt. Das HTMLBody-Objekt kennt eine style-Eigenschaft in Form der HTMLStyle-Klasse. HTMLStyle wiederum zeigt eine Eigenschaft fontSize, die die Schriftgröße einstellt oder zurückgibt.
Wollten wir also die Schriftgröße des gesamten Bodys eines im Webcontrol geladenen Dokuments auf 11pt setzen, so ließe sich das auch ohne MSHTML über folgenden Ausdruck erledigen:
Me!ctlHTML.Object.Document.body.style.fontSize = "11pt"
Hier wird deutlich, dass der Verzicht auf die MSHTML-Bibliothek zu sehr langen Ausdrücken und zur Unübersichtlichkeit führt.
Einfacher ist es, wenn man das Dokument direkt einer Objektvariablen zuweist und dann alle weiteren Aktionen mit deren Hilfe ausführt:
Dim objDoc As MSHTML.HTMLDocument Set objDoc = Me!ctlHTML.Document With objDoc .body.style.fontSize = "11pt" End With Eine Alternative wäre auch dieser Code: Dim objDoc As HTMLDocument Dim objBody As HTMLBody Set objDoc = Me!ctlHTML.Document Set objBody = objDoc.body With objBody .style.fontSize = "11pt" .style.font = "Arial" End With
Dies soll lediglich verdeutlichen, wie verschachtelt das DOM-Modell ist und wie man mithilfe von Objektvariablen mehr Struktur in den Code bringen kann.
Völlig unverzichtbar wird MSHTML, wenn man auf Ereignisse reagieren will. Ein Objekt des Typs HTMLDocument etwa kann fast 40 Ereignisse auslösen.
Markiert man beispielsweise im angezeigten Dokument eine Passage, so löst dies ein Ereignis onselectionchange aus. Um auf dieses reagieren zu können, benötigt man eine Objektvariable mit dem Zusatz WithEvents:
Private WithEvents objDoc As MSHTML.HTMLDocument ... Set objDoc = Me!ctlHTML.Document ... Private Sub objDoc_onselectionchange() Debug.Print " Textauswahl wurde geändert" End Sub
Die komplette Referenz zu DOM und MSHTML finden Sie unter [3] im Internet. An dieser Stelle soll es bei den wenigen grundsätzlichen Anregungen zur Arbeit mit der Bibliothek bleiben. Notiz am Rande: Die MSHTML-Bibliothek erweitert sich ständig im selbem Maße wie der Internet-Explorer.
Deshalb finden sich in der aktuellen Version etwa auch Klassen zum Phishing darin. Das gibt es aber erst seit Version 7. Wenn Sie mit der Bibliothek arbeiten, sollten Sie folglich genau darauf achten, dass keine Methoden Anwendung finden, die es auf einem etwaigen Zielsystem noch gar nicht gibt, weil dort vielleicht noch der Internet Explorer 6 installiert ist.
In der Referenz zu MSHTML ist jeweils angegeben, seit welcher Version eine Methode gültig ist.
HTML-Dokument bearbeiten
Kommen wir zur ursprünglichen Aufgabe zurück, ein Dokument im Web-Control in den bearbeitbaren Zustand zu versetzen.
Bisher sehen Sie nach dem Laden eines Dokuments über about:blank ja nur eine leere Fläche. Mit einer weiteren Zeile schreiben Sie nun HTML-Code hinein:
Dim strHTML As String strHTML = "<body><strong>TEST</strong></body>" ctlHTML.Document.write strHTML
Und schon erscheint die Ausgabe TEST im Steuerelement. Falls Sie hier auf die Idee kommen sollten, wie oben angeregt, eine Objektvariable einzusetzen, werden Sie keinen Erfolg haben:
Dim objDoc As HTMLDocument Set objDoc = Me!ctlHTML.Document objDoc.write strHTML
Dies führt zu einem Fehler, weil ausgerechnet die write-Methode von MSHTML.HTMLDocument nicht VBA-komform ist und als Parameter einen Variant-Typ vorsieht, den es unter VBA im Unterschied zu C++ nicht gibt.
Die Document.write-Methode – siehe oben – des Controls selbst hat damit aber offenbar keine Probleme und führt intern die notwendige Konvertierung durch.
Der hier hartkodierte Inhalt des Dokuments aus einer String-Variablen lässt sich selbstverständlich genauso gut aus einem Tabellenfeld auslesen, das wegen der möglichen Länge des Inhalts als Memofeld deklariert sein sollte. Im Formular könnte das mit nur einer Zeile so aussehen:
ctlHTML.Document.write Me!HTML_Memofeld.Value