HTML5 als Grafikkomponente

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

Wenn es um das Erzeugen von Grafiken geht, lässt Access Sie weitgehend im Regen stehen. Maximal die Anzeige von Bilddateien über das Bild- oder Anlagesteuerelement ist in Formularen und Berichten vorgesehen. Für alles darüber hinaus kommen Sie um den Einsatz aufwändiger Windows-API-Funktionen nicht herum. Wir zeigen hier aber, dass Sie auch mithilfe des Webbrowser-Steuerelements und HTML5 auf einfache Weise zu verblüffenden Ergebnissen kommen.

Die HTML5- und SVG-Erweiterungen von HTML

Grafikelemente können auf dynamischen Webseiten über zwei Schnittstellen erstellt werden. Die eine ist SVG und die andere HTML5. SVG ist eine auf XML basierende Auszeichnungssprache, die beliebige grafische Objekte mitsamt Effekten erzeugen und die resultierenden Anweisungen in einer Datei mit der Endung svg abspeichern kann.

Solche Dateien stellen Sie dann mit einem Grafikprogramm dar und bearbeiten sie dort auch, wobei sich vor allem Inkscape als Open-Source-Lösung etabliert hat. Seit geraumer Zeit beherrschen aber auch alle gängigen Webbrowser diese Schnittstelle, wobei sie keinesfalls auf das Laden solcher Dateien angewiesen sind, sondern die Grafikelemente auch direkt über Objekte aufbauen können. In der Regel kommt für die Steuerung JavaScript zum Einsatz.

Dasselbe gilt für HTML5. Auch hier gibt es vordefinierte Objekte, die Sie mit Leben füllen. Der wesentliche Unterschied zu SVG besteht allerdings darin, dass die damit getätigten Grafikoperationen nicht mehr rückgängig gemacht werden können. Während SVG eine Ansammlung von interaktiven (!) Grafikobjekten darstellt, die Sie später einzeln im Dokument ansprechen können, zeichnet HTML5 alles passiv auf eine Malfläche (canvas), wie etwa das Windows-GDI auch. Dafür ist seine Handhabung aber auch deutlich einfacher – einer der Gründe, die Schnittstelle als erste zu betrachten. In einem Folgebeitrag wenden wir uns dann der alternativen SVG-Schnittstelle zu.

HTML5-Objektmodell

Für alles, was mit HTML und dem Internet Explorer zusammenhängt, stellt Microsoft die Bibliothek MSHTML bereit. Laden Sie diese über die Auswahl Microsoft HTML Object Libary in die Verweise Ihres VBA-Projekts. Im Objektkatalog erscheint sie danach unter dem Namen MSHTML.

Diese Bibliothek ist die umfangreichste, die Sie unter Windows überhaupt finden können. Zu jedem HTML-Element gibt es hier ein Pendant in Form einer Klasse. So entspricht ein Web-Dokument etwa der Klasse HTMLDocument, eine Tabelle dem HTMLTable, ein div-Bereich dem HTMLDivElement, und so weiter.

Jede dieser Klassen weist genau jene Eigenschaften und Methoden auf, die das W3C-Konsortium für das HTML-Element definiert hat, auch wenn jeder Browser-Hersteller hier sein eigenes Süppchen kocht und manchmal nur eine Teilmenge oder modifizierte Parameter implementiert sind. Die Angleichung ist aber in vollem Gange, so dass sich derzeit der Internet Explorer in seinem Verhalten nur noch marginal von anderen Browsern, wie Firefox oder Chrome unterscheidet.

Häufig wird der Text eines HTML-Dokuments über Strings aufgebaut. Das ist mit MSHTML, mit dem Sie den Internet Explorer oder das Webbrowser-Steuerelement fernsteuern, eigentlich nicht mehr notwendig. Sie erzeugen stattdessen neue Klasseninstanzen der Elemente unter VBA und verschachteln diese im HTMLDocument oder dessen HTMLBody über das Setzen bestimmter Eigenschaftenparameter. Damit können Sie den HTML-Text komplett über Klassenobjekte erzeugen.

Das Stichwort HTML5 selbst werden Sie indessen im Objektkatalog nicht finden. Seine Implementierung versteckt sich hinter jenen Klassen, die den String Canvas enthalten, wie etwa HTMLCanvasElement, CanvasGradient, CanvasPattern, ICanvasPixelArray oder CanvasRenderingContext2D. Letztere ist von besonderer Bedeutung, weil eben sie alle benötigten Grafikmethoden aufweist.

Bild 1 zeigt einen Ausschnitt der Methoden im Objektkatalog. An ihren Namen lässt sich schon ablesen, was sie bewirken sollen. lineTo etwa zeichnet eine Line vom aktuellen Ort zu den Koordinaten x und y. arc erzeugt einen Bogen. Mit den Path-Methoden legen Sie geschwungene Linienzüge an. rect malt ein Rechteck. Die Fill-Methoden erlauben das Füllen dieser Grafikobjekte mit beliebigen Farben oder Mustern, wobei diverse Verlaufsformen möglich sind (Gradient). Die Aufgabe besteht also darin, ein HTML-Dokument zu erzeugen, diesem ein Canvas-Element über eine VBA-Objektvariable zu verabreichen und anschließend über die Methoden das CanvasRenderingContext2D Zeichenoperationen darauf auszuführen. JavaScript entfällt. Die Ausgabe geschieht im eingebauten Access-Webbrowsersteuerelement.

Die Zeichenoperationen der HTML5-Canvas-Klasse im Objektkatalog

Bild 1: Die Zeichenoperationen der HTML5-Canvas-Klasse im Objektkatalog

Webbrowser-Steuerelement

HTML5 und SVG hielten mit der Version 9 des Internet Explorers Einzug und reflektierten sich auch gleich im MSHTML-Objektmodell. Das Webbrowser-Steuerelement hostet ja lediglich den Internet Explorer und kann damit praktisch alles, was der auch kann. Bis zur aktuellen Version 11 (Edge verwendet eine andere Technik und hat mit dem Webbrowser Control nichts zu tun; auch unter Windows 10 nutzt das Control den Kompatibilitätsmodus des IE11) ereignete sich eine Evolution, die den Umfang der W3C-Implementierung kontinuierlich steigerte. Sie kann als weitgehend abgeschlossen betrachtet werden.

Dumm leider, dass das Steuerelement im Urzustand nur den Internet Explorer 7 hostet. Dies bedeutet, dass Microsoft aus Sicherheitsgründen den Internet Explorer im Steuerelement in den Zustand dieser alten Version versetzt. Und diese kennt überhaupt noch keine HTML5– oder SVG-Elemente! In diesem Modus ignoriert das Control schlicht alle entsprechenden Anweisungen. Zum Glück gibt es aber einen Workaround, den Sie schon im Beitrag der Ausgabe 3/2017 GoogleMaps als Kartendienst (Shortlink 1089) kennenlernten. Mithilfe des Moduls mdlWebControl und dem Aufruf der Prozedur SetWebControlAsIE11 oder der übergeordneten Prozedur PrepareBrowser können Sie über API-Funktionen erzwingen, dass der Internet Explorer im Webbrowser-Steuerelement in den Modus der Version 11 übergeht und zusätzlich noch weitere Limitierungen aufgehoben werden. Das alles ist legitim. Dasselbe Modul findet nun auch in der Beispieldatenbank HTMLImage.accdb zum vorliegenden Beitrag Anwendung. Von einer Funktionsbeschreibung sehe ich an dieser Stelle ab, weil dazu alles erschöpfend bereits im erwähnten Beitrag erläutert ist.

Dergestalt vorbereitet kann das Webbrowser-Steuerelement dann auch mit HTML5 und SVG umgehen.

Die erste HTML5-Grafik

Um die grundlegenden Vorgänge zu beleuchten, die die Anlage einer HTML5-Grafik im Webbrowser-Steuerelement erfordern, laden Sie das Formular frmHTML5 der Beispieldatenbank. Zunächst sehen Sie hier nur einen augenscheinlich leeren Detailbereich, sowie einen Button rechts oben im Formularkopf. Nach dessen Betätigung kommt es zur Ausgabe der ziemlich sinnfreien Grafik in Bild 2. Aber immerhin demonstriert das Beispiel einige Grafikelemente, die andernorts vielleicht nützlicher verwandt werden könnten.

Diese Grafik im Formular frmHTML5 wird mit nur recht wenigen Zeilen VBA-Code erzeugt

Bild 2: Diese Grafik im Formular frmHTML5 wird mit nur recht wenigen Zeilen VBA-Code erzeugt

Im Entwurf des Formulars finden Sie das Webbrowser-Steuerelement mit dem Namen ctlWeb im Detailbereich. Es füllt diesen fast ganz aus und hat die Einstellung Nach unten und quer dehnen für die Eigenschaft Verankern. Damit macht das Steuerelement alle Größenänderungen des Formulars mit. Der Steuerelementinhalt ist an kein Tabellenfeld gebunden, sondern auf den festen Wert =”about:blank”. Ohne diese Einstellung fänden Sie nämlich nach dem Laden des Formulars im Webbrowser die unschöne Ausgabe Die Adresse ist ungültig vor. about:blank hingegen rendert gleich zu Beginn eine leere Fläche und legt ein rudimentäres Dokument an, mit dem Sie anschließend weiterarbeiten können, um ihm die HTML5-Elemente unterzujubeln. Schauen wir uns dazu einmal den Code der Formularklasse an. Beim Laden des Formulars wird zunächst diese Zeile abgesetzt:

Me!ctlWeb.Object.Silent = True

Aufgepasst! Das Webbrowser-Steuerelement schleift keineswegs alle Methoden des zugrundeliegenden Internet Explorer-Objekts durch. Deshalb muss dessen Instanz erst über die Eigenschaft Object des Steuerelements erhalten werden, ähnlich, wie auch bei ActiveX-Steuerelementen üblich. Und eine seiner Eigenschaften ist Silent, die hier auf True eingestellt wird. Das weist ihn an, jegliche Meldungen zu fehlerhaften Skripten oder ähnliches zu unterlassen. Das hat zwar direkt nichts mit unserem HTML5-Vorhaben zu tun, ist jedoch grundsätzlich sinnvoll, wenn Sie dem Anwender Ihrer Datenbank im Zweifel nicht die Herkunft des dargestellten Inhalts verraten möchten …

Der Klick auf den Button cmdCanvas ruft nun die Ereignisprozedur in Listing 1 auf. Hier wird zunächst die Objektvariable oDoc vom Typ HTMLDocument (MSHTML-Bibliothek) auf das im Webbrowser-Steuerelement befindliche leere Dokument gesetzt. Der Hintergrund wird dann über das style-Attribut bgColor des body-Elements auf hellgrau (lightgray) eingestellt. In der Abbildung wird das durch die leicht graue Umrandung der Grafik deutlich. Das Setzen solcher Attribute wirkt sich übrigens immer unmittelbar im Browser aus. Bis hierhin hat das Ganze noch nichts mit HTML5 zu tun, was sich jedoch in der folgenden Zeile ändert. Die erzeugt über die Methode createElement des Dokuments ein allgemeines HTMLElement vom Typ canvas. Unterstützt der Browser HTML5 nicht, etwa, weil sein Modus noch unverändert auf der Version 7 des Internet Explorers beruht, so ereignet sich hier kein Fehler, sondern die Objektvariable oElement hat schlicht den Wert Nothing oder aber sie hat keinen spezifischen Typ. Deshalb fragt die nächste Zeile über die Hilfsroutine IsCanvasSupported noch ab, ob das Canvas-Element erfolgreich angelegt wurde und verlässt andernfalls die Routine.

Private Sub cmdCanvas_Click()
     Dim oDoc As HTMLDocument
     Dim oElement As IHTMLElement
     Dim oCanv As IHTMLCanvasElement
     Dim CTX As ICanvasRenderingContext2D
     Dim oGrad As ICanvasGradient
     Set oDoc = Me!ctlWeb.Object.Document
     oDoc.body.setAttribute "bgColor", "lightgray"
     Set oElement = oDoc.createElement("canvas")
     If Not IsCanvasSupported(oElement) Then Exit Sub
     oElement.Id = "zeichnung"
     Set oCanv = oElement
     oCanv.Width = 600
     oCanv.Height = 400
     oDoc.body.appendChild oElement
     Set CTX = oCanv.getContext("2d")
     With CTX
         Set oGrad = .createLinearGradient(0, 0, 500, 0)
         oGrad.addColorStop 0, "#e0e0e0"
         oGrad.addColorStop 0.5, "#90b080"
         oGrad.addColorStop 1, "#808080"
         .FillStyle = oGrad
         .fillRect 10, 10, 500, 300
         .RECT 30, 320, 100, 30
         .Fill
         .stroke
         .strokeRect 40, 350, 100, 30
         .globalAlpha = 0.2
         .moveTo 20, 200          ''''180, 200
         .lineWidth = 7
         .arc 100, 200, 80, 0, 6.4, 0
         .stroke
     End With
     Debug.Print oDoc.all(0).outerHTML
End Sub

Listing 1: Komplette Routine zur Anlage der HTML5-Grafik

Dem Canvas-Element wird nun die eindeutige Id zeichnung verabreicht, damit später gegebenenfalls auf es Bezug genommen werden kann. Da die Variable oElement nur vom allgemeinen Typ HTMLElement ist, unterstützt Intellisense von VBA auf sie nicht die speziellen Methoden des Canvas-Elements. Deshalb kommt die Hilfsvariable oCanv ins Spiel, die dezidiert den Typ IHTMLCanvasElement aufweist. Sie wird direkt auf die Variable oElement gecastet. Kleiner Einschub zur Hilfsroutine IsCanvasSupported: Die Funktion ermittelt einfach den Typ des in der Variablen oElement gespeicherten HTML-Elements:

If TypeName(oElement) <> "HTMLCanvasElement" Then
...(IsCanvasSupported = False)...  Else = True ...

Nun haben wir zwar ein Element vom Typ Canvas angelegt, doch dieses ist damit noch lange nicht Bestandteil des Dokuments, sondern existiert rein im Speicher. Die Methode appendChild des Body-Objekts erledigt dies in der Folge aber.

Das Canvas-Element selbst kennt noch keinerlei Methoden zum Zeichnen. Nur der sogenannte Render-Kontext kann das. Sie erhalten diesen über den Methodenausdruck getContext(“2D”). Das resultierende Objekt ist vom Typ ICanvasRenderingContext2D und wird in der Objektvariablen CTX abgespeichert. Der Parameter 2D ist übrigens der einzige, der hier aktuell erlaubt ist. Definiert ist über W3C zwar auch der Parameter 3D für dreidimensionale Grafiken, doch der Internet Explorer unterstützt das, wie viele andere Browser auch, noch gar nicht.

Die Vorarbeiten sind damit abgeschlossen und es kann nun direkt an die Zeichenoperationen gehen. Da derer mehrere folgen, ist die Variable CTX mit einem With-Block versehen, der fast bis ans Ende der Prozedur reicht.

Wir picken uns aus der Reihe der Anweisungen die Methode fillRect heraus. Die erzeugt ein gefülltes Rechteck. Als Parameter erwartet sie die Koordinaten der linken oberen Ecke, sowie Breite und Höhe des Rechtecks. Allein ausgeführt resultierte sie in einem komplett schwarzen Rechteck. Denn Angaben etwa zur Linienstärke der Umrandung und zur Art und Farbe der Füllung werden der Methode ja nicht übergeben. Die Methode greift nämlich auf den Kontext des Canvas zurück, in dem bereits einige Voreinstellungen zu diesen Stilen festgelegt sind. Um diese Stileinstellungen zu ändern, bedienen Sie sich weiterer Methoden des Objekts, und das übernehmen die Zeilen vor dem Aufruf von fillRect.

Soll ein Grafikelement nicht nur einfarbig gefüllt werden, sondern mit einem Verlauf, so muss die Eigenschaft FillStyle des Grafikkontexts auf ein Verlaufsobjekt gesetzt werden. Dieses Objekt vom Typ ICanvasGradient, zugewiesen der Variablen oGrad, erzeugt die Prozedur über die Methode createLinearGradient. Sie erwartet als Parameter zwei Punktkoordinaten in der Einheit Pixel, die der gewünschten Ausdehnung des Verlaufs entsprechen. Hier ist das einerseits die linke obere Ecke (0,0,) und dann die rechte obere (500,0). Der Verlauf erstreckt sich damit von links bis fast nach rechts. Sie können hier über andere Werte natürlich auch einen diagonalen Verlauf erzeugen, der problemlos auch über den Rand des Canvas hinausgehen darf. Zu erwähnen wäre an dieser Stelle auch, dass die alternative Methode createRadialGradient einen kreisförmigen Verlauf erzeugen könnte.

Anschließend müssen die Farben für den Verlauf bestimmt werden, was die Methode addColorStop ermöglicht. Sie erwartet zwei Parameter: Der erste gibt dabei den Abstand innerhalb des Verlaufs an, auf den die Farbe wirken soll. Der Wertebereich des Single-Werts erstreckt sich von 0 bis 1. 0 bedeutet in unserem Beispiel ganz links, 1 wäre ganz rechts, also bei der Breite 500 Pixel. Mit 0,5 treffen Sie genau die Mitte der Fläche, die sich bei der Breite 250 Pixel befindet. Es können beliebig viele solcher Farbbereiche durch Wiederholung der Methode addColorStop angelegt werden. Wenn Sie wollen, also ein ganzes buntes Farbspektrum. Die Farben selbst geben Sie jeweils im zweiten Parameter als hexadezimalen String an, wie sonst auch für solche HTML-Farbattribute üblich. Unsere drei Farbbereiche sind auf hellgrau, grün und dunkelgrau gesetzt. Die Methode fillRect füllt das Rechteck nun mit diesem im Kontext neu bestimmten Verlauf. Es nimmt fast den gesamten Raum des Canvas ein.

Es gibt noch eine alternative Methode, ein gefülltes Rechteck zu erzeugen. Das machen die nächsten drei Zeilen deutlich. Die Methode RECT legt zunächst ein Rechteck auf dem Hintergrund an, welches ohne Füllung daherkommt. Ein einfacher Aufruf von fill ändert dies. Sie füllt die zuletzt angelegten Grafikobjekte mit der im Kontext weiter oben festgelegten Verlaufseigenschaft. Zu sehen bekommen Sie das tatsächlich jedoch erst, nachdem Sie die Methode stroke aufrufen. Sie erst rendert alle zuvor angelegten Grafikobjekte in den Canvas. Im Klartext heißt das, dass sie mehrere Grafikobjekte, etwa auch Kreise (arc), hintereinander erzeugen können, sie auf einen Schlag mit fill füllen und dann ebenso komplett über stroke physisch anzeigen können. In der Abbildung sehen Sie das verlaufsgefüllte kleine Rechteck links unten.

Darunter befindet sich noch ein ungefülltes. Das könnte ebenfalls über die Methode RECT, gefolgt von stroke, erzeugt werden. Einfacher aber ist die kombinierende Methode stokeRect, welche das auf einen Schlag erledigt.

Nun fehlt noch der Kreis in der Abbildung, der über die Methode arc erzeugt wird, welche auch Teilbogen darstellen kann. Die Reihenfolge der Parameter: Erst die x– und y-Koordinaten des Mittelpunkts (100, 200), dann der Radius – alle Angaben in Pixel. Darauf folgen Start- und Endwinkel im Bogenmaß (0, 6.4). 6.4 entspricht ungefähr zweimal Pi, weshalb ein kompletter Kreis gemalt wird. Der Winkel 0 befindet sich übrigens ganz rechts vom Mittelpunkt und die Zeichenrichtung geschieht im Uhrzeigersinn, wenn der allerletzte Parameter der Methode auf 0 steht. Mit dem Wert 1 würde gegen den Uhrzeigersinn gezeichnet.

Sie können mit dieser Methode also beliebige Kreisabschnitte erzeugen, nicht jedoch ellipsenförmige. Die Breite des Zeichenstifts wurde zuvor über die Eigenschaft lineWidth des Kontexts auf 7 Pixel eingestellt. Wo aber kommt die Querlinie her, die den Kreis zu einem Stoppschild macht Eine weitere Methode zum Linienzeichnen findet sich ja in der Routine nicht.

Grund dafür ist die Tatsache, dass dich der Canvas den Endpunkt der letzten Zeichenoperation hernimmt und ihn als Startpunkt der nächsten verwendet. Und die letzte Aktion war das Rechteck ganz unten. Der Startpunkt des Kreises begänne damit bei dessen Ecke links oben. Genauer: Es würde zusätzlich eine gerade Linie von dieser Ecke zum Kreisanfang gezeichnet werden. Auf diese Weise kommt es durch mehrere aufeinanderfolgende Grafikoperationen zu einem Linienzug. Der kann aber durch die Methode moveTo unterbrochen werden. In der Prozedur wird der Stift gleichsam zur Koordinate 80 Pixel links vom Mittelpunkt des Kreises bewegt (x=100, r=80, r-x = 20). Von hier aus entsteht dadurch eine waagrechte Linie zum Startpunkt des Kreises rechts. Die ganze Geschichte wirkt sich auch hier erst dann sichtbar aus, nachdem die Methode stroke aufgerufen wird.

Eine weitere Style-Eigenschaft des Canvas ist außerdem globalAlpha. Die entspricht unter HTML sonst dem Attribut opacity und gibt den Grad der Durchsichtigkeit der folgenden Grafikoperationen an. Der Kreis ist deshalb durchscheinend. Dieses Feature, den Alpha-Kanal zu beeinflussen, ermöglicht unter HTML5 überlagernde Grafikelemente! globalAlpha hat allerdings einen etwas eigentümlichen Wertebereich. 1 bedeutet opak, 0 völlig durchsichtig. Doch 0.5 bedeutet noch keinesfalls halbdurchsichtig! Dies kommt eher durch einen Wert von 0.3 zustande. Selbst beim Wert 0.01 ist der Kreis noch deutlich zu erkennen. Offenbar verwendet HTML5 hier eine exponentielle Kurve.

Sie sehen, dass sich über HTML5 mit nur wenigen Zeilen Code Grafikelemente erzeugen lassen, die mit dem Windows-API (GDI32 oder GDIPlus) hunderte Zeilen erfordert hätten! An dieser Stelle kann nicht jede Methode des Canvas und deren Parameter erläutert werden. Die Referenz dazu finden Sie in der MSDN von Microsoft (https://msdn.microsoft.com/de-de/library/ff976025(v=vs.85).aspx). Hier ging es zunächst nur um die grundliegenden Schritte, die zu einer HTML5-Grafik im Webbrowser-Steuerelement führen. Die letzte Zeile der Routine gibt den erzeugten Quelltext des Dokuments über die Methode outerHTML im VBA-Direktfenster aus. Und der ist reichlich übersichtlich:

<html>
   <head></head>
   <body bgcolor="lightgray">
     <canvas width="600" height="400" id="zeichnung"></canvas>
   </body>
</html>

Hier wird offenbar, dass die Zeichenoperationen sich, anders, als bei SVG, in keiner Weise im Quelltext wiederspiegeln. Sie lassen sich durch erneutes Laden des Quelltexts nicht mehr reproduzieren. Die erstellte Grafik ist ein reines Bitmap im Browser. Wie Sie an die Daten dieses Bitmaps herankommen, um sie in einer Datei oder direkt in Tabellenfeldern abzuspeichern, folgt später. Neben Grafikelementen kann HTML5 natürlich auch Texte ausgeben. Beides kombiniert eröffnen sich weite Anwendungsbereiche. Denken Sie an Flussdiagramme, technische Zeichnungen oder auch spezielle Grids, die sich aus den Tabellen der Datenbank speisen. Der einzige Nachteil von HTML5 ist die fehlende Interaktivität. Sie können etwa nicht auf ein Grafikelement klicken und es über ein Ereignis identifizieren. Dafür brauchen Sie die in einem Folgebeitrag dargestellte Alternative SVG.

Diagramme per HTML5 ausgeben

Das Grundwissen ist nun vermittelt und wir schreiten zu einem nützlicheren Beispiel, dem Anlegen von Balken- und Tortendiagrammen über HTML5. Implementiert ist das im Formular frmHTML5Diagram der Beispieldatenbank (s. Bild 3).

Das HTML5-Balkendiagramm zu Umsätzen nach Monaten im Formular frmHTML5Diagram

Bild 3: Das HTML5-Balkendiagramm zu Umsätzen nach Monaten im Formular frmHTML5Diagram

Dessen Funktionen sind schnell beschrieben. Nach dem öffnen finden Sie im Detailbereich zunächst eine weiße Fläche vor, bei der es sich tatsächlich um ein Webbrowser-Steuerelement handelt. Im Formularkopf sind einige Controls untergebracht, die die Anlage der Diagramme steuern. Sie können zum einen wählen, ob ein Balken- oder ein Tortendiagramm generiert werden soll. Zum anderen ermitteln Abfragen auf eine Kunden- und Bestelldatenbank die aggregierten Umsätze nach Monaten oder die Umsätze nach Herkunftsländern der Besteller.

Je nach Auswahl wird dann ein anderes Diagramm über den Button links oben erzeugt. Das fertige Diagramm können Sie über die Schaltflächen rechts als Grafikdatei abspeichern oder auch direkt ausdrucken.

Das zugehörige an die bekannte Nordwind-Datenbank angelehnte Datenmodell zeigt Bild 4. Es gibt Kunden (tblKunden), die Artikel (tblArtikel) bestellen. Die Verknüpfung geschieht über die 1:n-Tabelle tblBestellungen. Jeder Kunde kann ja mehrmals bestellen. Jede Bestellung wiederum kann mehrere Artikel enthalten, weshalb hier zusätzlich die n:m-Tabelle tblBestellDetails benötigt wird.

Am Datenmodell zu Kunden, Bestellungen und Artikeln sind vier relationale Tabellen beteiligt

Bild 4: Am Datenmodell zu Kunden, Bestellungen und Artikeln sind vier relationale Tabellen beteiligt

Zur Auswertung der Umsätze pro Zeiteinheit sind nach diesem Modell alle drei rechten Tabellen zu befragen, denn jene ergeben sich aus der Summe des Felds Einzelpreis in der Artikeltabelle. Das ist warenwirtschaftlich zwar nicht korrekt, weil im richtigen Leben der Bestellpreis eigentlich zusätzlich in der Tabelle tblBestelldetails stehen müsste – beim Versand könnte sich der Artikelpreis ja bereits geändert haben und die Rechnung enthielte dann einen falschen Betrag. Dieses uralte Modell kam so aber bereits in verschiedenen Beispieldatenbanken von Access im Unternehmen zum Einsatz, weshalb wir es hier einfach unverändert übernehmen.

Wie auch immer, die zur Umsatzauswertung gehörige Abfrage stellt Bild 5 dar. Gruppiert wird hier nach dem Bestelldatum, wobei die Format-Funktion Jahre und Monate zusammenfasst. Für den 24.11.2015 etwa ergäbe sich damit der Textausdruck 2015/11. Der Umsatz ergibt sich aus der Summe der Einzelpreise multipliziert mit der Artikelanzahl in tblBestellungen. Der Rabatt wird der Einfachheit halber ignoriert. Das Ergebnis der Abfrage ist eine nach Jahren und Monaten aufsteigend sortierte Liste der Umsätze und damit die Basis für das Diagramm weiter oben.

Zur Auswertung der Umsätze nach Monaten für die Diagramme kommt die Gruppierungsabfrage qry_UmsatzMonat zum Einsatz

Bild 5: Zur Auswertung der Umsätze nach Monaten für die Diagramme kommt die Gruppierungsabfrage qry_UmsatzMonat zum Einsatz

Sollen die Umsätze nach Herkunftsländern der Besteller ausgewertet werden, sind hingegen alle vier Tabellen in die Auswahlabfrage zu integrieren, da das Land ein Attributfeld des Kunden ist. Es ergibt sich die Abfrage aus Bild 6, in der nach dem Feld Land des Kunden gruppiert wird und sich die Umsatzsumme S auf gleiche Weise errechnet, wie bei der zuvor erörterten Abfrage.

Zur Auswertung der Umsätze nach Land für die Diagramme kommt die Gruppierungsabfrage qry_UmsatzLaender zum Einsatz

Bild 6: Zur Auswertung der Umsätze nach Land für die Diagramme kommt die Gruppierungsabfrage qry_UmsatzLaender zum Einsatz

Die Sortierung erfolgt hier aber nach der Umsatzsumme absteigend, wodurch dann das Land mit dem höchsten Umsatz ganz oben steht. Das reflektiert sich dann auch im alternativ erzeugten Diagramm in Bild 7, das zudem eine andere Farbgebung aufweist. Beide Diagramme werden aber über den gleichen VBA-Code generiert, der in ein Klassenmodul clsHTMLDiagram ausgelagert ist. über Eigenschaften der Klasse und Parameter der Methodenaufrufe können Sie dann die Gestalt des Diagramms beeinflussen.

ähnlich das HTML5-Balkendiagramm zu Umsätzen nach Ländern im Formular frmHTML5Diagram

Bild 7: ähnlich das HTML5-Balkendiagramm zu Umsätzen nach Ländern im Formular frmHTML5Diagram

Tatsächlich finden Sie im Formularcode nichts, was mit HTML5 zu tun hätte, weil all dies im eingeklinkten Klassenmodul passiert:

Private CDgm As clsHTMLDiagram    ''''Modulkopf
Private CDgmPie As clsHTMLPieChart
Private Sub Form_Load()
     Set CDgm = New clsHTMLDiagram
     Set CDgmPie = New clsHTMLPieChart
End Sub

Die Objektvariable CDgm wird formularglobal deklariert und beim Laden des Formulars erzeugt. Dasselbe geschieht mit der separaten für das Tortendiagramm verantwortlichen Klasse clsHTMLPieChart, die in der Variablen CDgmPie landet. Klicken Sie auf den Button cmdDiagram mit der Beschriftung Erzeuge Diagramm, so stößt dies die Ereignisprozedur aus Listing 2 an, die schließlich die Diagramme generiert.

Private Sub cmdDiagram_Click()
     Dim rs As DAO.Recordset
     Dim sTitle As String
     Dim bRandom As Boolean
     NewDoc
     Select Case optGrp.Value
     Case 1
         Set rs = CurrentDb.OpenRecordset("qry_UmsaetzeMonat")
         sTitle = "Umsätze nach Monaten"
         bRandom = False
     Case 2
         Set rs = CurrentDb.OpenRecordset("qry_UmsatzLaender")
         sTitle = "Gesamtumsatz nach Ländern"
         bRandom = True
     Case Else
         Exit Sub
     End Select
     If optgrp2.Value = 1 Then
         With CDgm
             .Width = Me!ctlWeb.Object.Width
             .Height = Me!ctlWeb.Object.Height
             .BackColor = RGB(224, 224, 224)
             .Title = sTitle
             .RandomColors = bRandom
             Set .DataRecordset = rs
             If Not .CreateDiagramDoc(Me!ctlWeb.Object) Then
                 MsgBox "Erzeugen des Diagramms schlug fehl"
             End If
         End With
     Else
         With CDgmPie
             .Width = Me!ctlWeb.Object.Width
             .Height = Me!ctlWeb.Object.Height
             .BackColor = RGB(224, 224, 224)
             .Title = sTitle
             .RandomColors = bRandom
             Set .DataRecordset = rs
             If Not .CreateDiagramDoc(Me!ctlWeb.Object) Then
                 MsgBox "Erzeugen des Diagramms schlug fehl"
             End If
         End With
     End If
     rs.Close
End Sub

Listing 2: Die Formularroutine zum Erzeugen des Diagramms

In dieser erfolgt zunächst ein Aufruf der Hilfsprozedur NewDoc, welche im Webbrowser-Steuerelement ctlWeb per about:blank ein neues leeres Dokument anlegt:

Private Sub NewDoc()
     ctlWeb.Object.Navigate2 "about:blank"
     Do
         DoEvents
     Loop Until ctlWeb.Object.ReadyState > 2
End Sub

Im Select-Case-Zweig entscheidet die Routine anhand des in der Optionsgruppe optGrp eingestellten Werts, ob die Umsätze nach Monaten oder nach Ländern ausgegeben werden sollen. Von diesem Wert hängt ab, welche der beiden Abfragen in das Recordset rs geladen wird. Zusätzlichen werden der Titel des Diagramms (sTitle) und die zufällige Farbgebung (bRandom) gesetzt. Der nächste Bedingungszweig auf die Optionsgruppe optGrp2 wertet aus, welche Diagrammart generiert werden soll. Im einen Fall werden Eigenschaftswerte für die Diagrammklasse CDgm eingestellt, im anderen die für die Klasse CDgmPie. Die Methoden der Klassen sind dabei aber identisch.

Sie teilen den Klassen mit, wie breit und hoch das Diagramm werden soll (Width, Height). Die Eigenschaft BackColor stellt dessen Hintergrundfarbe ein, Title den Titeltext links oben im Diagramm. Ob zufällige Farben für die Balken oder Tortenstücke erscheinen sollen, entscheidet RandomColors über einen Boole-Wert. Zuletzt ist lediglich das im ersten Schritt geöffnete Recordset rs über die Methode DataRecordset zuzuweisen. Und nun übernimmt das Klassenmodul die Regie, indem Sie seine Prozedur CreateDiagramDoc aufrufen.

Erst diese führt auf Basis der erfolgten Einstellungen all jene HTML5-Aktionen durch und rendert das Diagramm im als Parameter übergebenen Webbrowser-Objekt. Klappt dies, so gibt die Funktion True zurück. Andernfalls zeigt eine Meldung an, dass kein Diagramm erzeugt werden konnte, wobei allerdings keine Ausgabe des genauen Fehlergrunds erfolgt.

HTML5-Balkendiagrammklasse

Im Modul clsHTMLDiagram finden Sie nur etwas mehr als 200 Zeilen Code vor. Ein großer Teil geht dabei auf Variablendeklarationen und Property-Prozeduren drauf, die selbst keine Aktionen ausführen. Die zwei wirklich aktiven Prozeduren CreateDiagramDoc und deren Subroutine PaintData benötigen nur etwa 120 Zeilen. Gemessen am Ergebnis ist das extrem wenig, wenn man es mit anderen Grafikmethoden vergleicht. Wollten Sie dasselbe mit dem Windows-API erreichen, so könnten Sie bei GDIPlus locker mit etwa dem Fünffachen, bei GDI32 mit dem Zehnfachen rechnen. Dennoch zeigen wir Ihnen hier nicht den kompletten Code des Moduls, sondern konzentrieren uns lieber auf das Wesentliche.

Beginnen wir mit der Zuweisung des Recordsets an die Klasse über die Property-Methode DataRecordset. Die ist die einzige, die neben dem Speichern des Eigenschaftswerts in eine Membervariable (m_RS) noch eine zusätzliche Aufgabe erledigt, nämlich das Ermitteln des Wertebereichs der gelieferten Daten:

Property Set DataRecordset(rs As DAO.Recordset)
     Set m_RS = rs
     m_min = 9 ^ 10
     m_max = -9 ^ 10
     With m_RS
         .MoveFirst
         Do While Not .EOF
             If rs(1).Value > m_max Then m_max = rs(1).Value
             If rs(1).Value < m_min Then m_min = rs(1).Value
             .MoveNext
         Loop
         .MoveFirst
     End With
     If m_min = m_max Then m_min = m_max - 10
End Property

m_min nimmt die untere Grenze des Wertebereichs auf, m_max die obere. Zu erwähnen wäre an dieser Stelle, dass die Klasse zwingend ein maßgeschneidertes Recordset erwartet: Im ersten Feld vom Typ Text muss die Beschriftung des Datenpunkts stehen, im zweiten dessen Wert als Ganz- oder Gleitkommazahl. Gleitkommazahlen werden allerdings auf den Vorkommateil gerundet, was folglich die Anwendung auf Wertebereiche von mindestens 10 und aufwärts beschränkt. Der Name der Felder im Recordset spielt indessen keine Rolle, da auf sie über deren Indizes 0 und 1 zugegriffen wird. Beispieldaten sähen damit etwa so aus:

Feld1        Feld2
Polen        12564,88
Frankreich        32211,24
Belgien        8923,47

In einer Schleife werden alle Datensätze des Recordsets durchlaufen und der niedrigste Wert des zweiten Felds (rs(1)) in m_min gespeichert, der höchste in m_max. Diese Werte werden später benötigt, wenn es an die automatische Dimensionierung der Diagrammbalken und die Positionierung der Achsenbeschriftungen geht.

Listing 3 zeigt nun die wesentlichen Bestandteile der Prozedur CreateDiagramDoc zum Erzeugen des Diagramms – oder genauer: der Anlage des HTML5-Kontexts. Erst der Aufruf der Subroutine PaintData zum Schluss führt dann zum eigentlichen Zeichnen des Diagramms. Die meistens Vorgänge in der Routine entsprechen dem, was bereits zum Listing 1 erläutert wurde.

Function CreateDiagramDoc(Browser As WebBrowser) As Boolean
     Dim oDiv As HTMLDivElement
     Dim oElement As IHTMLElement
     
     On Error Goto Fehler    
     Set oDoc = Browser.Document
     oDoc.body.setAttribute "bgColor", ColorToStr(vbWhite)
     
     Set oDiv = oDoc.createElement("div1")
     oDiv.Id = "div1"
     oDoc.body.appendChild oDiv
     
     Set oElement = oDoc.createElement("canvas")
     oElement.Id = "zeichnung"
     Set oCanvE = oElement
     oCanvE.Width = m_Width: oCanvE.Height = m_Height
     oDiv.appendChild oElement
     
     Set CTX = oDoc.getElementById("zeichnung").getContext("2d")
     PaintData
     CreateDiagramDoc = True
     Exit Function
Fehler:
End Function

Listing 3: Diese Prozedur der Klasse erzeugt den HTML5-Kontext

Die Klassenvariable oDoc nimmt das leere Dokument des Webbrowsers entgegen. über das Attribut bgColor wird nun die Hintergrundfarbe des body-Elements auf Weiß eingestellt. Folgend legt die Methode createElement einen DIV-Bereich im Dokument an, der als Container für die HTML5-Grafik dienen soll. Die wird über den canvas erzeugt, welcher mit der ID zeichnung versehen ist, dem DIV-Bereich, der nun in der Objektvariablen oDiv steht, hinzugefügt (appendChild). Breite und Höhe des HTML5-Canvas werden an dieser Stelle auch schon eingestellt (Width, Height). Zusätzlich erhält die Klassenvariable oCanvE eine Kopie dieses Canvas-Objekts, weil ein Verweis auf ihn später noch von anderen Prozeduren benötigt wird.

Abschließend nimmt die Klassenvariable CTX noch den Grafikkontext von HTML5 auf. Auch dieser wird später noch weiterverwendet. Die Deklaration der drei Klassenvariablen sieht so aus:

Private WithEvents oDoc As HTMLDocument
Private CTX As ICanvasRenderingContext2D
Private oCanvE As IHTMLCanvasElement

oDoc ist deshalb mit WithEvents deklariert, weil auf Ereignisse des Browser-Dokuments reagiert werden soll. So etwa auf das Klicken mit der rechten Maustaste, was üblicherweise das Kontextmenü des Internet Explorers hervorbringt. Die folgende kleine Ereignisprozedur aber unterbindet dies:

Private Function oDoc_oncontextmenu() As Boolean
     ''''
End Function 

Damit sind alle Vorarbeiten erledigt, der HTML5-Kontext angelegt, und es kann an das Zeichnen des Diagramms gehen. Das übernimmt nun die Prozedur PaintData.

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

Testzugang

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

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar