DesktopCanvas.Paint
From Xojo Documentation
New in 2021r3
Supported for all project types and targets.
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
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).
Do not call 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 Refresh (specifying the rect to refresh), 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 Refresh call, the drawing is clipped to only the refreshed regions regardless of whether you use the areas parameter.
MacOS Big Sur and above no longer reliably provide information as to areas that need to be redrawn. |
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 DesktopCanvas 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.DrawText("The quick brown fox", 10, 50)
This code assigns the color of a particular point in a DesktopCanvas control to a variable:
This code sets a particular point of a DesktopCanvas control to a specific RGB value:
This is code draws a triangle in a DesktopCanvas. 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 DesktopCanvas. The code is in the Paint event of a DesktopCanvas. 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 DesktopCanvas control changes from black to white when you click on it.
Drag a DesktopCanvas control into a window. Add a Boolean property (mBlack) to the Window. This is used toggle the color of the DesktopCanvas when it is clicked.
The code for the DesktopCanvas MouseDown event handler looks like this:
mBlack = Not mBlack // Toggle the color
Me.Refresh(False) // Tell the Canvas to draw itself
End Function
This code toggles the mBlack value when the DesktopCanvas 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
DesktopWindow.Paint event