From Xojo Documentation

You are currently browsing the old Xojo documentation site. Please visit the new Xojo documentation site!

DesktopCanvas.Paint(g as Graphics, areas() As Rect)

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.


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, 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:

Sub Paint(g As Graphics)
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:

Sub Paint(g As Graphics)
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:

Sub Paint(g As Graphics)
Var c As Color
c = g.Pixel(10, 10)

This code sets a particular point of a DesktopCanvas control to a specific RGB value:

Sub Paint(g As Graphics)
g.Pixel(10, 10) = RGB(100, 105, 225)

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:

Sub Paint(g As Graphics)
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.

Sub Paint(g As Graphics)
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:

Function MouseDown(X As Integer, Y As Integer) As Boolean
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:

Sub Paint(g As Graphics)
If mBlack Then
g.DrawingColor = &c000000
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:

Function IsWithinRect(left As Integer, top As Integer, width As Integer, height As Integer, rectangle As Rect) As Boolean
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
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:

Sub Paint(g As Graphics, areas() As Rect)
// 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