Creating Your Own Drawing Application in Visual Basic.NET, Part 3

In the previous two parts (Part 1, Part 2) of this article series, you have created the Drawing Application Framework, enhanced it by adding a marquee while drawing, and extended the color filling capabilities. This part (Part 3) was probably the most fun to create! What I will cover here is the following:

  • Drawing of Odd shapes, such as:
    • Hearts
    • Spirals
    • Rounded Rectangles
    • Stars
    • Lines
  • Filling these shapes with the following Brushes:
    • Horizontal Hatch Brush
    • Vertical hatch Brush
    • Zig Zag Hatch Brush
    • Wave Hatch Brush
    • Plaid Hatch Brush
    • Linear Gradient Brush
    • Path Gradient Brush
    • Texture
    • Solid Brush

Now, get started!

Giving the Triangle Button a Face Lift

From the beginning, I wasn’t too fond of the Triangle button; I envisioned it to be much more useful than what it was. My whole plan for this (the Triangle button) was to be able to draw shapes like Hearts, Spirals, and Rounded Rectangles—uncommon shapes—and to have one button control all the odd shapes.

Step 1: Rename All the Objects to More Appropriate Names

On frmTools, rename the Triangle button to butCOddShapes and change its Text property to Odds. Comment out the butCTriangle_Click event handler completely, so that you can start over with butCOddShapes’ events. Create a Paint event for butCOddShapes that looks like the following:

Private Sub butCOddShapes_Paint(ByVal sender As System.Object, _
   ByVal e As System.Windows.Forms.PaintEventArgs) _
   Handles butCOddShapes.Paint

   'Declare A GraphicsPath Object, Which Is Used To Draw The Shape
   'Of The Button
   Dim StarPath As System.Drawing.Drawing2D.GraphicsPath = _
      New System.Drawing.Drawing2D.GraphicsPath

   'Create A Star Path
   Dim pSPoints As Point() = { _
      New Point(30, 0), _
      New Point(40, 20), _
      New Point(60, 20), _
      New Point(42, 30), _
      New Point(60, 60), _
      New Point(30, 40), _
      New Point(0, 60), _
      New Point(17, 30), _
      New Point(0, 20), _
      New Point(20, 20)}    ' star shape + changed caption to Odds,
                            ' center middle aligned

   'Add The Point Array Object To The GraphicsPath Object.
   StarPath.AddLines(pSPoints)

   'Size Of The Button
   butCOddShapes.Size = New System.Drawing.Size(60, 60)

   If blnTraingleClicked Then
      'If The Button Is Selected To Draw, Change The Color
      butCOddShapes.BackColor = Color.DeepSkyBlue
   Else
      'If The Button Is Not Selected To Draw With, Change Back To
      'Original Color
      butCOddShapes.BackColor = Color.Aquamarine
   End If

   'Create The Triangular Shaped Button, Based On The Graphics Path
   butCOddShapes.Region = New Region(StarPath)

   'Release All Resources Owned By The Graphics Path Object
   StarPath.Dispose()

End Sub

Once run, the code above will create a Star shaped button with five points.

In frmTools_Load, include a ToolTip for your new button:

tipCanvas.SetToolTip(butCOddShapes, "Draw An Odd Shape")

Step 2: Add the Odd Shapes Menu and Their Associated Event Handlers

Add a Context Menu named cmCanvasShapes to frmTools. Add the following Menu Items to cmCanvasShapes:

Menu Item Name Text
mnuTriangle Triangle
mnuHeart Heart
mnuRoundRect Rounded Rectangle
mnuLine Line
mnuSpiral Spiral

After adding the new Context Menu, you have to Select the Context Menu property on butCOddShapes, and set it to cmCanvasShapes. Add a butCOddShapes_MouseDown event to frmTools in the code window, and type the following:

Private Sub butCOddShapes_MouseDown(ByVal sender As Object, _
   ByVal e As System.Windows.Forms.MouseEventArgs) _
   Handles butCOddShapes.MouseDown
   If e.Button = MouseButtons.Left And e.X > 28 And e.Y > 28 Then
       butCOddShapes.ContextMenu.Show(butCOddShapes, _
                                      New Point(e.X, e.Y))
   End If
End Sub

This will enable you to get a menu showing once you click on the bottom right point of the star.

Now, add all the event handlers, to set which Tool was selected, to True. This is the same principle as all the other buttons:

Private Sub mnuTriangle_Click(ByVal sender As Object, _
                              ByVal e As System.EventArgs) _
   Handles mnuTriangle.Click
   'Set All Boolean Flags Of Tools Click To False, Except For The
   'Current One : Triangle
   blnCircleClicked   = False
   blnSquareClicked   = False
   blnDrawClicked     = False
   blnEraserClicked   = False
   blnTraingleClicked = True

   blnHeartClicked     = False
   blnRoundRectClicked = False
   blnLineClicked      = False
   blnSpiralClicked    = False

   'Refresh/Repaint The Buttons To Indicate Current Selection State
   butCCircle.Refresh()
   butCSquare.Refresh()
   butCOddShapes.Refresh()
End Sub

Private Sub mnuHeart_Click(ByVal sender As Object, _
   ByVal e As System.EventArgs) Handles mnuHeart.Click
   'Set All Boolean Flags Of Tools Click To False, Except For The
   'Current One: Triangle
   blnCircleClicked   = False
   blnSquareClicked   = False
   blnDrawClicked     = False
   blnEraserClicked   = False
   blnTraingleClicked = False

   blnHeartClicked     = True
   blnRoundRectClicked = False
   blnLineClicked      = False
   blnSpiralClicked    = False

End Sub

Private Sub mnuRoundRect_Click(ByVal sender As Object, _
   ByVal e As System.EventArgs) Handles mnuRoundRect.Click
   'Set All Boolean Flags Of Tools Click To False, Except For The
   'Current One: Triangle
   blnCircleClicked   = False
   blnSquareClicked   = False
   blnDrawClicked     = False
   blnEraserClicked   = False
   blnTraingleClicked = False

   blnHeartClicked     = False
   blnRoundRectClicked = True
   blnLineClicked      = False
   blnSpiralClicked    = False
End Sub

Private Sub mnuLine_Click(ByVal sender As Object, _
   ByVal e As System.EventArgs) Handles mnuLine.Click
   'Set All Boolean Flags Of Tools Click To False, Except For The
   'Current One: Triangle
   blnCircleClicked   = False
   blnSquareClicked   = False
   blnDrawClicked     = False
   blnEraserClicked   = False
   blnTraingleClicked = False

   blnHeartClicked     = False
   blnRoundRectClicked = False
   blnLineClicked      = True
   blnSpiralClicked    = False
End Sub


Private Sub mnuSpiral_Click(ByVal sender As Object, _
   ByVal e As System.EventArgs) Handles mnuSpiral.Click
   'Set All Boolean Flags Of Tools Click To False, Except For The
   'Current One: Triangle
   blnCircleClicked   = False
   blnSquareClicked   = False
   blnDrawClicked     = False
   blnEraserClicked   = False
   blnTraingleClicked = False

   blnHeartClicked     = False
   blnRoundRectClicked = False
   blnLineClicked      = False
   blnSpiralClicked    = True
End Sub

Also, make sure that the Circle button and the Square button, Pencil button and the Eraser button also know about these new tools:

Private Sub butCCircle_Click(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles butCCircle.Click

   'Set All Boolean Flags Of Tools Click To False, Except For The
   'Current One: Circle
   blnTraingleClicked = False
   blnSquareClicked   = False
   blnDrawClicked     = False
   blnEraserClicked   = False
   blnCircleClicked   = True

   blnHeartClicked     = False
   blnRoundRectClicked = False
   blnLineClicked      = False
   blnSpiralClicked    = False

   'Refresh/Repaint The Buttons, To Indicate Current Selection State
   butCCircle.Refresh()
   butCSquare.Refresh()
   butCOddShapes.Refresh()

End Sub

Private Sub butCSquare_Click(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles butCSquare.Click

   'Set All Boolean Flags Of Tools Click To False, Except For The
   'Current One: Square
   blnCircleClicked   = False
   blnTraingleClicked = False
   blnDrawClicked     = False
   blnEraserClicked   = False
   blnSquareClicked   = True

   blnHeartClicked     = False
   blnRoundRectClicked = False
   blnLineClicked      = False
   blnSpiralClicked    = False

   'Refresh/Repaint The Buttons, To Indicate Current Selection State

   butCCircle.Refresh()
   butCSquare.Refresh()
   butCOddShapes.Refresh()

End Sub

Private Sub butCPencil_Click(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles butCPencil.Click

   'Set All Boolean Flags Of Tools Click To False, Except For The
   'Current One: Pencil
   blnCircleClicked   = False
   blnTraingleClicked = False
   blnSquareClicked   = False
   blnDrawClicked     = True

   blnHeartClicked     = False
   blnRoundRectClicked = False
   blnLineClicked      = False
   blnSpiralClicked    = False

   'Refresh/Repaint The Buttons, To Indicate Current Selection State
   butCCircle.Refresh()
   butCSquare.Refresh()
   butCOddShapes.Refresh()

End Sub


Private Sub butCEraser_Click(ByVal sender As Object, _
   ByVal e As System.EventArgs) Handles butCEraser.Click

   'Set All Boolean Flags Of Tools Click To False, Except For The
   'Current One: Eraser
   blnCircleClicked   = False
   blnTraingleClicked = False
   blnSquareClicked   = False
   blnDrawClicked     = False
   blnEraserClicked   = True

   blnHeartClicked     = False
   blnRoundRectClicked = False
   blnLineClicked      = False
   blnSpiralClicked    = False

   'Refresh/Repaint The Buttons, To Indicate Current Selection State
   butCCircle.Refresh()
   butCSquare.Refresh()
   butCOddShapes.Refresh()

End Sub

Step 3: Drawing the Actual Shapes

Before you draw anything, you should declare the pen objects for each of your new shapes, at the top of picCDraw_MouseUp.

Dim pHeartPen     As New Pen(Color.Black, 3)
Dim pRoundRectPen As New Pen(Color.Black, 3)
Dim pLinePen      As New Pen(Color.Black, 3)
Dim pSpiralPen    As New Pen(Color.Black, 3)

Drawing the Heart

The first shape you are going to draw is the Heart because you already have the working code that draws your Triangle.

To draw your heart, you will need to add Point variables that keep track of the starting point and ending point of your Heart. Declare them in the Declarations section of frmCanvas.

'Start Point For Heart
Private ptHeartStartPoint As System.Drawing.Point

'End Point For Heart
Private ptHeartEndPoint As System.Drawing.Point

At the bottom of picCDraw_MouseDown, set the Start Point’s x & Y equals to the current x & y points:

ptHeartStartPoint.X = e.X 'Heart Start Point - X

ptHeartStartPoint.Y = e.Y 'Heart Start Point - Y

In picCDraw_MouseUp, just above the starting IF block, add these lines to set the Heart’s ending points equals to the current ending X & Y points

ptHeartEndPoint.X = e.X 'Heart End Point - X

ptHeartEndPoint.Y = e.Y 'Heart End Point - Y

Still in picCDraw_MouseUp, add another If block after the Triangle’s If block, to determine whether the Heart Tool was selected. Then, draw the Heart shape.

'Determine If The Heart Tool Has Been Clicked
If blnHeartClicked Then

   'Yes, It Has Been Clicked, Set The Pen's Color To Selected Color
   pHeartPen.Color = cColor

   If blnOutlineDrawClicked Then
      'Draw The Heart With The Current Starting, And Ending Values
      Dim HeartWidth As Integer = _
         Math.Max(ptHeartEndPoint.X, ptHeartStartPoint.X) - _
         Math.Min(ptHeartEndPoint.X, ptHeartStartPoint.X)
      Dim HeartHeight As Integer = _
         Math.Max(ptHeartEndPoint.Y, ptHeartStartPoint.Y) - _
         Math.Min(ptHeartEndPoint.Y, ptHeartStartPoint.Y)
      Dim HeartAvg As Integer = CInt((HeartWidth + HeartHeight) / 2)
      If HeartAvg > 0 Then
         Dim HeartTopLeft As Point = ptHeartStartPoint
         If ptHeartEndPoint.X < ptHeartStartPoint.X Then
            HeartTopLeft.X = ptHeartEndPoint.X
         End If
         If ptHeartEndPoint.Y < ptHeartStartPoint.Y Then
            HeartTopLeft.Y = ptHeartEndPoint.Y
         End If

         Dim HeartRadius As Integer = CInt(HeartAvg / 2)
         Dim HeartTopLeftSquare As New Rectangle(HeartTopLeft.X, _
            HeartTopLeft.Y, HeartRadius, HeartRadius)
         Dim HeartTopRightSquare As New Rectangle(HeartTopLeft.X + _
            HeartRadius, HeartTopLeft.Y, HeartRadius, HeartRadius)

         gCanvas.DrawArc(pHeartPen, HeartTopLeftSquare, 135.0F, 225.0F)
         gCanvas.DrawArc(pHeartPen, HeartTopRightSquare, 180.0F, 225.0F)
         gCanvas.DrawLine(pHeartPen, CInt(HeartTopLeft.X + _
            HeartRadius / 2 - Math.Sin(45 / 180 * Math.PI) * _
            HeartRadius / 2), _
            CInt(HeartTopLeft.Y + HeartRadius / 2 + _
               Math.Sin(45 / 180 * Math.PI) * HeartRadius / 2), _
               HeartTopLeft.X + HeartRadius, HeartTopLeft.Y + HeartAvg)
         gCanvas.DrawLine(pHeartPen, CInt(HeartTopLeft.X + _
            HeartAvg - (1 - Math.Sin(45 / 180 * Math.PI)) * _
            HeartRadius / 2), _
            CInt(HeartTopLeft.Y + HeartRadius / 2 + _
               Math.Sin(45 / 180 * Math.PI) * HeartRadius / 2), _
               HeartTopLeft.X + HeartRadius, HeartTopLeft.Y + HeartAvg)
      End If
   End If

At the bottom of picCDraw_MouseUp, dispose of your Heart pen.

pHeartPen.Dispose()

Drawing the Rounded Rectangle

In picCDraw_MouseUp, add the following If block, right after the Heart section you’ve just created:

'Determine If The RoundRect Tool Has Been Clicked
If blnRoundRectClicked Then

   'Yes, It Has Been Clicked, Set The Pen's Color To Selected Color
   pRoundRectPen.Color = cColor

   If blnOutlineDrawClicked Then
      'Draw The RoundRect With The Current Starting And Ending Values
      Dim RectX As Integer      = Math.Min(sStartX, sEndX)
      Dim RectY As Integer      = Math.Min(sStartY, sEndY)
      Dim RectWidth As Integer  = Math.Abs(sStartX - sEndX)
      Dim RectHeight As Integer = Math.Abs(sStartY - sEndY)
      Dim RectRadius As Integer = 15
      Dim pRRPath As New System.Drawing.Drawing2D.GraphicsPath

      If RectRadius > Rectwidth / 2 OrElse RectRadius > _
         RectHeight / 2 Then RectRadius = RectHeight / 2

      pRRPath.StartFigure()

      pRRPath.AddArc(RectX, RectY, RectRadius * 2, _
                     RectRadius * 2, 180, 90)
      pRRPath.AddArc(RectX + RectWidth - RectRadius * 2, RectY, _
                     RectRadius * 2, RectRadius * 2, 270, 90)
      pRRPath.AddArc(RectX + RectWidth - RectRadius * 2, RectY + _
                     RectHeight - RectRadius * 2, RectRadius * 2, _
                     RectRadius * 2, 0, 90)
      pRRPath.AddArc(RectX, RectY + RectHeight - RectRadius * 2, _
                     RectRadius * 2, RectRadius * 2, 90, 90)
      pRRPath.CloseFigure()

      gCanvas.DrawPath(pRoundRectPen, pRRPath)
   End If

At the bottom of picCDraw_MouseUp, dispose of the RoundRect Pen object:

pRoundRectPen.Dispose()

Drawing the Line

Underneath the code you entered to draw the Rounded Rectangle, add the following If block:

'Determine If The Line Tool Has Been Clicked
If blnLineClicked Then

   'Yes, It Has Been Clicked, Set The Pen's Color To Selected Color
   pLinePen.Color = cColor


   If blnOutlineDrawClicked Then
      'Draw The Line With The Current Starting, And Ending Values
      gCanvas.DrawLine(pLinePen, sStartX, sStartY, sEndX, sEndY)
   End If

End If

Dispose of the Line Pen as well:

pLinePen.Dispose()

Experimenting with The Line Tool

Drawing the Spiral

Inside picCDraw_MouseUp, just underneath the code to create the Line, add the following to draw your Spiral.

'Determine If The Spiral Tool Has Been Clicked
If blnSpiralClicked Then

   'Yes, It Has Been Clicked, Set The Pen's Color To Selected Color
   pSpiralPen.Color = cColor

   If blnOutlineDrawClicked Then
      'Draw The Spiral With The Current Starting, And Ending Values
      Dim SpiralPI          As Double = 3.14159265358979
      Dim SpiralOrientation As Double = 3.356987413    'orientation
      Dim SpiralWidth       As Integer
      Dim SpiralX           As Integer
      Dim SpiralHeight      As Integer
      Dim SpiralY           As Integer

      'location of spiral
      Dim SpiralRect As New Rectangle(sStartX, sStartY, sEndX, sEndY)

      SpiralWidth  = SpiralRect.Width
      SpiralHeight = SpiralRect.Height

      Dim SpiralAAng  As Single
      Dim SpiralBAng  As Single
      Dim SpiralLoop  As Long
      Dim SpiralAngle As Double

      SpiralAAng = 0.15
      SpiralBAng = 0.15

      For SpiralLoop = 0 To 8000    ' size of spiral
         SpiralAngle = (SpiralPI / 720) * SpiralLoop
         SpiralX = SpiralWidth + (SpiralAAng * _
            (System.Math.Cos(SpiralAngle)) * _
            (SpiralOrientation ^ (SpiralBAng * SpiralAngle)))
         SpiralY = SpiralHeight - (SpiralAAng * _
            (System.Math.Sin(SpiralAngle)) * _
            (SpiralOrientation ^ (SpiralBAng * SpiralAngle)))
         'the higher the + number the thicker the lines
         gCanvas.DrawLine(pSpiralPen, SpiralX, SpiralY, _
         SpiralX + 5, SpiralY + 5)
      Next SpiralLoop
   End If

End If

Dispose of the Spiral pen.

pSpiralPen.Dispose()

You are now ready to Build and run Canvas. Once Canvas is run, you will immediately notice the newly created “Star” button, instead of the old Triangle, on the Tools Window. When you click on the bottom right point of the star, a menu will appear listing all of the Odd shapes you can draw. It should list Triangle, Heart, Rounded Rectangle, Line, and Spiral. Feel free to experiment drawing with your new shapes. The Line and the Spiral shapes only work in Outline Draw mode; the rest you can draw in Outline Mode, or Full Fill Draw mode.

Move on now, to filling these shapes with color.

More by Author

Must Read