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.