Canvas.Paint
From Xojo Documentation
The area needs to be redrawn, such as when it has been covered by another window and then uncovered. Use the g parameter for all drawing. The areas parameter contains the areas of the Canvas that need redrawing. If areas is empty then the entire Canvas needs to be redrawn.
Notes
Directly accessing the Graphics property of the Canvas has been removed as of 2018r3. Use this event instead for all drawing. |
The g parameter gives you access to the Graphics class methods for drawing. Use the Paint event handler for all drawing. You can also use a method called from the Paint event handler, but be sure to pass the Graphics object from the Paint event to your method.
You can continue to have code outside the Paint event handler draw directly to your own Picture properties. To display the Picture, simply draw it to the Canvas in the Paint event handler (using g.DrawPicture).
On Windows only, the EraseBackground property determines whether the entire Canvas background is erased before repainting.
Do not call Invalidate or Refresh from within the Paint event as this can cause an infinite loop and eventual app crash.
Redrawing Areas
By redrawing just the specific areas that have changed, you can significantly improve your drawing performance. When you call Invalidate (specifying the rect to invalidate), the areas parameter contains the coordinates for the rectangles that need redrawing. You can then choose to redraw just those parts of the Graphics or you can continue to redraw everything as you have done in the past. This parameter may also contain coordinates that do not come directly from Invalidate calls as the operating system is ultimately in control of drawing.
When this event is called as a result of an Invalidate call, the drawing is clipped to only the invalidated regions regardless of whether you use the areas parameter.
Sample Code
This code draws a 3D rectangle with a raised look:
Const White = &cffffff
Const DarkGray = &c8c8c8c
Const LightGray = &cefefef
g.DrawingColor = White
g.DrawLine(1, 1, Me.Width, 1)
g.DrawLine(1, Me.Height - 1, 1, 1)
g.DrawingColor = DarkGray
g.DrawLine(Me.Width - 1, 2, Me.Width - 1, Me.Height)
g.DrawLine(1, Me.Height - 1, Me.Width, Me.Height - 1)
//fill in the light gray rectangle
g.DrawingColor = LightGray
g.FillRectangle(2, 2, Me.Width - 3, Me.Height - 3)
This code uses the Paint event handler of a Canvas control to draw the text "The quick brown fox" in Helvetica bold, italic, 18 point, 50 points from the top of and 10 points from the left side of the control:
g.Bold = True
g.Italic = True
g.FontName = "Helvetica"
g.FontSize = 18
g.DrawString("The quick brown fox", 10, 50)
This code assigns the color of a particular point in a Canvas control to a variable:
This code sets a particular point of a Canvas control to a specific RGB value:
This is code draws a triangle in a Canvas. It is placed in the Paint event. The parameter g as Graphics is passed into this event:
Var points(6) As Integer
points(1) = 10 // X of Point 1
points(2) = 10 // Y of Point 1
points(3) = 75 // X of Point 2
points(4) = 30 // Y of Point 2
points(5) = 10 // X of Point 3
points(6) = 125 // Y of Point 3
g.DrawingColor = RGB(100, 200, 255)
This code uses the Clip method to define child Graphics items within the parent Canvas. The code is in the Paint event of a Canvas. The two clippings define regions at the top of the canvas and the DrawOval method draws object in each one. Notice that the first call tries to draw an oval that is wider than the region. It is truncated in the drawing.
Var myClip As Graphics = g.Clip(0, 0, 150, 15)
Var myClip2 As Graphics = g.Clip(150, 0, 150, 15)
// draw the border of the Canvas..
g.DrawingColor = &c000000
g.DrawRectangle(0, 0, g.Width, g.Height)
// draw into the first area...
myClip.DrawingColor = &cff0000
myClip.DrawRectangle(0, 0, myClip.Width, myClip.Height) // draw the border of the area..
myClip.DrawOval(0, 0, 200, 15) // the oval does not appear outside the region despite the call
// draw into the second area...
myClip2.DrawingColor = &c0000ff
myClip2.DrawRectangle(0, 0, myClip2.Width, myClip2.Height) // draw the border of the area
myClip2.DrawOval(0, 0, 150, 15)
Creating a Custom Control
This simple Canvas control changes from black to white when you click on it.
Drag a Canvas control into a window. Add a Boolean property (mBlack) to the Window. This is used toggle the color of the Canvas when it is clicked.
The code for the Canvas MouseDown event handler looks like this:
mBlack = Not mBlack // Toggle the color
Me.Invalidate(False) // Tell the Canvas to draw itself
End Function
This code toggles the mBlack value when the Canvas is clicked and then tells the Canvas to redraw itself. In the Paint event handler, you draw the rectangle setting the color based on the value of mBlack:
If mBlack Then
g.DrawingColor = &c000000
Else
g.DrawingColor = &cffffff
End If
g.FillRectangle(0, 0, g.Width, g.Height)
End Sub
Optimization to draw only in the given areas
Add the following functions to a class or module:
left = Max (left, rectangle.Left)
top = Max (top, rectangle.Top)
Var right As Integer = Min(left + width, rectangle.Left + rectangle.Width)
Var bottom As Integer = Min(top + height, rectangle.Top + rectangle.Height)
Return (left < right) And (top < bottom)
End Function
Function IsWithinRects(left As Integer, top As Integer, width As Integer, height As Integer, rects() As Rect) As Boolean
For Each r As Rect In rects
If IsWithinRect(left, top, width, height, r) Then
Return True
End If
Next
End Function
With that, you can test if an object you want to draw within a particular position needs drawing at all, possibly speeding up the drawing process:
// Draw an image at position 100 / 100, but only if needed
If areas.Ubound < 0 Or IsWithinRects(100, 100, theImage.Width, theImage.Height, areas) Then
g.DrawPicture(theImage, 100, 100)
End If
See Also
Window.Paint event