Erzeugen Sie Grafiken mit Visio und Visual Basic
Veröffentlicht: 08. Sep 2001 | Aktualisiert: 18. Jun 2004
Von Ken Spencer
Die grafische Darstellung von Daten kann man oft auch an Visio delegieren, wodurch eine ansprechende Optik schnell und bequem realisiert werden kann.
Auf dieser Seite
Grafikausgabe per Programm
Die Arbeit mit Visio
Fazit
Diesen Artikel können Sie hier lesen dank freundlicher Unterstützung der Zeitschrift:
Grafikausgabe per Programm
Microsofts Visio war schon seit jeher gut per Programm ansteuerbar. Da ich sehr viele Anfragen erhalte, die sich um die Ansteuerung von Visio drehen, möchte ich dieses Thema einmal eingehend behandeln.
In vielen Anwendungen ist es sinnvoll, den zu beschreibenden Sachverhalt grafisch darzustellen. Wenn Sie zum Beispiel Empfänge ausrichten, müssen Sie sich vermutlich bei der einen oder anderen Gelegenheit Gedanken über die Sitzordnung machen. Eine entsprechende Karte können Sie zum Beispiel erstellen, indem Sie die darzustellenden Informationen im Listenformat erfassen und die Karte mit einem Werkeug wie Visio zeichnen. Besser aber wäre es, sie von Visio automatisch generieren zu lassen.
Die letztere Variante ist natürlich vorzuziehen. Als Beispiel für diesen Vorgang möchte ich nun die Erstellung solcher Sitzordnungskarten genauer beschreiben. Das Beispielprogramm habe ich ursprünglich in Visual Basic geschrieben, und zwar bereits 1995. Damals habe ich es in meinem Buch "Client/Server Programming with Microsoft Visual Basic" (Microsoft Press, 1995) benutzt. Ich dachte mir, es könnte interessant werden, diese Anwendung nun mit Visual Basic 6.0 und Visio 2000 zu implementieren.
Die Anwendung greift auf Daten zurück, die in einer Access-Datenbank liegen (Visitor2000.mdb, zusammen mit der Anwendung auf CD). In dieser Datenbank gibt es die beiden Tabellen Visitmaster und VisitorDemographics. Die Visitmaster-Tabelle enthält Informationen über die Aktivitäten, also Picknicks, Konferenzen und so weiter. Die VisitorDemographics-Tabelle ist über das Feld VisitName mit der Visitmaster-Tabelle verknüpft und enthält Informationen darüber, wer als Teilnehmer für das betreffende Ereignis vorgesehen ist. Außerdem wird in der VisitorDemographics-Tabelle das Geschlecht des Teilnehmers erfasst, der Abteilungsname und der Umstand, ob der Teilnehmer Wein zum Essen wünscht oder nicht.
Wenden wir uns dem Code zu. Ein Teil der Anwendung ist natürlich damit beschäftigt, die erforderlichen Informationen aus der Access-Datenbank auszulesen. Der Originalcode wurde mit RDO geschrieben (Remote Data Objects). Also habe ich nun den RDO-Code gelöscht und durch ADO-Code (ActiveX Data Objects) ersetzt, um die Dinge zu beschleunigen. Allerdings möchte ich an dieser Stelle nicht weiter auf den Datenbankcode eingehen, weil es ganz normales ADO ist und wohl jeder Leser schon weiß, wie man mit ADO umgeht.
B1 Auswahl des Ereignisses
Bild B1 zeigt die Schnittstelle der Anwendung. Die drei Befehlsschaltflächen im oberen linken Teil des Formulars erledigen die Arbeit mit Visio. Der Anwender braucht nur aus einer Auswahlliste das Ereignis auszuwählen, das ihn interessiert, und dann die Schaltfläche Set the Table anzuklicken. Dann wird Visio initialisiert und übernimmt das Zeichnen der Karte. Sobald Visio fertig ist, kann der Anwender das Ergebnis ausdrucken oder als Grafikdatei speichern, indem er die entsprechende Schaltfläche drückt.
Sobald der Anwender die Schaltfläche Set the Table anklickt, wird die SetTheTable-Funktion aufgerufen. Sie sorgt für die Erstellung der Grafik in Visio. Der Name des Teilnehmers wird von der cboVisitorList an die Prozedur übergeben. Sobald die Funktion fertig ist, sorgt die Funktion EnableCmdButtons dafür, dass alle drei Schaltflächen im Steuerelemente-Array freigeschaltet werden.
Bild B2 zeigt, wie das Ergebnis in Visio aussehen sollte. Die resultierende Visio-Grafik zeigt einen Tisch, die Anordnung der Stühle und die Verteilung der Sitzplätze. Bei jedem besetzten Stuhl ist der Name des Teilnehmers vermerkt, die Abteilung und seine Entscheidung, was den Wein anbetrifft. Außerdem sitzen die Teilnehmer immer neben Angehörigen des anderen Geschlechts, erkennbar an den kleinen Bildchen auf den Stühlen.
B2 Die Sitzordnung in der Visio-Darstellung.
Die Arbeit mit Visio
Den Hauptteil der Arbeit übernehmen die Methoden im Modul modTableSetting. Ich habe die Attribute für die Anwendung als Objekte definiert und im Deklarationsabschnitt untergebracht. Dadurch sind sie in der Wartungsphase der Anwendung leichter zu finden.
Ich möchte nun nicht auf alle Einzelheiten eingehen, aber mir scheint es sinnvoll zu sein, zumindest die wichtigsten Definitionen zu beschreiben. Im Quelltext finden Sie zu den Objekten, die für Visio benutzt werden, entsprechende Kommentarzeilen mit kurzen Beschreibungen.
Der VisitorType ist ein anwenderdefinierter Typ, der die Informationen aus einem Teilnehmerdatensatz aufnimmt. Die Anwendung speichert den Namen, die Abteilung und die Entscheidung bezüglich des Weins:
Public Type VisitorType Name As String Department As String Wine As String End Type
Die Arrays MaleVisitors und FemaleVisitors enthalten Elemente dieses Typs. Da die Anwendung mit diesen beiden Arrays arbeitet, muss sie die einzelnen Datensätze natürlich beim Einlesen aus der Datenbank entsprechend zuordnen. Immerhin wird durch diesen zusätzlichen Aufwand die spätere Zuordnung der Teilnehmer bei der Erstellung der Grafik vereinfacht (in der Funktion SetTheTable). Die nächsten beiden Zeilen definieren die Arrays MaleVisitors und FemaleVisitors. In beiden Arrays sind die Elemente vom Typ VisitorType:
Dim MaleVisitors(1 To 9) As VisitorType Dim FemaleVisitors(1 To 9) As VisitorType
Die nächste Zeile definiert zwei Zähler, in denen die Zahl der Teilnehmer erfasst wird.
Dim CounterMale As Integer, CounterFemale As Integer
SetTheTable ist die wichtigste Funktion der Anwendung. Sie erledigt den wesentlichen Teil der Arbeit oder sorgt zumindest für deren Durchführung. Die folgenden drei Befehle legen einige Variablen an, die in dieser Funktion benutzt werden:
Sub SetTheTable(VisitName As String) Dim sCurrentSex As String Dim sCurrentName As String, sCurrentDepartment As String Dim sWine As String
Die folgenden Konstanten werden zur Berechnung der SmartShapes-Positionen bei 0 oder 45 Grad benutzt. Die Multiplikation mit 12 dient zur Umrechnung der Werte auf Inch.
Const Degree0 = 3 * 12 Const Degree45 = 2.5 * 12
Die nächsten beiden Zeilen vervollständigen die Definitionen für diese Funktion. Sie definieren eine Variable für die Ereignisbeschreibung und eine weitere für den Anwendungspfad:
Dim VisitDescription As String Dim sPath As String
Nun folgen einige Zeilen, mit denen die Positionen der verschiedenen Objekte in der Zeichnung festgelegt werden. Zuerst geht es um die Mitte des Tisches:
YCenter = 8.5 * 12 XCenter = 11 * 12
Anschließend bringen die Variablen XPos und YPos jedes Objekt an seinen vorgesehenen Platz. Für den Anfang erhalten die Variablen XPos und YPos ihre Standardwerte:
XPos = XCenter YPos = YCenter + Degree45
Mit dem frmSetTable.labStatus-Steuerelement als Status-Etikett beschreibe ich die Aktionen des ActiveX-Servers während des Vorgangs Set the Table.
frmSetTable.labStatus = "Loading table from database"
Im Code werden Ihnen die eingestreuten DoEvents-Anweisungen auffallen. Sie sollen der Anwendung die Aktualisierung des Status-Etiketts ermöglichen, während die Dinge im Programm ihren Gang gehen. Falls Sie nach einer Textänderung DoEvents vergessen, kann es geschehen, dass sich die Änderung erst dann auf dem Bildschirm bemerkbar macht, wenn der betreffende Vorgang abgeschlossen ist.
Anschließend wird die Funktion LoadVisitorTables mit VisitName als Argument aufgerufen.
LoadVisitorTables VisitName
Das veranlasst LoadVisitorTables, die Gästeliste aus der Datenbank in die zwei Arrays zu laden.
Anschließend wird die Funktion GetDescription aufgerufen. Sie liest die Beschreibung des Ereignisses aus der Datenbank.
VisitDescription = GetDescription(VisitName) frmSetTable.labStatus = "Starting Visio"
An dieser Stelle starte ich Visio mit der Methode StartVisio. Dann setze ich die Fehlerbearbeitung zurück.
On Error GoTo 0 frmSetTable.labStatus = "Creating new document in Visio"
Im nächsten Schritt wird die Variable sPath auf den aktuellen Pfad der Anwendung gesetzt, an den sich der Name der Visio-Vorlage anschließt. Ich benutze folgenden Angabe:
sPath = App.Path & "\TableSetting.vst"
Die folgende Zeile eröffnet mit der Add-Methode eine neue Zeichnung. Diese Zeichnung beruht auf der Vorlage TableSetting.vst.
Set objTableDiagram = Visio.Documents.Add(sPath).Pages(1)
Die nächste Zeile legt eine Referenz auf die Schablone an, die in der Zeichnung benutzt werden soll.
Set objTableSettingStencil = Visio.Documents _ ("Table Setting Stencil.vss")
Nach diesen Vorbereitungen setzt der Code objDocument auf das ActiveDocument-Property von Visio, um die neue Zeichnung zu repräsentieren.
Set objDocument = Visio.ActiveDocument frmSetTable.labStatus = "Adding table to new document"
Das objDocument wird am Ende der Funktion nicht entsorgt, damit es noch in anderen Teilen der Anwendung Verwendung finden kann.
Der folgende Befehl ist in meinem Beispiel auskommentiert. In einer Produktionsanwendung würde ich das Kommentarzeichen löschen, um die Visio-Schnittstelle beim Positionieren der Objekte abzuschalten. Dadurch wird das Programm nämlich bedeutend schneller, weil Visio dann bis zum Ende der Funktion keine Ausgaben auf dem Bildschirm mehr vornimmt.
'Visio.ScreenUpdating = False
Anschließend beginnt der Code mit der Positionierung der Objekte auf der Zeichnung. Zuerst wird der Tisch in die Mitte gerückt. Das Property stTemp enthält den Namen der Vorlage für meinen Tisch. Die nächste Anweisung legt eine Objektreferenz auf diese Vorlage an (in der Sammlung Masters).
stTemp = """Round Table 60""" Set shMaster = objTableSettingStencil.Masters(stTemp) Die Drop-Methode legt das Objekt auf die Zeichnung ab: Set objTableRound60 = objTableDiagram.Drop(shMaster, _ XCenter, YCenter) frmSetTable.labStatus = "Adding title to new document"
Der nächste Codeabschnitt legt den Titel für die Zeichnung fest. Hierzu werden sieben Fuß zum YCenter-Wert addiert (in Zoll umgerechnet), damit der Titel oberhalb der Zeichnung erscheint. Das Text-Property wird in der letzten Anweisung auf den gewünschten Text gesetzt.
stTemp = "Title" Set shMaster = objTableSettingStencil.Masters(stTemp) Set objTitle = objTableDiagram.Drop(shMaster, XCenter, _ YCenter + (7 * 12)) objTitle.Text = "Table Setting for " & VisitDescription
Die folgenden Zeilen initialisieren verschiedene Variablen.
iTableCount = 1 sCurrentSex = "F" CounterMale = 1 CounterFemale = 1
Eine While-Schleife läuft über alle am Tisch verfügbaren Plätze.
While iTableCount <= 8 sCurrentName = ""
Eine Select Case-Anweisung hilft bei der Berechnung der Positionen. Die Werte der X- und Y-Koordinaten hängen von der Lage des Sitzplatzes am Tisch ab. Hier dienen die Degree-Konstanten zur Positionierung der Stühle (Listing L1).
L1 Positionsbestimmung
Select Case iTableCount Case 1: XPos = XCenter YPos = YCenter + Degree0 Case 2: XPos = XCenter + Degree45 YPos = YCenter + Degree45 Case 3: XPos = XCenter + Degree0 YPos = YCenter Case 4: XPos = XCenter + Degree45 YPos = YCenter - Degree45 Case 5: XPos = XCenter YPos = YCenter - Degree0 Case 6: XPos = XCenter - Degree45 YPos = YCenter - Degree45 Case 7: XPos = XCenter - Degree0 YPos = YCenter Case 8: XPos = XCenter - Degree45 YPos = YCenter + Degree45 End Select frmSetTable.labStatus = "Adding individual - number: " & iTableCount
Eine If-Anweisung bestimmt anhand der Variablen sCurrentSex das Geschlecht des Gastes und setzt dann den Richtigen an den freien Platz (Listing L2).
L2 Mann oder Frau?
If sCurrentSex = "M" And CounterMale <= 9 Then sCurrentName = MaleVisitors(CounterMale).Name sCurrentDepartment = _ MaleVisitors(CounterMale).Department sWine = MaleVisitors(CounterMale).Wine stSex = "Men" CounterMale = CounterMale + 1 sCurrentSex = "F" Else If CounterFemale <= 9 Then sCurrentName = FemaleVisitors(CounterFemale).Name sCurrentDepartment = _ FemaleVisitors(CounterFemale).Department sWine = FemaleVisitors(CounterFemale).Wine stSex = "Women" CounterFemale = CounterFemale + 1 sCurrentSex = "M" End If End If
Dann lege ich für jeden Gast eine Referenz auf das dem Geschlecht entsprechende Symbol an. Zuvor wurde das Property stSex auf Men oder Women gesetzt. Wie Sie wohl schon ahnen, heißen auch die beiden Bildchen Men und Women. Wie in den vorigen Anweisungen werden zur Positionierung des Symbols und zur Beschriftung die Properties Drop und Text benutzt.
If Len(sCurrentName) > 0 Then Set shMaster = objTableSettingStencil.Masters(stSex) Set Shape = objTableDiagram.Drop(shMaster, _ XPos, YPos) stLabel = sCurrentName & " - " & sCurrentDepartment stLabel = stLabel & " (" & sWine & ")" Shape.Text = stLabel
Die nächsten Zeilen setzen den Text auf Fettdruck, mit Ausnahme der Anzeige "Wine".
IndexCounter = Shape.Shapes.Count Set objTextShape = Shape.Shapes(IndexCounter) Set objChars = objTextShape.Characters objChars.end = InStr(objTextShape.Text, "(") - 1 objChars.CharProps(visCharacterStyle) = visBold iTableCount = iTableCount + 1 Else iTableCount = 10 ' verlasse die Schleife End If Wend
Den letzten Zeilen der Funktion bleiben die Aufräumarbeiten überlassen:
frmSetTable.labStatus = "Finished" Visio.ScreenUpdating = True End Sub
Wie schon erwähnt, erfolgt der Start von Visio mit der Methode StartVisio. Die Kapselung einer Programmfunktion (wie zum Beispiel den Start eines ActiveX-Servers) in einer eigenen Methode ist eine gute Programmierpraxis. Durch die Abtrennung dieses Codes können Sie die erforderlichen Fehlerüberprüfungen durchführen und brauchen keine anderen Methoden mit Codezeilen zu überschwemmen, die eigentlich nicht dort hingehören.
Sub StartVisio() On Error Resume Next
Sofern Visio bereits läuft, sorgt die nächste Anweisung für die erforderliche Visio-Objektreferenz. Falls Visio noch nicht läuft, tritt ein Fehler auf, der zur Weiterführung des Programms mit der nächsten Anweisung führt. Die If-Anweisung prüft den Fehler, indem sie den Err-Wert überprüft, Visio mit CreateObject startet und für die Objektreferenz sorgt.
Set Visio = GetObject(, "Visio.Application") If Err Then Set Visio = CreateObject("Visio.Application") End If End Sub
Die Ereignisroutine cmdSetTable_Click des Formulars ist für die drei Schaltflächen zuständig, die für die Zusammenarbeit mit Visio vorgesehen sind. Es war bereits die Rede davon, wie die Schaltfläche Set the Table funktioniert. Werfen wir einen kurzen Blick auf die Speicherung und den Druck der Dokumente, die mit Visio erzeugt werden. Wenn der Anwender die Schaltfläche Save the Table anklickt, legt der Code den Pfad für das neue Dokument fest und ruft dann die SaveAs-Methode des ActiveDocument-Objekts auf:
sFileName = App.Path & "\" & txtFileName objDocument.SaveAs sFileName
Klickt der Anwender die Schaltfläche Print the Table an, wählt der Code die auszudruckende Seite aus (die erste Seite natürlich) und ruft dann die Print-Methode der Page-Referenz auf, die von der Pages-Sammlung geliefert wurde.
Set objPage = objDocument.Pages(1) junk = objPage.Print
Damit wären die wichtigsten Aspekte des Codes besprochen. Wenn ein Anwender mit diesem Programm arbeitet, kann er ihm die Arbeit überlassen, insbesondere die Zeichenarbeit. Außerdem kann er nach der Erstellung der Zeichnung nach Visio wechseln und das Ergebnis überarbeiten. Insgesamt wird so die grafische Darstellung von Daten aus einer Datenbank relativ einfach und flexibel.
Fazit
Einer der interessanten Aspekte dieser Anwendung ist das Objektmodell von Visio. Wie erwähnt, wurde die Anwendung bereits 1995 geschrieben, und zwar mit einer ganz anderen Version von Visio. Ich brauchte keine einzige Codezeile zu ändern, damit sie mit Visio 2000 funktioniert. Das dürfte wohl für die Stabilität des Objektmodells von Visio sprechen. Der Code funktioniert unter anderem auch deswegen noch, weil die Verbindung zu Visio mit später Bindung erfolgt. Ich weiß zwar, dass die späte Bindung langsam ist, aber immerhin geht es hier um einen Client und nicht um eine Server-Anwendung mit einer großen Zahl von Nutzern. Die reine Laufgeschwindigkeit ist also nicht der entscheidende Punkt.
Ich habe die Objektreferenzen für Visio überprüft, die sich in Visual Basic benutzen lassen. Ich stieß auf 14 Objekte, die dem Entwickler eine Menge Optionen zur Lösung von grafischen Problemen mit Visio geben.
Bevor Sie mit dem Beispielcode arbeiten können, müssen Sie noch die SetTheTable-Funktion so abändern, dass sie mit allen Einträgen (den Gästen) aus der Datenbank arbeiten kann. In der aktuellen Form funktioniert der Code nur mit einer Tabelle und acht Gästen oder weniger.