Custom Table Cell canvases need manually refreshed when created

Not sure if this is a bug or expected behavior. If it’s expected, getting some tips on what’s going on under-the-hood would be great so I can work with instead of around the issue… :wink:

I have custom cells which pull their data from the internet (e.g., downloading an image) and display them in a canvas control within the container.

For this reason, the data source will create all the cells it needs when created/refreshed and saves them in an array. The RowCount method is just the Count of the array and the RowData method is just returning the cell at the given row that already exists. This way, as the user scrolls around, they aren’t re-downloading the same data every single time the cell happens to get back onto the screen.

Now to the issue…

Something within Xojo seems to be caching the rendered canvas data or something. I have some user interactions which allow them to reload the data source with new data (e.g., data from a different day). When this happens, I RemoveAll the cells from array and table.CreateCustomCell again for all the new cells that will contain the updated data. I then call ReloadDataSource.

However, when the table refreshes, the cells rendered are a complete mess. Some are the new ones, but in the wrong order, and others are from the old table data. When I inspect the new cells, though, they are 100% correct: all the data in them is correct and fine.

What I’m forced to do is Refresh the canvas controls within the newly created cells like so:

cells.RemoveAll

for each r as Record in LoadedRecords
    var cell as MobileTableCellData = table.CreateCustomCell(GetTypeInfo(CustomCell))
    var c as CustomCell = CustomCell(cell.Control)

    // Initialize members of the custom cell control here

    c.CustomCanvas.Refresh ' <-- doing this fixes everything!

    // add the cell to the data source
    cells.Add cell
next

table.ReloadDataSource
table.EndRefresh

But it’s completely baffling to me why that’s the case. It should be a brand new control. It should automatically redraw the canvas as it’s brand new.

If it’s using cached render surfaces for performance, I totally get that, but it should still force a redraw automatically, I’d think.

If I try and put the Refresh somewhere else, like in the CustomCell class’s Opening event or Constructor, then it doesn’t work. It only seems to work if I call it the way I’m doing it above.

You are assuming that is a new control, but on iOS the cells are reused so they can be old instances (performing reason), so you have to consider your cell content as with an “indeterminate” previous state

So it’s potentially non only for canvas, but also, for example, for labels if you use a color in some condition (in this case you have to set the color for every case), or check the constrains if you use a specific layout constrain for some case.

This is how iOS works

First of all, if you’re using a DataSource, you shouldn’t be prepopulating the table with data. There’s an interface (iOSMobileTableDataSource) that you need to implement on the object that manages the connection to your DataSource which includes a method called RowData where you dynamically create a single row when the table asks for it. This allows the OS to manage the 15 or so rows at any given time that are currently visible.

Basically, you ask the system to create or retrieve a cell of a specific type from the cache that it has already created, update it with your new data and return it to the OS for display. Then your rows are always up to date.

1 Like

But canvases that do stuff in Paint(), don’t display the right data unless you go at them hard.
I struggled with this myself for a week recently and ended up using Imageviewers instead… set an image and they will display it properly
Ask a canvas to display ‘something from somewhere’ and it often displays what it had before it was reused.
The re-use of a cell should trigger a repaint but it doesn’t seem to do so, (whether thats iOS or Xojo’s responsibility)

Well right. When you pull the cell from the cache, you need to issue a Refresh on the canvas.