vb.netwinformsmatrixgraphicsrotatetransform

How to transform a shape in VB.NET


I'm trying to first draw a shape (which i've done already) and then have it transformed as selected by a user for example, rotated to a certain angle, or scaled, showing this original shape and the newly transformed shape.

I've tried the following on trying to rotate:

Private Sub paint_box_Paint(sender As Object, e As PaintEventArgs) Handles paint_box.Paint
    Dim x As Integer = paint_box.Size.Width / 2
    Dim y As Integer = paint_box.Size.Height / 2

    Dim rect As New Rectangle(x, y, 80, 80)

    ' Create pen.
    Dim blackPen As New Pen(Color.Black, 3)
    ' Create pen.
    Dim redPen As New Pen(Color.Red, 3)
    e.Graphics.DrawRectangle(blackPen, rect)
End Sub

I was expecting to have that shape printed on Form.Load to rotate by the specified angle 30.0F


Solution

  • When we want to paint on a Control's surface, we need subscribe to the Paint event of a Control (not all controls provide this event: TextBox controls don't, for example. We could derive a Custom Control class from TextBox and override the OnPaint method. It may be disappointing, though).

    To paint and rotate a shape, we need some Fields or Properties to store the references of the objects/values we use for painting. Here, just the Rectangle object that provides the measure of the shape and a single Field to store the rotation angle.

    Private drawingRect As Rectangle = New Rectangle(50, 50, 100, 100)
    Private rotationAngle As Single = 0.0F
    

    These references allow to change the shape of the object and the rotation angle when needed. When these values change, we just need to call the Invalidate() method of a Control to raise its Paint event.

    In Form.Load() (or in the Form's constructor - Public Sub New()) we can define the initial position and size of the shape, if required:

    Public Sub New()
        InitializeComponent()
        drawingRect = New Rectangle(50, 50, 100, 100)
    End Sub
    

    When we change the measures of a shape, to update the drawing, we call the Control's Invalidate() method. Here, the drawing surface is provided by a GroupBox control.
    On a Button.Click(), we update the shape with the new values calling GroupBox.Invalidate(). The drawing will be updated immediately:

    Private Sub btnRotate_Click(sender As Object, e As EventArgs) Handles btnRotate.Click
        ' Rotate the shape 45 degrees
        rotationAngle = 45
        GroupBox1.Invalidate()
    End Sub
    

    To rotate a shape, we can use two simple methods: the Graphics.RotateTransform method, or the Matrix.RotateAt() method.
    I'm using the latter: it's very simple to use, it accepts an angle expressed in degrees and the we just need to provide the coordinates of the rotation.
    Here, the shape is rotated using the coordinates of its center point:

    Using mx As Matrix = New Matrix
        mx.RotateAt(rotationAngle, New PointF(drawingRect.X + (drawingRect.Width / 2.0F),
                                              drawingRect.Y + (drawingRect.Height / 2.0F)))
        e.Graphics.Transform = mx
        e.Graphics.DrawRectangle(pen, drawingRect)
    End Using
    

    Sample of the results:

    Graphis DrawRectangle Matrix RotateAt

    See also:
    Drawing and scaling rectangle using a ratio
    How to use the Paint event to draw shapes at mouse coordinates

    All the code used to draw and rotate the shape:

    Public Class Form1
        Private drawingRect As Rectangle = Rectangle.Empty
        Private rotationAngle As Single = 0.0F
    
        Public Sub New()
            InitializeComponent()
            drawingRect = New Rectangle(50, 50, 100, 100)
        End Sub
    
        Private Sub GroupBox1_Paint(sender As Object, e As PaintEventArgs) Handles GroupBox1.Paint
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias
            Using pen As Pen = New Pen(Color.LightGreen, 4),
                mx As Matrix = New Matrix()
    
                mx.RotateAt(rotationAngle, New PointF(drawingRect.X + (drawingRect.Width / 2.0F),
                                                      drawingRect.Y + (drawingRect.Height / 2.0F)))
                e.Graphics.Transform = mx
                e.Graphics.DrawRectangle(pen, drawingRect)
            End Using
        End Sub
    
        Private Sub btnPaint_Click(sender As Object, e As EventArgs) Handles btnPaint.Click
            ' Redefine the shape and redraw it
            drawingRect.Location = New Point(100, 100)
            drawingRect.Size = New Size(200, 300)
            GroupBox1.Invalidate()
        End Sub
    
        Private Sub btnRotate_Click(sender As Object, e As EventArgs) Handles btnRotate.Click
            ' Rotate the shape 45 degrees
            rotationAngle = 45
            GroupBox1.Invalidate()
        End Sub
    End Class