PiedSoftware 3 Posted October 11, 2020 (edited) Hi I have a TScrollBox that I am placing a number of "Cards" that are TFrame subclasses onto. As the number of cards increases, the performance of loading the list of cards initially is getting too long. Is there a way to delay loading these cards until they are scrolled into view? i.e. is there a way to trap the event of a card being shown in the scroll box? Edited October 12, 2020 by PiedSoftware Fixing typo Share this post Link to post
Anders Melander 1782 Posted October 11, 2020 Something like this? (untested, just a guess) interface type TMyFrame = class(TFrame) private FHasLoaded: boolean; protected procedure PaintWindow(DC: HDC); override; procedure DoLoadFrame; end; implementation procedure TMyFrame.PaintWindow(DC: HDC); begin if (not FHasLoaded) then DoLoadFrame; inherited; end; procedure TMyFrame.DoLoadFrame; begin FHasLoaded := True; // Do load stuff here... end; Of course the frames has to be created before they can be scrolled into view but you can defer loading the frame content. I would probably solve the problem in a different way but I think the above does what you asked for. 1 Share this post Link to post
PiedSoftware 3 Posted October 12, 2020 Thanks for that, Anders. I am open to a better solution, if you want to suggest one. Share this post Link to post
Fr0sT.Brutal 900 Posted October 12, 2020 I use delayed loading called from PaintWindow, you can check it here https://github.com/Fr0sT-Brutal/Delphi_OSMMap/blob/master/Source/OSM.MapControl.pas Share this post Link to post
Remy Lebeau 1394 Posted October 12, 2020 (edited) 18 hours ago, PiedSoftware said: I have a TScrollBox that I am placing a number of "Cards" that are TFrame subclasses onto. As the number of cards increases, the performance of loading the list of cards initially is getting too long. What is on the Cards exactly? When a UI has to display a lot of elements that start hindering performance, that is when I start considering either redesigning the UI to use different higher-performant controls, or switching to owner-drawing as much as possible to reduce resource usage. Edited October 12, 2020 by Remy Lebeau 1 Share this post Link to post
PiedSoftware 3 Posted October 13, 2020 Remy: There are 3 labels and a shape. The shape sits on the bottom to provide a diving line. Share this post Link to post
PiedSoftware 3 Posted October 13, 2020 I was overriding SetBounds in the card frame and it was getting called literally 10,000s of times and taking up most of the time. So, I have got rid of that. Here is some timing information, where I accumulate the time taken in various processes, and then display it in seconds, sorted by time in descending order. The second figure is the number of calls. There is overlap. UpdateCardList is the overall time. 1.935s is still a long time, but much better. Using SetBounds: 16:32:19 Update cards for HUN3 16:32:19 Record count = 202 16:32:24 Listing accumulated times: 16:32:24 * UpdateCardList: 4.569, 1 16:32:24 * SetBounds: 2.546, 95849 16:32:24 * Load cards from qry: 2.357, 1 16:32:24 * Create cards: 2.062, 1 Without SetBounds: 16:38:53 Update cards for HUN3 16:38:53 Record count = 202 16:38:55 Listing accumulated times: 16:38:55 * UpdateCardList: 1.935, 1 16:38:55 * Load cards from qry: 0.881, 1 16:38:55 * Create cards: 0.789, 1 Share this post Link to post
Vincent Parrett 750 Posted October 13, 2020 (edited) Are all the cards the same height with the same layout? If so then something like this might be faster - https://github.com/VSoftTechnologies/VSoft.VirtualListView - it's working well for me in the application I'm using it for I pre-calculate the layout for the rows when the control resizes, so the painting is fast. It does take a lot more effort to write the layout and painting code but it's very smooth. The sample app doesn't show the pre-calc idea, you can see that here https://github.com/DelphiPackageManager/DPM/blob/master/Source/IDE/DPM.IDE.EditorViewFrame.pas I wrote this control for a specific purpose, but I have tried to keep it reusable for other things, it might still be a little rough around the edges. Edited October 13, 2020 by Vincent Parrett Share this post Link to post
PiedSoftware 3 Posted October 13, 2020 Vincent: I don't think we will have a fixed height of the cards. They contain memo text, which may potentially be configurable or automatically expand. Share this post Link to post
Fr0sT.Brutal 900 Posted October 13, 2020 Probably virtual list/tree view would be really easier to use. https://github.com/Virtual-TreeView/Virtual-TreeView is very full-featured and maintained; there's also https://github.com/TurboPack/MustangpeakEasyListview Share this post Link to post
PiedSoftware 3 Posted October 14, 2020 Thanks, Fr0sT.Brutal. We are using TJvTreeView and TTreeView in the project already, so if the current line I am following doesn't help, I will look into that. Share this post Link to post
PiedSoftware 3 Posted October 16, 2020 I have implemented Anders Melander's idea, and it works well. Part of the trick of getting it to work was that I needed to invalidate the cards explicitly to ensure that PaintWindow got called and that the Load method was called. Which was obvious, eventually. Here is the guts of the code that makes it work, from the abstract superclass of the cards: procedure TDBCard.SetKey(const Value: variant); begin fKey := Value; loaded := false; Invalidate; // Force call to Load in PaintWindow if card on screen end{ SetKey}; procedure TDBCard.PaintWindow(DC: HDC); begin inherited; if not loaded then begin loaded := true; list.DataQuery.Locate(list.KeyFieldName, key, []); Load(list.DataQuery); Color := list.CardColour(self, false); end{if}; end{ PaintWindow}; Share this post Link to post
Lars Fosdal 1792 Posted October 16, 2020 If your data takes a little time to load, you could delegate the loading to a background task which then again triggers another repaint on completion. That would eliminate any UI stutter due to load times. 1 Share this post Link to post
PiedSoftware 3 Posted October 20, 2020 Thanks Lars. I did actually try out a background thread, running the query, then creating the cards and populating them, but it didn't look good. The scroll bar and its contents remained inaccessible until the whole process had finished for some reason. But the 2 changes I ended up making, i.e. removing the override of SetBounds (which was to help with formating) and adding the override of PaintWindow, see above, has brought the speed of loading a couple of hundred to a reasonable value. Share this post Link to post