Zeichnen in Formularen mit Lebans Picturebox-Klasse
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