Creating Your Own Drawing Application with Visual Basic.NET, Part 2

In Part 1 of this series, you created the Framework for your Drawing Application, but it's by no means perfect yet. A lot of improvements still need to be made. In this article, you will:

  • Include the capability to first draw, then fill, the drawn objects
  • Include a marquee while drawing, just so you have a visible cue of the diameters of the current drawing
  • Refine the triangle logic because in Part 1, the triangle wasn't as it should be, and didn't really draw according to the mouse movements

Enough talk. It's time to start!

Enable the Filling of Already Drawn Shapes

At the moment, you can draw outline shapes, and draw Filled shapes, but you cannot Fill an already drawn shape with a different color; that's all about to change now. The first step is to add a Context Menu Object to frmTools. (If you have not downloaded the code from Part 1, you should do so first).

Adding A Context Menu

  • Open frmTools in the Form designer.
  • In the Toolbox (On the Windows Forms tab), scroll down until you find the Context Menu Object. Double-click it to add it to frmTools.
  • In the Properties Window, name the Context Menu cmCanvas.
  • Make sure the cmCanvas is still selected. If a Context Menu is selected, the menu will appear at the top of the form. Click Context Menu at the top of the form.
  • Add the following items to the menu:
  • Control Name Properties
    mnuCOutline
    Text Outline Draw
    Checked True
    RadioCheck True
    mnuCFillDraw
    Text Full Fill Draw
    RadioCheck True
    mnuCDrawFill
    Text Draw Then Fill Full
    RadioCheck True
  • On frmTools, select the button named butCFillColor, and set the ContextMenu property to cmCanvas.
  • You may change the Image property to a better picture indicating that this button contains a sub menu. In the included project, you will notice that I have changed the picture to PaintBucket2.png; it looks almost the same as the original except that it now contains a small black box in the bottom right corner—to indicate to the users where to click.

Speaking of clicking, add the necessary code to frmTools to set the proper ToolTips and enable the ContextMenu actions.

Adding code to frmTools to activate the new ContextMenu

  • In frmTools_Load, replace the following line:
  • tipCanvas.SetToolTip(butCFillColor, "Draw A Filled Shape")

    With:

    tipCanvas.SetToolTip(butCFillColor, "Fill Options")
  • Comment out the butCFillColor_Click event procedure because, seeing the fact that you now have a context menu, you are not going to use it anymore:
  • 'Private Sub butCFillColor_Click(ByVal sender _
    'As System.Object, ByVal e As System.EventArgs) _
    'Handles butCFillColor.Click
       'How Many Times Is This Tool Clicked?
       'sFillClicked = sFillClicked + 1
       'If This Tool Is Clicked Once ...
       'If sFillClicked = 1 Then
       ' 'Load A Different Picture (Change State)
       ' butCFillColor.Image = Image.FromFile("PaintBucket.png")
       ' 'Display A Different ToolTip, To Indicate State /
       ' 'Tool Change
       ' tipCanvas.SetToolTip(butCFillColor, "Return to Outline
       ' Color")
       ' 'Fill Is Clicked, Fill The Objects Based On blnFillClicked
       ' blnFillClicked = True
       'Else
       ' 'Fill Is Not Clicked Anymore, Return To Original Button /
       ' 'Tool State
       ' sFillClicked = 0
       ' blnFillClicked = False
       ' butCFillColor.Image = Image.FromFile("PaintBrush.png")
       ' tipCanvas.SetToolTip(butCFillColor, "Draw A Filled Shape")
       'End If
    'End Sub
    
  • Add the butCFillColor_MouseDown event to handle the Contextmenu:

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

    Here, you are determining where the MouseButton was pressed, so that you then can show the context menu.

  • Add event procedures for each of the ContextMenu items:

    Private Sub mnuCOutline_Click(ByVal sender As System.Object, _
       ByVal e As System.EventArgs) Handles mnuCOutline.Click
       blnOutlineDrawClicked = True
       blnFillDrawClicked    = False
       blnDrawFillClicked    = False
       mnuCOutline.Checked   = True
       mnuCFillDraw.Checked  = False
       mnuCDrawFill.Checked  = False
    End Sub
    
    Private Sub mnuCFillDraw_Click(ByVal sender As System.Object, _
       ByVal e As System.EventArgs) Handles mnuCFillDraw.Click
       blnOutlineDrawClicked = False
       blnFillDrawClicked    = True
       blnDrawFillClicked    = False
       mnuCOutline.Checked   = False
       mnuCFillDraw.Checked  = True
       mnuCDrawFill.Checked  = False
    End Sub
    
    Private Sub mnuCDrawFill_Click(ByVal sender As System.Object, _
       ByVal e As System.EventArgs) Handles mnuCDrawFill.Click
       blnOutlineDrawClicked = False
       blnFillDrawClicked    = False
       blnDrawFillClicked    = True
       mnuCOutline.Checked   = False
       mnuCFillDraw.Checked  = False
       mnuCDrawFill.Checked  = True
       blnCircleClicked      = False
       blnTriangleClicked    = False
       blnSquareClicked      = False
       blnDrawClicked        = False
       blnEraserClicked      = False
    End Sub
    

The filling subs

The next step is to do the actual filling of the shapes once they are drawn. For this, you'll need to add the following two sub procedures to frmCanvas. The first sub is called CanvasFloodFill. The whole object of this function is to do the actual filling of the shapes. Take a closer look:

Private Sub CanvasFloodFill(ByVal FillBmp As Bitmap, _
                            ByVal cffX As Integer, _
                            ByVal cffY As Integer, _
                            ByVal cffNewCol As Color)
'Get Old Pixel Color
Dim cffOldCol As Color = FillBmp.GetPixel(cffX, cffY)
'If Old Color Equals The New Color Exit
If cffOldCol.ToArgb = cffNewCol.ToArgb Then Exit Sub

'Create A Stack For All The Points
Dim FloodStack As New Stack(1000)
'Put Current Pixel On Stack
FloodStack.Push(New Point(cffX, cffY))
'Set Current Pixel To New Color
FillBmp.SetPixel(cffX, cffY, cffNewCol)

'While There Are Items In The Stack
Do While FloodStack.Count > 0
   'Remove & Return Point At Top Of Stack
   Dim FillPt As Point = DirectCast(FloodStack.Pop(), Point)
   If FillPt.X > 0 Then CanvasGetSetPixel(FillBmp, FloodStack, _
      FillPt.X - 1, FillPt.Y, cffOldCol, cffNewCol)    'Left
   If FillPt.Y > 0 Then CanvasGetSetPixel(FillBmp, FloodStack, _
      FillPt.X, FillPt.Y - 1, cffOldCol, cffNewCol)    'Top
   'Right
   If FillPt.X < FillBmp.Width - 1 Then CanvasGetSetPixel(FillBmp, _
      FloodStack, FillPt.X + 1 _, FillPt.Y, cffOldCol, cffNewCol) _
      FillPt.Y + 1, cffOldCol, cffNewCol)    'Bottom
Loop
FloodStack.Clear()    'Clear Stack
End Sub

The next sub is called CanvasGetSetPixel. This procedure identifies the current pixel's colour, and then whether the current pixel is the same as the old colour—in other words, replace the current colour with the new colour.

Private Sub CanvasGetSetPixel(ByVal gspBmp As Bitmap, _
                              ByVal GetSetStack As Stack, _
                              ByVal gspX As Integer, _
                              ByVal gspY As Integer, _
                              ByVal gspOldCol As Color, _
                              ByVal gspNewCol As Color)
'Get Current Pixel Color
Dim gspClr As Color = gspBmp.GetPixel(gspX, gspY)
'If Current Color Equals Old Color
If gspClr.ToArgb = gspOldCol.ToArgb Then
'Push Next Point To Top Of Stack
   GetSetStack.Push(New Point(gspX, gspY))
'Set New Pixel Color
   gspBmp.SetPixel(gspX, gspY, gspNewCol)
End If
End Sub

These two subs work in conjunction with one another. The one sub creates a Stack onto which you can put all the pixels that need to be replaced. The next sub determines which pixels to replace. Not really as complicated as it looks.

To use these subs, you need to call the CanvasFloodFill sub (which in turn calls the CanvasGetSetPixel sub) inside the picCDraw_MouseDown event.

If blnDrawFillClicked Then
   'Get Current Color
   Dim old_color As Color = bImage.GetPixel(e.X, e.Y)
   'New Color = Selected Color
   Dim new_color As Color = cColor
   If e.Button = MouseButtons.Left Then
      'Fill Shape With New Color If Left Clicked
      CanvasFloodFill(DirectCast(picCDraw.Image, Bitmap), _
                                 e.X, e.Y, new_color)
   Else
      'Fill Shape With Old Color If Right Clicked
      CanvasFloodFill(DirectCast(picCDraw.Image, Bitmap), _
                                 e.X, e.Y, old_color)
   End If
End If

When run, the user is now able to fill the drawn shapes. He/she will select either choose to draw in Outline mode or in Full Fill mode. The user cannot draw with Draw then Fill mode! Once the user has drawn a shape, he/she needs to select the Draw then Fill option. Once selected, he/she can just click once inside the section he/she wants filled. If the user wants to draw again, he/she must select Outline Draw or Full Fill Draw again. Large drawn objects will take a bit longer to fill than smaller shapes.

Creating Your Own Drawing Application with Visual Basic.NET, Part 2

It's always good to include visible clues as to what is happening in the program. That is why you need to change the cursor to something else when you are filling the objects. To do this, follow these steps:

  • Inside picCDraw_MouseMove, add the following, above all the existing code:

    If blnDrawFillClicked _
       And Not blnDrawClicked _
       And Not blnSquareClicked _
       And Not blnCircleClicked _
       And Not blnTriangleClicked _
       And Not blnEraserClicked Then
          Me.Cursor = Cursors.PanSouth
       Else
          Me.Cursor = Cursors.Cross
    End If
    
  • This will change your cursor according to your actions. If you are not filling the shapes, your cursor will look like a cross. If you are filling the shapes, the cursor will look like a downward pointing arrow.

  • Almost the last thing you need to do here is to also change the behaviour of the butCClear button. When you move your mouse over this button, it mustn't show the downward pointing arrow cursor, so quickly change that. Add the following code to the butCClear_MouseEnter event:

    Private Sub butCClear_MouseEnter(ByVal sender As Object, _
       ByVal e As System.EventArgs) Handles butCClear.MouseEnter
       Me.Cursor = Cursors.Cross
    End Sub
    

Accommodating the Part 2 Code

Seeing the fact that the way you are drawing has changed a lot since Part 1, you need to be able now to determine which mode (Outline, FullFill Draw, Draw Then Fill) you are in. You achieve this through three new variables that you can declare in your Module:

'Is The Fill Draw Tool Clicked?
Public blnFillDrawClicked As Boolean
'Is The Draw Fill Tool Clicked?
Public blnDrawFillClicked As Boolean
'Is The Outline Draw Tool Clicked?
Public blnOutlineDrawClicked As Boolean

These variables will keep track of which mode you are in. In frmTools, you already have set these variables to true depending on which was selected. Now, you need to modify your picCDraw_MouseUp to first determine which mode you are in, and then draw according to that. Change your old picCDraw_MouseUp event:

The circle.

Change this section:

'Determine If The Circle Tool Has Been Clicked

If blnCircleClicked Then
   'Yes, It Has Been Clicked, Set The Pen's Color To Selected Color
   pCirclePen.Color = cColor
   'Draw The Circle With The Current Starting, And Ending Values
   gCanvas.DrawEllipse(pCirclePen, sStartX, sStartY, _
                       sEndX - sStartX, sEndY - sStartY)
   'Was The Fill Tool Clicked?

   If blnFillClicked Then
      'Yes, Set The Brush Color To The Selected Color
      sbCircleBrush.Color = cColor
      'Fill The Shape Being Drawn
      gCanvas.FillEllipse(sbCircleBrush, sStartX, sStartY, _
                          sEndX - sStartX, sEndY - sStartY)
   End If

End If

To:

'Determine If The Circle Tool Has Been Clicked

If blnCircleClicked Then

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

   If blnOutlineDrawClicked Then

      'Draw The Circle With The Current Starting, And Ending Values
      gCanvas.DrawEllipse(pCirclePen, sStartX, sStartY, _
                          sEndX - sStartX, sEndY - sStartY)
   End If

   'Was The Fill Tool Clicked?

   If blnFillDrawClicked Then

      'Yes, Set The Brush Color To The Selected Color
      sbCircleBrush.Color = cColor
      'Fill The Shape Being Drawn
      gCanvas.FillEllipse(sbCircleBrush, sStartX, sStartY, _
                          sEndX - sStartX, sEndY - sStartY)
   End If

End If

Square

Change this part:

'Determine If The Square Tool Has Been Clicked

If blnSquareClicked Then

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

   'Draw The Square With The Current Starting, And Ending Values

   Dim SquareX As Integer = Math.Min(sStartX, sEndX)
   Dim SquareY As Integer = Math.Min(sStartY, sEndY)
   Dim SquareWidth As Integer = Math.Abs(sStartX - sEndX)
   Dim SquareHeight As Integer = Math.Abs(sStartY - sEndY)
   gCanvas.DrawRectangle(pSquarePen, SquareX, SquareY, _
                         SquareWidth, SquareHeight)
   'Was The Fill Tool Clicked?

   If blnFillClicked Then

      'Yes, Set The Brush Color To The Selected Color
      sbSquareBrush.Color = cColor
      'Fill The Shape Being Drawn
      gCanvas.FillRectangle(sbSquareBrush, SquareX, SquareY, _
                            SquareWidth, SquareHeight)
   End If 

End If

To:

'Determine If The Square Tool Has Been Clicked

If blnSquareClicked Then

   'Yes, It Has Been Clicked, Set The Pen's Color To Selected Color
   pSquarePen.Color = cColor
   Dim SquareX As Integer = Math.Min(sStartX, sEndX)
   Dim SquareY As Integer = Math.Min(sStartY, sEndY)
   Dim SquareWidth As Integer = Math.Abs(sStartX - sEndX)
   Dim SquareHeight As Integer = Math.Abs(sStartY - sEndY)

   If blnOutlineDrawClicked Then

      'Draw The Square With The Current Starting, And Ending Values
      gCanvas.DrawRectangle(pSquarePen, SquareX, SquareY, _
                            SquareWidth, SquareHeight)
   End If

   'Was The Fill Tool Clicked?

   If blnFillDrawClicked Then

      'Yes, Set The Brush Color To The Selected Color
      sbSquareBrush.Color = cColor
      'Fill The Shape Being Drawn
      gCanvas.FillRectangle(sbSquareBrush, SquareX, SquareY, _
                            SquareWidth, SquareHeight)
   End If

End If

Triangle

Change this section:

'Determine If The Triangle Tool Has Been Clicked

If blnTriangleClicked Then

   'Yes, It Has Been Clicked, Set The Pen's Color To Selected Color
   pTrianglePen.Color = cColor
   'Create The Triangle Based On User's Mouse Coordinates + 150,
   'To Make The Triangle Display A Bit "Fatter"
   gCanvas.DrawLine(pTrianglePen, sStartX, sStartY, sEndX, sEndY)
   gCanvas.DrawLine(pTrianglePen, sEndX, sEndY, _
                    Me.MousePosition.X, _
                    Me.MousePosition.Y + 150)
   gCanvas.DrawLine(pTrianglePen, sStartX, sStartY, _
                    Me.MousePosition.X, Me.MousePosition.Y + 150)
   'Was The Fill Tool Clicked?

   If blnFillClicked Then

   'Use A Point Array, In Order To Be Able To Use The FillPolygon
   'Method - To Fill The Triangle
      Dim pTPoints As Point() = { _
      New Point(sStartX, sStartY), _
      New Point(sEndX, sEndY), _
      New Point(Me.MousePosition.X, Me.MousePosition.Y + 150)}
      'Yes, Set The Brush Color To The Selected Color
      sbTriangleBrush.Color = cColor
      'Fill The Shape Being Drawn
      gCanvas.FillPolygon(sbTriangleBrush, pTPoints)
   End If
End If

To:

'Determine If The Triangle Tool Has Been Clicked
If blnTriangleClicked Then

   'Yes, It Has Been Clicked, Set The Pen's Color To Selected Color
   pTrianglePen.Color = cColor
   If blnOutlineDrawClicked Then

      Dim p(2) As Point
      p(0) = New Point(sStartX - 10, sStartY)
      p(1) = New Point(sEndX, sEndY)
      p(2) = New Point(sStartX + 50, sStartY - 50)
      gCanvas.DrawPolygon(pTrianglePen, p)
   End If

   'Was The Fill Tool Clicked?

   If blnFillDrawClicked Then

      'Use A Point Array, In Order To Be Able To Use The
      'FillPolygon Method - To Fill The Triangle

      Dim pTPoints As Point() = { _
         New Point(sStartX - 10, sStartY), _
         New Point(sEndX, sEndY), _
         New Point(sStartX + 50, sStartY - 50)}
      'Yes, Set The Brush Color To The Selected Color
      sbTriangleBrush.Color = cColor
      'Fill The Shape Being Drawn
      gCanvas.FillPolygon(sbTriangleBrush, pTPoints)
   End If

End If

What you did here was to enable all the modes of drawing whenever the Circle, Square, or Triangle is clicked. You may have noticed the new way of creating the triangular-shaped button, but I'll get into more details a bit later.

Creating Your Own Drawing Application with Visual Basic.NET, Part 2

Adding a Marquee While Drawing

All this, and you still don't have a visible clue about the dimensions of your drawing. While drawing, users expect some way of indicating the bounds of the drawing to them, and when the drawing is complete. Including this functionality into your applications is quite easy. Do it step by step.

Step 1: Declare the Following Modular variables to keep track of your mouse:

Dim blnMouse As Boolean    'Is The Mouse Down?
Dim ptStart  As Point      'Start Point For Marquee
Dim ptEnd    As Point      'End   Point For Marquee

Step 2: Create your Marquee sub:

Private Sub CanvasMarquee(ByVal pt1 As Point, ByVal pt2 As Point)
Dim sr As Rectangle          'Our Actual Rectangle
pt1 = PointToScreen(pt1)     'Computes Location Of Specified Client
                             'Point Into Screen Coordinates
pt2 = PointToScreen(pt2)     'Computes Location Of Specified Client
                             'Point Into Screen Coordinates

If (pt1.X < pt2.X) Then      'If pt1 Is Smaller Than pt2
   'Drawn Rectangle's X = pt1
   sr.Width = pt2.X - pt1.X  'Drawn Rectangle's Width
                             '(From Left To Right)
Else
   sr.X = pt2.X                'Draw Rectangle's X = pt2
   sr.Width = pt1.X - pt2.X    'Drawn Rectangle's Width
                               '(From Right To Left)
End If
If (pt1.Y < pt2.Y) Then          'If pt1 Is Smaller Than pt2
   sr.Y = pt1.Y                  'Drawn Rectangle's Y = pt1
   sr.Height = pt2.Y - pt1.Y     'Drawn Rectangle's Height
                                 '(From Top To Bottom)
Else
   sr.Y = pt2.Y                  'Drawn Rectangle's Y = pt2
   sr.Height = pt1.Y - pt2.Y     'Drawn Rectangle's Width
                                 '(From Bottom To Top)
End If

   'Draw a Reversible Frame On Screen Within Specified Bounds,
   'With Specified Background Color, And In Specified State
ControlPaint.DrawReversibleFrame(sr, Color.White, FrameStyle.Dashed)
End Su

The above sub computes the dimensions of your current shape based on your current mouse movements, and then draws the rectangular-shaped marquee while you are drawing.

Step 3: Initialise your blnMouse variable in frmCanvas_Load:

blnMouse = False

Step 4: picCDraw_MouseDown Initialise Starting and Ending Points:

ptStart.X = e.X    'Start = Current X
ptStart.Y = e.Y    'Start = current Y
ptEnd.X = -1
ptEnd.Y = -1
blnMouse = True

Step 5: Inside picCDraw_MouseUp, reset the blnMouse variable to False again, so that you can draw another new rectangle at a new location:

blnMouse = False

Step 6: Still in picCDraw_MouseUp, refresh all the other variable values to be able to keep track of the next rectangle:

If (ptEnd.X <> -1) Then       'If ptEnd Has A Proper Value
   Dim ptCurrent As Point
   ptCurrent.X = e.X
   ptCurrent.Y = e.Y
   CanvasMarquee(ptStart, ptEnd)    'Draw Marquee
End If
'Refresh Marquee Values
ptEnd.X   = -1
ptEnd.Y   = -1
ptStart.X = -1
ptStart.Y = -1

Step 7: Inside picCDraw_MouseMove, do the actual drawing of the Rectangle:

If (blnMouse) Then
   Dim ptCurrent As Point
   ptCurrent.X = e.X
   ptCurrent.Y = e.Y
   If (ptEnd.X <> -1) Then
      CanvasMarquee(ptStart, ptEnd)
   End If
   ptEnd = ptCurrent                    'Set ptEnd's value
   CanvasMarquee(ptStart, ptCurrent)    'Draw Marquee
End If

Here, you call the CanvasMarquee sub with the current Starting and Ending values.

Step 8: Inside picCDraw_MouseMove, set the blnMouse variable to False when you are drawing with the Pencil:

If blnDrawing Then
   'Yes We Are, Set The Pen Color To Selected Color
   pDrawingPen.Color = cColor
   'Draw The Free Hand Lines
   gCanvas.DrawLine(pDrawingPen, sStartX, sStartY, e.X, e.Y)
   'Set New Starting Points For Next Line. New Starting Points Are
   'The End Points Of Previous Line
   sStartX = e.X
   sStartY = e.Y
   'Set The Images Drawn Thus Far In The Picture Box = To The In -
   'Memory Image Object
   Me.picCDraw.Image = bImage
   blnMouse = False
End If

Step 9: Inside picCDraw_MouseMove, set the blnMouse variable to False when you are Erasing:

If blnErasing Then
   'Yes We Are, Draw The Eraser Lines
   gCanvas.DrawLine(pEraserPen, sStartX, sStartY, e.X, e.Y)
   'Set New Starting Points For Next Line. New Starting Points Are
   'The End Points Of Previous Line
   sStartX = e.X
   sStartY = e.Y
   'Set The Images Drawn Thus Far In The Picture Box = To The In -
   'Memory Image Object
   Me.picCDraw.Image = bImage

   blnMouse = False
End If

What will happen here is that while you draw any shape, a Rectangular Dashed line will grow and shrink depending on your Mouse movements. By doing so, it indicates to you more or less the diameter of the shape you are currently drawing.

[Marquee.png]

Perfecting Your Triangle

The last section in this part is to refine your triangle logic. In Part 1, the code (in the Picturebox's MouseUp event) used to produce the Triangle drawing looked like this:

Canvas.DrawLine(pTrianglePen, sStartX, sStartY, sEndX, sEndY)
gCanvas.DrawLine(pTrianglePen, sEndX, sEndY, Me.MousePosition.X, _
                 Me.MousePosition.Y + 150)
gCanvas.DrawLine(pTrianglePen, sStartX, sStartY, _
                 Me.MousePosition.X, Me.MousePosition.Y + 150)

Filling the triangle looked like this:

Dim pTPoints As Point() = { _
   New Point(sStartX, sStartY), _
   New Point(sEndX, sEndY), _
   New Point(Me.MousePosition.X, Me.MousePosition.Y + 150)}

The whole problem with the above was the fact that I used the current MousePosition, and then added 150 to it. It wasn't necessary to use the MousePosition X and Y properties because you already have objects storing the ending points (sEndX and sEndY). It was definitely an overkill to add a further 150 to the ending mouse positions!

The above code produced Triangles that looked like the following picture:

[OldTriangles.png]

As you can see, these triangles did not look pretty. Also, these triangles were completely out of proportion. You can fix this problem quickly.

Replace the Outline Triangle part with this:

Dim p(2) As Point
p(0) = New Point(sStartX - 10, sStartY)
p(1) = New Point(sEndX, sEndY)
p(2) = New Point(sStartX + 50, sStartY - 50)
gCanvas.DrawPolygon(pTrianglePen, p)

As you can see here, I'm making use of a Point array to keep track of the various X and Y values for each corner. You will also note that you add/subtract 50 from the last point, to make the triangles smaller, and more visually appealing.

Replace the Filling Triangle part with:

Dim pTPoints As Point() = { _
   New Point(sStartX - 10, sStartY), _
   New Point(sEndX, sEndY), _
   New Point(sStartX + 50, sStartY - 50)}

It follows the same principle as the Outline Triangle, but just declares the Point Array differently. Now, your Triangles look like this:

[Triangle.png]

I think you'll agree that these triangles look much much better! All these changes and improvements you have done today have made your drawing application very powerful, and if you have followed all the steps above, you will have gained a lot in the various techniques used in ordinary drawing applications. If you haven't followed all the steps above (or if all the above steps confused you bit), you can still look at the sample included with this article where all these methods are present. As you may have noticed by now, Canvas is not really an ordinary drawing application, especially after you see Part 3.

In Part 3, you will experiment with drawing odd shapes such as Hearts, Spirals, Stars, and so forth. I will also introduce you to different filling options such as filling your shapes with Gradients, using brushes other than the solid brush you have been using all along; you will fill the shapes with patterns—for example, pictures. Part 3 is even more fun than Part 1 and Part 2!

I sincerely hope you have enjoyed this lesson; and hope to see you reading Part 3.



About the Author

Hannes du Preez

Hannes du Preez is a Microsoft MVP for Visual Basic. He is a trainer at a South African-based company. He is the co-founder of hmsmp.co.za, a community for South African developers.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds