Canvas Paint working in MacOS but not Windows

Hi everyone. I’ve been working on a project over the last few days which I would like to be cross-platform so I’ve chosen Xojo. It’s all working great in MacOS, but when I build it for Windows I’m having issues with canvas rendering. The same issue occurs whether I cross-compile for Windows from the MacOS version of Xojo or compile for Windows within the Windows version.

The rough layout of the GUI is as follows:

  • Canvas subclass ‘CollectionCanvasCell’ which uses its Paint event to draw a fill colour and some text. The colour and text for each cell are returned from methods defined in a datasource class interface
  • A container control subclass ‘CollectionCanvas’ which has a control set of CollectionCanvasCell within a canvas and a scrollbar so the set of CollectionCanvasCells can be scrolled. The number of cells and some layout properties are returned from other methods in the datasource class interface
  • Instances of CollectionCanvas are then embedded into windows, the datasource property set to an object which implements the datasource class interface, and a method on the CollectionCanvas instance called to lay out the CollectionCanvasCell items

This works great in MacOS. It’s fast, allows code reuse fairly easily, and makes the collection very customisable. Anyone familiar with UICollectionView or NSCollectionView in Cocoa will recognise the concept.

The problem is that in Windows, the CollectionCanvasCell items are not visible. They are there, because when I click where I know they should be the software responds as though I had clicked on it.

I also have another Canvas subclass which allows dragging to resize the CollectionCanvas like a resizabe pane. Again this works in that I can drag to resize, but the graphics which are drawn on the control in its Paint event are again not displayed in Windows.

The Paint event is definitely being called because I can for example change the window title within this method. I’ve also tried hard coding the values for fill colour, text colour and text in the Paint events but they still aren’t visible.

The CollectionCanvasCells are invalidated and redrawn frequently. They have computed properties with backing variables for things like the background colour, text etc, and the cells are invalidated in each of the Set methods for these properties. They are also redrawn or invalidated in response to certain user actions, such as clicking to highlight, triggering long press visualisation animations, editing the text etc.

I tried adding calls to invalidate and refresh the window and CollectionCanvas. In my main window’s Open event I added ‘Self.Invalidate’ and ‘Self.TracksCollectionCanvas.Invalidate’ after the code which draws the items in the CollectionCanvas. It didn’t make any difference so I tried using Refresh instead of Invalidate and that didn’t work either.

Have you checked the parent of the cells?
have you checked the z-order (bringing controls to front or back?)

OTT:
(Sorry, people cannot resist with ‘I wouldnt do it that way’ - feel free to ignore the rest of this message…)

Its hard to follow what you are doing from the text above, but in your shoes I might have been tempted to ditch the container controls and just add properties and methods to a subclass of canvas.

For example, if it needed to draw a collection of ‘fruit’ objects,
Define a fruit class with properties
Give it a ‘drawyourself’ method taking a graphics parameter, plus maybe x,y positions and a scale factor

Then to the canvas subclass, add a dictionary/collection of fruits as a property

In the paint event, iterate through the fruit collection and pass each of them the g parameter into their ‘drawyourself’ method .

Thanks Jeff. I haven’t checked the z order yet, though I know for a fact that nothing is above them. I’ll check it nonetheless and report back. Is there anything specific I should be checking the parent of the cells for? I have set a background colour on it which does show, so I know it at least is where it should be. I appreciate the alternative method. The reason I did it this way is mainly because there are multiple controls which are reused, namely a canvas, a scrollbar, a title, and some borders and lines. I could paint the title and lines, but I’d like to be able to reuse the scrolling code if possible. Though I suppose I could do this by subclassing Scrollbar. I’m just trying to reuse as much code as possible.

Instead of us asking 100 questions about how things are set up in detail (e.g. what properties are set at runtime and design time, the order that things are altered and the sequence of events) are you able to replicate the problem in a simple example that you can upload?

@JulianS - I can copy the relevant classes to a new project and upload, sure.

1 Like

That would be perfect if it shows the problem, thanks.

Well this is interesting. I just created a new project and copied the minimum classes needed to get this working (which was quite a few as there’s a fair bit of interconnection), and it worked. So now I need to work out why it’s working in a test project but not in the real one!

And down the rabbit hole you go :wink:

My initial suggested places to look would be:

a) changing settings in paint events causing recursion
b) changing visibility of controls during initialisation

Haha yes, I’ve been down this rabbit hole many times in different IDEs and for different reasons xD I’ll start with your suggestions and go from there. Thanks guys, I’ll report back!

1 Like

Thanks again for the suggestions here everyone. In the end I felt that my implementation was too complex, so I simplified it by separating out the code which gets the data from the datasource, using this to build an array of an intermediary custom class containing these data, and then in my canvas Paint event I iterate through this array and draw the backgrounds and text directly onto the canvas. I can also use this array in mouse events to detect hits within the items and trigger highlighting and click events etc. Also, I removed the need for a container control by rolling my own scroll bar (which took quite a bit of head scratching to work out how to calculate the ratios for correct rendering and behaviour). Now I’ve got all that done I’ve got a control which behaves very similarly to UICollectionView/NSCollectionView, which gathers its data from a datasource object, and which renders identically under MacOS and Windows.

As to the original question, I don’t know what was causing the rendering issue but I suspect it was something to do with the way the rendering and datasource polling code were integrated, or differences in refresh behaviour between platforms, or more likely some combination of these issues. In any case, this much more simple mechanism is working faster and more reliably and renders without a hitch!

Thanks for the help here everyone. Although I didn’t get to the bottom of the issue, your responses did guide me to a solution nonetheless.

1 Like