Das PictureBox-Steuerelement, genauer das bei dessen Paint-Ereignis oder in der Methode OnPaint() übergebene Graphics-Objekt (Feld im Parameter PaintEventArgs), ist über geometrische Transformationen (TranslateTransform() und weitere, Matrix) in der Lage eine Skalierung vorzunehmen. Hierbei werden jedoch auch die Breite das Zeichen-Objekt Pen bei Vergrößerungen (ScaleTransform()) mit transformiert. Wenn man eine echte Vergrößerung der Grafik möchte, ist dies das korrekte Verhalten. Hat man aber z.B. eine Messwert-Kurve, möchte man zwar bei einer Vergrößerung die X- und Y-Achse strecken, aber nicht die Liniendicke der Messkurve. Gleiches gilt für Markierungen (z.B. kleine Quadrate, die man an den Linien anbringt. Und zuletzt vergesse ich immer wieder, wie das mit der Skalierung funktioniert ...
Das Steuerelement UrsScaledPictureBox schafft Abhilfe, wie die folgenden Grafik zeigt:
Ein anderes Beispiel zeigt die folgende Bilderfolge. Die Achsen wurden skaliert ohne die Strichdicke oder die Größe der Markierungen zu ändern.
Im Wesentlichen handelt es sich bei der UrsScaledPictureBox um eine von PictureBox abgeleitete und erweiterte Klasse. Es stehen Methoden zur Skalierung, zum Zeichnen, für Transformationen und weitere Ereignisse (Events) zur Verfügung. Weitere Methoden lassen sich relativ einfach ergänzen (siehe unten).
Zur Steuerung der Skalierung wurden folgende Eigenschaften hinzugefügt:
Eigenschaft | Funktion | Anmerkung |
---|---|---|
Origin As PointF |
Ruft den Ursprung des skalierten Koordinatensystems ab oder legt ihn fest. | Änderungen lösen das Ereignis ScaleChanged aus. |
ScaledSize As SizeF |
Ruft die skalierte Größe der UrsScaledPictureBox ab oder legt sie fest. | Änderungen lösen das Ereignis ScaleChanged aus. |
ScaledRight As Single |
Ruft die X-Koordinate des rechten oberen Eckpunkts ab. | Es handelt sich um die skalierte Koordinate. |
ScaledTop As Single |
Ruft die Y-Koordinate des rechten oberen Eckpunkts ab. | Es handelt sich um die skalierte Koordinate. |
TransformMatrix As Drawing2D.Matrix |
Ruft eine Kopie der geometrischen globalen Transformationsmatrix ab, die für die Skalierung bei den in dieser Klasse definierten Zeichenfunktionen genutzt wird. | ReadOnly |
DrawCoordinates As Boolean |
Ruf einen Wert ab, der angibt, ob im Design-Modus die Koordinaten der Eckpunkte eingeblendet werden, oder legt ihn fest. | True, wenn die Koordinaten eingeblendet werden sollen, ansonsten False. |
ScalingMode as ScalingModes | Legt das Verhalten bei Größenänderungen fest. | Zoom: Die skalierte Größe bleibt unverändert. Die
Zeichnung wird vergrößert oder verkleinert. Extend: Die skalierte Größe ändert sich proportional zur Größe der Box. Die Zeichnung behält ihre Größe. Die Wertebereich der Achsen der Box vergrößert bzw. verkleinert sich. |
Die Funktion von DrawCoordinates: (X und Y sind unterschiedlich skaliert) |
Ereignis | Funktion |
---|---|
ScaleChanged As EventHandler |
Wird ausgelöst, wenn sich der Wert der ScaledSize- oder der Origin-Eigenschaft ändert. |
Methode | Wirkung | Anmerkung |
---|---|---|
DrawLine | Zeichnet eine verbindende Linie zwischen zwei Point-Strukturen. | Beide Punkte werden transformiert. |
DrawLines | Zeichnet eine Reihe von Liniensegmenten, die ein Array von Point-Strukturen verbinden. | Sämtliche Punkte werden transformiert. |
DrawCurve | Zeichnet eine Cardinal-Splinekurve durch ein angegebenes Array von Point-Strukturen. | Sämtliche Punkte werden transformiert. |
DrawRectangle | Zeichnet ein Rechteck, das durch ein Koordinatenpaar, eine Breiten- und eine Höhenangabe angegeben ist. | Der Ursprung wird transformiert, Breite und Höhe bleiben erhalten. |
FillRectangle | Füllt das Innere eines Rechtecks, das durch ein Koordinatenpaar, eine Höhen- und eine Breitenangabe
angegeben ist. |
Der Ursprung wird transformiert, Breite und Höhe bleiben erhalten. |
DrawEllipse | Zeichnet eine Ellipse, die durch ein umschließendes Rechteck definiert ist, das durch Koordinaten für die obere linke Ecke des Rechtecks, eine Höhen- und eine Breitenangabe angegeben ist. | Der Ursprung wird transformiert, Breite und Höhe bleiben erhalten. |
FillEllipse | Füllt das Innere einer Ellipse, die durch ein umschließendes Rechteck definiert ist, das durch ein Koordinatenpaar, eine Höhen- und eine Breitenangabe angegeben ist. | Der Ursprung wird transformiert, Breite und Höhe bleiben erhalten. |
Typ | Member | Funktion |
---|---|---|
PointF | Transform(x As Single, y As Single) | Erstellt aus den angegeben skalierten Koordinaten ein PointF-Objekt in Pixel-Koordinaten. |
Point | Transform(x As Integer, y As Integer) | Erstellt aus den angegeben skalierten Koordinaten ein Point-Objekt in Pixel-Koordinaten. |
PointF | Transform(x As Double, y As Double) | Erstellt aus den angegeben skalierten Koordinaten ein PointF-Objekt in Pixel-Koordinaten. |
PointF | Transform(pt As PointF) | Erstellt aus einem PointF-Objekt in skalierten Koordinaten eines in Pixel-Koordinaten. |
Point | Transform(pt As Point) | Erstellt aus einem Point-Objekt in skalierten Koordinaten eines in Pixel-Koordinaten. |
PointF() | Transform(pts As PointF()) | Erstellt aus einem PointF-Array in skalierten Koordinaten eines in Pixel-Koordinaten. |
Point() | Transform(pts As Point()) | Erstellt aus einem Point-Array in skalierten Koordinaten eines in Pixel-Koordinaten. |
RectangleF | Transform(rct As RectangleF) | Erstellt aus einem RectangleF-Objekt in skalierten Koordinaten eines in Pixel-Koordinaten. |
Rectangle | Transform(rct As Rectangle) | Erstellt aus einem Rectangle-Objekt in skalierten Koordinaten eines in Pixel-Koordinaten. |
Single | TransformScaledWidthToPixelWidth | Transformiert eine horizontale Länge
(Weite) in skalierten Werte in eine solche mit Pixel-Werten. Horizontale Längen (Weiten) und vertikale Längen (Höhen) haben unterschiedliche Skalierungsfaktoren! |
Single | TransformScaledHeightToPixelHeight | Transformiert eine vertikale Länge
(Höhe) in skalierten Werte in eine solche mit Pixel-Werten. Horizontale Längen (Weiten) und vertikale Längen (Höhen) haben unterschiedliche Skalierungsfaktoren! |
Single | TransformPixelWidthToScaledWith | Transformiert eine horizontale Länge
(Weite) in Pixel in eine solche mit skalierten Werten. Horizontale Längen (Weiten) und vertikale Längen (Höhen) haben unterschiedliche Skalierungsfaktoren! |
Single | TransformPixelHeightToScaledHeight | Transformiert eine vertikale Länge
(Höhe) in Pixel in eine solche mit skalierten Werten. Horizontale Längen (Weiten) und vertikale Längen (Höhen) haben unterschiedliche Skalierungsfaktoren! |
PointF | InversTransform(pt As PointF) | Erstellt aus einem PointF-Objekt in Pixel-Koordinaten eines in skalierten Koordinaten. |
- Code verbergen | + Code anzeigen
''' <summary>
''' Löst das Paint-Ereignis aus.
''' </summary>
''' <param name="pe">Ein PaintEventArgs, das die Ereignisdaten enthält. </param>
''' <seealso cref="PictureBox.OnPaint(PaintEventArgs)"/>
Protected Overrides Sub OnPaint(pe As PaintEventArgs)
If Not DesignMode Then 'macht problem im Designmode
ComputeTransformMatrix()
End If
mGraphics = pe.Graphics
MyBase.OnPaint(pe)
pe.Graphics.ResetTransform()
' Koordinaten im Design-Modus einzeichnen
If DesignMode AndAlso DrawCoordinates Then
mGraphics.DrawString(Origin.ToString, SystemFonts.DialogFont, Brushes.Brown, 5, Height - 20)
Dim s As String = "{Width=" & mScaledSize.Width & "}"
Dim sz = mGraphics.MeasureString(s.ToString, SystemFonts.DialogFont)
mGraphics.DrawString(s, SystemFonts.DialogFont, Brushes.Brown, Width - sz.Width - 5, Height - sz.Height - 5)
s = "{Height=" & mScaledSize.Height & "}"
sz = mGraphics.MeasureString(s.ToString, SystemFonts.DialogFont)
mGraphics.DrawString(s, SystemFonts.DialogFont, Brushes.Brown, 5, 5)
s = "{X=" & mScaledSize.Width + mOrigin.X & ", Y=" & mScaledSize.Height + mOrigin.Y & "}"
sz = mGraphics.MeasureString(s.ToString, SystemFonts.DialogFont)
mGraphics.DrawString(s, SystemFonts.DialogFont, Brushes.Brown, Width - sz.Width - 5, 5)
End If
End Sub
Die Berechnung der Transformationsmatrix ist nicht besonders schwierig. Zu beachten ist allerdings, dass sich die Nettogröße des Zeichenbereichs (ClientRectangle) zur Skalierung genutzt werden muss.
- Code verbergen | + Code anzeigen
''' <summary>
''' Berechnet die Tranformationsmatrix
''' </summary>
Private Sub ComputeTransformMatrix()
With ClientRectangle
Dim mx = .Width / mScaledSize.Width ' Skalierung X
Dim my = -.Height / mScaledSize.Height ' Skalierung Y
mTransformMatrix = New Drawing2D.Matrix
mTransformMatrix.Translate(-mOrigin.X, -mOrigin.Y) ' Skalierte linke untere Ecke auf skaliert (0 | 0)
mTransformMatrix.Scale(mx, my, Drawing2D.MatrixOrder.Append) ' Skalierung auf Control-Größe
mTransformMatrix.Translate(0, .Height - 1, Drawing2D.MatrixOrder.Append) ' Bezugspunkt ist links unten
'(anstatt links oben)
End With
End Sub
Zu beachten ist, dass Arrays als Referenz übergeben werden. Hier muss die Transformation an einer Kopie des Arrays ausgeführt werden (Clone-Methode).
- Code verbergen | + Code anzeigen
''' <summary>
''' Zeichnet eine Reihe von Liniensegmenten, die ein Array von PointF-Strukturen verbinden.
''' </summary>
''' <param name="Pen">Pen, der die Farbe, Breite und den Stil der Liniensegmente bestimmt.</param>
''' <param name="points">Array von PointF-Strukturen, die die zu verbindenden Punkte darstellen.</param>
''' <seealso cref="Graphics.DrawLines(Pen, PointF())"/>
Public Sub DrawLines(Pen As Pen, points As PointF())
Dim tmpMatrix = mGraphics.Transform
mGraphics.ResetTransform()
Dim pts = points.Clone
mTransformMatrix.TransformPoints(pts)
mGraphics.DrawLines(Pen, pts)
mGraphics.Transform = tmpMatrix
End Sub