Drawing with the .NET Framework is very easy. There are numerous namespaces that allow you to draw any shape imaginable. A few years ago, I wrote a number of articles on how to create a drawing application, but I never got around to adding some more features into them. This is where this article comes in. This article will show you how to create a rectangle shape that is resizable. I have also decided to create a series for this topic, so in the future you can expect to be able to resize round shapes and odd shapes.
There is a lot of work, so let’s get started straight-away!
Practical
Open Visual Studio and create a new Visual Basic Windows Forms project. Name it anything you desire. Once the project has loaded, add one picturebox onto the form. Your design should resemble Figure 1.

Figure 1: Design
For now, the form is complete; we will continue with it a bit later. Add a class to your project and give it a descriptive name. I have named mine clsObject (because it will get extended later). Add the following fields to your class:
Private picObject As PictureBox
Public rectObject As Rectangle
Private blnClick As Boolean = False
Private blnMove As Boolean = False
Private intPrevX As Integer
Private intPrevY As Integer
Private intNode As Integer = 5
A dynamic Picturebox gets created as well as a Rectangle object. The next two flags will determine whether or not the user is clicking or moving; this helps with keeping track of the resizing handles you will create later. X and Y get stored and the size of your resizing node will be 5 pixels by 5 pixels. Add the Enum:
Private intSelNode As NodePos = NodePos.None
Private Enum NodePos
TopLeft
TopMiddle
TopRight
BottomLeft
BottomMiddle
BottomRight
LeftMiddle
RightMiddle
None
End Enum
The NodePos enum provides the settings for the display of the resizing handles. Add the constructor:
Public Sub New(ByVal rctTemp As Rectangle)
rectObject = rctTemp
blnClick = False
End Sub
This initializes the class. Add the Create method that creates the resizable rectangle:
Public Sub Create(ByVal g As Graphics)
g.DrawRectangle(New Pen(Color.Green), rectObject)
For Each npPos As NodePos In _
[Enum].GetValues(GetType(NodePos))
g.DrawRectangle(New Pen(Color.Green), GetObject(npPos))
Next
End Sub
The Create sub creates the Rectangle object; it also creates the sizing handles so that you are able to grab on to them and resize the Rectangle. You will call the Create Sub a bit later. You must add events to your dynamic picturebox; otherwise, you will not be able to handle any mouse clicks or movements.
Public Sub AddPicEvents(ByVal pic As PictureBox)
Me.picObject = pic
AddHandler picObject.MouseDown, AddressOf picObject_MouseDown
AddHandler picObject.MouseMove, AddressOf picObject_MouseMove
AddHandler picObject.MouseUp, AddressOf picObject_MouseUp
AddHandler picObject.Paint, AddressOf picObject_Paint
End Sub
Event handlers for the Mouse are added as well as the very, extremely important Paint event. The Paint event is responsible for, well, painting the drawing. You will call the Create sub from the Paint event, as you will see now.
Private Sub picObject_Paint(ByVal sender As Object, _
ByVal e As PaintEventArgs)
Try
Create(e.Graphics)
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Add the mouse events:
Private Sub picObject_MouseDown(ByVal sender As Object, _
ByVal e As MouseEventArgs)
blnClick = True
intSelNode = NodePos.None
intSelNode = SelectableNode(e.Location)
If rectObject.Contains(New Point(e.X, e.Y)) Then
blnMove = True
End If
intPrevX = e.X
intPrevY = e.Y
End Sub
Private Sub picObject_MouseUp(ByVal sender As Object, _
ByVal e As MouseEventArgs)
blnClick = False
blnMove = False
End Sub
Private Sub picObject_MouseMove(ByVal sender As Object, _
ByVal e As MouseEventArgs)
ChangeCursor(e.Location)
If blnClick = False Then
Return
End If
Dim rctTemp As Rectangle = rectObject
Select Case intSelNode
Case NodePos.TopLeft
rectObject.X += e.X - intPrevX
rectObject.Width -= e.X - intPrevX
rectObject.Y += e.Y - intPrevY
rectObject.Height -= e.Y - intPrevY
Case NodePos.LeftMiddle
rectObject.X += e.X - intPrevX
rectObject.Width -= e.X - intPrevX
Case NodePos.BottomLeft
rectObject.Width -= e.X - intPrevX
rectObject.X += e.X - intPrevX
rectObject.Height += e.Y - intPrevY
Case NodePos.BottomMiddle
rectObject.Height += e.Y - intPrevY
Case NodePos.TopRight
rectObject.Width += e.X - intPrevX
rectObject.Y += e.Y - intPrevY
rectObject.Height -= e.Y - intPrevY
Case NodePos.BottomRight
rectObject.Width += e.X - intPrevX
rectObject.Height += e.Y - intPrevY
Case NodePos.RightMiddle
rectObject.Width += e.X - intPrevX
Case NodePos.TopMiddle
rectObject.Y += e.Y - intPrevY
rectObject.Height -= e.Y - intPrevY
Case Else
If blnMove Then
rectObject.X = rectObject.X + e.X - intPrevX
rectObject.Y = rectObject.Y + e.Y - intPrevY
End If
End Select
intPrevX = e.X
intPrevY = e.Y
If rectObject.Width < 5 OrElse rectObject.Height < 5 Then
rectObject = rctTemp
End If
ObjHitTest()
picObject.Invalidate()
End Sub
The mouse events set the Click and Move flags, depending on the state of the mouse. In the MouseMove event, you determine the direction in which the user is moving and update the size of the rectangle as well as the placements of the sizing handles.
Private Sub ObjHitTest()
If rectObject.X < 0 Then rectObject.X = 0
If rectObject.Y < 0 Then rectObject.Y = 0
If rectObject.Width <= 0 Then rectObject.Width = 1
If rectObject.Height <= 0 Then rectObject.Height = 1
If rectObject.X + rectObject.Width > picObject.Width Then
rectObject.Width = picObject.Width - rectObject.X - 1
End If
If rectObject.Y + rectObject.Height > picObject.Height Then
rectObject.Height = picObject.Height - rectObject.Y - 1
End If
End Sub
Private Function SizeNode(ByVal x As Integer, _
ByVal y As Integer) As Rectangle
Return New Rectangle(x - intNode / 2, y - intNode / 2, _
intNode, intNode)
End Function
Private Function GetObject(ByVal pos As NodePos) As Rectangle
Select Case pos
Case NodePos.TopLeft
Return SizeNode(rectObject.X, rectObject.Y)
Case NodePos.TopMiddle
Return SizeNode(rectObject.X + rectObject.Width / 2, _
rectObject.Y)
Case NodePos.TopRight
Return SizeNode(rectObject.X + rectObject.Width, _
rectObject.Y)
Case NodePos.BottomLeft
Return SizeNode(rectObject.X, rectObject.Y + _
rectObject.Height)
Case NodePos.BottomMiddle
Return SizeNode(rectObject.X + rectObject.Width / 2, _
rectObject.Y + rectObject.Height)
Case NodePos.BottomRight
Return SizeNode(rectObject.X + rectObject.Width, _
rectObject.Y + rectObject.Height)
Case NodePos.LeftMiddle
Return SizeNode(rectObject.X, rectObject.Y + _
+rectObject.Height / 2)
Case NodePos.RightMiddle
Return SizeNode(rectObject.X + rectObject.Width, _
rectObject.Y + rectObject.Height / 2)
Case Else
Return New Rectangle()
End Select
End Function
Ensure that the Sizing handle you click is indeed selectable and responsive:
Private Function SelectableNode(ByVal pnt As Point) As NodePos
For Each pos As NodePos In [Enum].GetValues(GetType(NodePos))
If GetObject(pos).Contains(pnt) Then
Return pos
End If
Next
Return NodePos.None
End Function
Polishing off the class is changing the mouse cursor according to which sizing handle it is placed on:
Private Sub ChangeCursor(ByVal pnt As Point)
picObject.Cursor = GetCursor(SelectableNode(pnt))
End Sub
Private Function GetCursor(ByVal p As NodePos) As Cursor
Select Case p
Case NodePos.TopLeft
Return Cursors.SizeNWSE
Case NodePos.TopMiddle
Return Cursors.SizeNS
Case NodePos.TopRight
Return Cursors.SizeNESW
Case NodePos.BottomLeft
Return Cursors.SizeNESW
Case NodePos.BottomMiddle
Return Cursors.SizeNS
Case NodePos.BottomRight
Return Cursors.SizeNWSE
Case NodePos.LeftMiddle
Return Cursors.SizeWE
Case NodePos.RightMiddle
Return Cursors.SizeWE
Case Else
Return Cursors.[Default]
End Select
End Function
Add the following code to your form:
Private objRect As clsObject
Public Sub New()
InitializeComponent()
objRect = New clsObject(New Rectangle(5, 5, 230, 230))
objRect.AddPicEvents(picCanvas)
End Sub
This instantiates the clsObject object and activates it on your form’s picturebox. When run, you should have a Rectangle that you can resize dynamically, as shown in Figure 2.

Figure 2: Runtime
The code for this article is available on GitHub.
Conclusion
Drawing in .NET is extremely powerful. Stay tuned for the next article in this series, where you can resize round shapes.