Zeichnen in Formularen mit Lebans Picturebox-Klasse
Donnerstag 12. Februar 2009 von Andreas Vogt
Bekannter Weise gibt es für Formular-Objekte keine Zeichen-Befehler wie line() oder Circle(), dies ist den Berichts-Objekten vorbehalten. Das kann man ärgerlich finden oder nicht, ändern kann man das eh nicht. “Man” sucht also nach Alternativen – der Kunde will das schließlich.
Fündig wird man wie so oft bei Stephen Lebans, einem Access Enthusiast der sein Wissen bereitwillig teilt. Auf http://www.lebans.com/imageclass.htm findet man für Access 97 und 2000+ 2 Downloads namens PictureBoxA97.zip und PictureBoxA2K.zip.
Der Download besteht aus einer Beispieldatenbank die die Funktionsweise demonstriert. Für eigene Projekte sind die beiden Klassenmodele clsPictureBox und clsVertices relevant, die man am besten gleich in eine leere Access-Datei kopiert.
Meine Aufgabenstellung war, anhand von Konstruktionsangaben offene und geschlossene Grundrisszeichnungen darzustellen. Die Angaben waren Kundenspezifisch und bestehen aus Innenwinkel und Länge von Linien, also eine relative Polarkoordinaten. Ein Quadratischer Grundriss der Länge 1000 hat daher die folgenden Angaben:
(90/1000), (90/1000), (90/1000), (90/1000)
Gibt man noch eine Tiefe von 200mm vor, dann sieht die generierte Zeichnung so aus:

Die Berechnungen die dahinterstehen sind ziemlich komplex, daher kann ich nur auf die wesentliche Schritte eingehen. Der erste Schritt ist die relative Polarkoordinaten in ein Kartesisches System zu überführen. Der Startpunkt wird dabei auf 0/0 gesetzt. Die Koordinaten werden in einem mehrdimensionellen Array gespeichert, die erste Dimension läuft von 1 bis Anzahl DS, die zweite von 0 bis 1, wobei 0 die x-Komponente und 1 die y-Komponente darstellt. Die Umrechnung erfolgt durch die Cosinus- und Sinus-Funktion.
Global Const SSB_RAD = 57.2957795130823
Global pxy AS Variant
Global pab AS Variant
...
Public Sub convert2XY()
Dim WSum As Double, WB As Double, AL As Double, AW As Double, i As Long
Dim rs As DAO.Recordset
Set rs = forms!Grundriss.Form.RecordsetClone
i = 1
WSum = 180
rs.MoveLast
ReDim pxy(1 To DS.RecordCount, 1)
ReDim pab(1 To DS.RecordCount, 1)
rs.MoveFirst
pxy(1, 0) = 0
pxy(1, 1) = 0
Do Until rs.EOF
If i > 1 Then
WSum = WSum - 180 + AW
WB = WSum / SSB_RAD
pxy(i, 0) = pxy(i - 1, 0) + AL * Cos(WB)
pxy(i, 1) = pxy(i - 1, 1) + AL * sIn(WB)
End If
AW = rs!Winkel
If Not IsNull(rs!Laenge) Then AL = -rs!Laenge
i = i + 1
rs.MoveNext
Loop
End Sub
Weitere Funktionen die ich hier nicht nennen möchte ist die Überprüfung der Daten auf Plausibilität und Vollständigkeit. Dabei wird auch überprüft ob sich Grundrisslinien überschneiden.
Der nächste Schritt ist anhand der Tiefenangabe den “inneren” Linienzug zu berechnen und die Koordinaten im Array pab() abzuspeichern, das zuvor dimendioniert wurde.
Public Sub Get2Points(tiefe)
Dim rs As DAO.Recordset
Dim WSum As Double, WH As Double, WB As Double, AL As Double, i As Long
Set rs = Forms!Grundriss.RecordsetClone
WSum = 180
i = 1
DS.MoveFirst
Do Until rs.EOF
WH = rs!Winkel / 2
WSum = WSum - 180 + WH
WB = (WSum + IIf(tiefe < 0, 180, 0)) / SSB_RAD
AL = -Abs(tiefe) / Sin(WH / SSB_RAD)
pab(i, 0) = pxy(i, 0) + AL * Cos(WB)
pab(i, 1) = pxy(i, 1) + AL * Sin(WB)
WSum = WSum + WH
i = i + 1
rs.MoveNext
Loop
DS.Close
End Sub
Der nächste Schritt ist dann die Initialisierung des Image-Controls mittels der PictureBox-Klasse von Stephen Lebans, dem ich an dieser Stelle mal ausdrücklich danken möchte für seine hervorragende Arbeit in den zur Verfügung gestellten Beispielen.
If pb Is Nothing Then
Set pb = New clsPictureBox
pb.ImageControl = Me!Image0
pb.ImageForm = Me
pb.BackColor = RGB(255, 255, 255)
pb.Clear
ScaleAmt = 1
Else
pb.Clear
End If
Es wird eine Objektvariable pb gesetzt, mit der dann auf die Eigenschaften und Methoden der Klasse zugegriffen wird. Me!Image0 ist das Bildsteuerelement, das sich auf dem Formular befinden muss. Hintergrund wird auf Weiss gesetzt und das Bild geleert. Ist das Klassenobjekt schon initialisiert, wird lediglich ein pb.Clear zum Leeren abgesetzt.
Die Höhe der PictureBox wird mit pb.dib_height ermittelt, die Breite mit pb.dib_width. Damit, sowie mit der maximalen Breite und Höhe des Linienzuges, wird ein skalierungs-Faktor berechnet, mit dem alle x/y Koordinaten des inneren und äußeren Linienzuges multipliziert werden:
Public dibW As Long
Public dibH As Long
...
OffsetX = 50
OffsetY = 50
Faktor = 1
If dibH < disY Then FaktorY = dibH / disY
If dibW < disX Then FaktorX = dibW / disX
If FaktorX < FaktorY Then
Faktor = FaktorX
ElseIf FaktorX = 0 And FaktorY = 0 Then
Faktor = 1
Else
Faktor = FaktorY
End If
For i = 1 To UBound(pxy)
If IsNull(pxy(i, 0)) Or IsEmpty(pxy(i, 0)) Or IsNull(pxy(i, 1)) Or IsEmpty(pxy(i, 1)) Then GoTo weiter
pxyz(i, 0) = pxy(i, 0) * Faktor
pxyz(i, 1) = pxy(i, 1) * Faktor
pabz(i, 0) = pab(i, 0) * Faktor
pabz(i, 1) = pab(i, 1) * Faktor
weiter:
Next i
disX und disY sind die maximalen Distanzen in x- bzw. y-Richtung. Es ergeben sich 2 Faktoren, FaktorX und FaktorY. Damit die Grafik komplett in der ImageBox angezeigt wird, muss der kleinere der beiden verwendet werden. Auf die Deklarierung der restlichen Variablen wird hier verzichtet.
Bei der Skalierung werden die neuen Koordinaten in ein neues Array pxyz() bzw. pabz() gespeichert. Die Dimensionierung dieser beiden Arrays erfolgt ebenfalls in der Prozedur convert2XY().
Bleibt als finaler Akt noch das Zeichnen beider Linienzüge. Dies geschieht in zwei Schleifen. In der ersten Schleife werden der äußere und innere Linienzug gezeichnet, in der zweiten Schleife werden jeweils die korospondierenden Punkte beider Linienzüge verbunden, um die Sichtkanten anzuzeigen.
Also wenn ein Linienzug die Punkte A bis F besitzt, und der zweite Linienzug die Punkte A' bis F', dann werden die Punkte A/A', B/B', C/C' usw. verbunden.
Die Methode zum Zeichnen von Linen ist:
DrawLine(x1 As Long, y1 As Long, x2 As Long, y2 As Long, _
Optional TempColor As Long = 0)
Abschließend mal ein Beispiel einer etwas komplexeren Zeichnung:

Die realen Anwendungsfälle für diese Picturebox sind sicherlich nicht sehr zahlreich. Ich könnte mir vorstellen dass man dies für Grundrisse aller Art, - z.b. Gebäude, Grundstücke etc. - oder auch für flächige Geometrien, z.B. für die Darstellung von Blechstanzteile oder gelaserte Blechteile etc verwenden kann.
Eines ist die PictureBox aber nicht: Es ist kein CAD-Programm
AV 2/2009
Dieser Beitrag wurde erstellt am Donnerstag 12. Februar 2009 um 14:29:19 und abgelegt unter Formulardesign, VBA Code. Kommentare zu diesen Eintrag im RSS 2.0 Feed. Sie können einen Kommentar schreiben, oder einen Trackback auf Ihrer Seite einrichten.
PDF Version