Fabian1648 2 Posted July 24, 2023 Hi, In an Android app, I'm retrieving some data (3 columns) from a dataset and need to display it in a Listbox with 3 fields in each line. If I use the following procedure in a loop, adding one line after another, it works for about ten lines, but when it comes to displaying a hundred lines, the process is too slow. ... ListBox1.BeginUpdate; try for i:=0 ... ... AddListBoxItem(Listbox1,value1[i],value2[i],value3[i]); ... end; finally ListBox1.EndUpdate; end; //********************* procedure TForm1.AddListBoxItem(myListBox:TListBox;Definition, Qty1, Qty2:string); begin ... ListBoxItem:= TListBoxItem.Create(myListBox); ListBoxItem.Parent:= myListBox; ListBoxItem.ApplyStyleLookup; ListBoxItem.StylesData['Reference.text']:=Definition; ListBoxItem.StylesData['Qty1.text']:=Qty1; ListBoxItem.StylesData['Qty2.text']:=Qty2; ... end; It's much faster if the data extracted from the dataset are placed in a Stringlist and I transmit the data to the listbox in a single operation using "myListBox:=myStringList". In this case, the values are only in the 1st field of the listbox. How can I perform the same kind of operation to put the data in ListBox fields 2 and 3? Share this post Link to post
Serge_G 87 Posted July 25, 2023 Why don't you use a TListView with dynamic appearance and Livebindings ? Share this post Link to post
Fabian1648 2 Posted July 25, 2023 Hi, 1. If I don't use Livebindings, it's because I can't use Livebindings (the source is a dataset > I extract some columns of this dataset to display these in a visual component) 2. This speed problem occurs with all visual components (Combobox, Listbox, etc...) 3. I've already solved a similar problem with combobox: Dataset > Stringlist > Listbox.assign(Stringlist). For 1000 lines, the delay goes from more than 30 sec to 500 millisec!. In the case of the Listbox, I don't have just one dimension, but two: not just lines, but lines made up of 3 distinct fields. Share this post Link to post
Martifan 5 Posted July 30, 2023 The way you're currently doing this is creating a new TListBoxItem for each row of data, and then applying the styles and setting the data for each. This is a relatively expensive operation, particularly if you're doing it hundreds of times in a loop. Instead, consider batching your data updates. One common way to do this is to create a list of objects (each containing the three pieces of data for each row), and then update the ListBox with this list all at once. To do this, you could define a simple class to hold your data: type TListBoxData = class Definition: string; Qty1: string; Qty2: string; end; Then, you could create a list of these objects and fill it with your data: var DataList: TObjectList<TListBoxData>; i: Integer; Data: TListBoxData; begin DataList := TObjectList<TListBoxData>.Create; try for i := 0 to ... do begin Data := TListBoxData.Create; Data.Definition := value1[i]; Data.Qty1 := value2[i]; Data.Qty2 := value3[i]; DataList.Add(Data); end; UpdateListBox(DataList); finally DataList.Free; end; end; Then, in your UpdateListBox procedure, you could loop through this list of data objects and add them to the ListBox: procedure TForm1.UpdateListBox(DataList: TObjectList<TListBoxData>); var i: Integer; ListBoxItem: TListBoxItem; begin ListBox1.BeginUpdate; try for i := 0 to DataList.Count - 1 do begin ListBoxItem := TListBoxItem.Create(ListBox1); ListBoxItem.Parent := ListBox1; ListBoxItem.StylesData['Reference.text'] := DataList[i].Definition; ListBoxItem.StylesData['Qty1.text'] := DataList[i].Qty1; ListBoxItem.StylesData['Qty2.text'] := DataList[i].Qty2; ListBoxItem.ApplyStyleLookup; end; finally ListBox1.EndUpdate; end; end; This will allow you to create and set up all your TListBoxItem instances in one batch, which should significantly improve performance. If you still experience performance issues, you may want to look into using a virtualized list control that only creates and renders items as they're needed. This can significantly improve performance when dealing with large amounts of data. Please note that this code was written in Delphi since you posted Delphi-like code, but you mentioned Android in the beginning. If you're using Kotlin or Java for Android development, the approach would be different 1 Share this post Link to post
Fabian1648 2 Posted July 30, 2023 (edited) Hi, Thank for your answer Martifan. The code is actually written in Delphi. I tested your solution and I get exactly the same time to perform the operation as my best solution (6 sec for 1 000 items!). The following operation takes far too long because it adds one ListBoxItem after another for i := 0 to DataList.Count - 1 do begin ... ListBoxItem.StylesData['Reference.text'] := DataList[i].Definition; ListBoxItem.StylesData['Qty1.text'] := DataList[i].Qty1; ListBoxItem.StylesData['Qty2.text'] := DataList[i].Qty2; If I use a "Listbox.assign(ListObject)", the delay for 1 000 items goes from more than 6 sec to 500 millisec because all ListBoxItem are added in one shot. The problem is that only the field Reference of ListBoxItems is completed... The question is .... Is it possible to fill in the other fields of a ListBoxItem using an assign function? Edited July 30, 2023 by Fabian1648 Share this post Link to post
Martifan 5 Posted July 30, 2023 The slow speed is likely due to frequent user interface (UI) updates during the loop. To overcome this, you can use the ListBox.Items.BeginUpdate and ListBox.Items.EndUpdate methods to prevent the ListBox from updating the UI for each addition, thereby significantly speeding up the process. Here is an example of how to use it: ListBox.Items.BeginUpdate; try for i := 0 to DataList.Count - 1 do begin ... ListBoxItem.StylesData['Reference.text'] := DataList[i].Definition; ListBoxItem.StylesData['Qty1.text'] := DataList[i].Qty1; ListBoxItem.StylesData['Qty2.text'] := DataList[i].Qty2; ... end; finally ListBox.Items.EndUpdate; end; This BeginUpdate and EndUpdate pair can significantly increase the speed of your ListBox updates, as the control does not need to refresh the UI with each added item. Instead, it waits until all updates are complete and refreshes the UI just once, thus saving time. Share this post Link to post
Fabian1648 2 Posted July 31, 2023 (edited) I already use ListBox.Items.BeginUpdate and ListBox.Items.EndUpdate. This reduced the display of 1,000 ListBox items from 30 seconds to 6 seconds. As already written, what's wrong is adding one listboxitem after another in a loop. This is equivalent to an action for each item. If you assign a Listobject to the Listbox, it's a single action. I've tested the assignment method; it's immediate, but I can only do it for the main ListBox field. My question is whether it's possible to make an assignment that updates the other two ListBox fields by making a single assignment for the ListBox, or an assignment for each of the 3 fields, which would make it possible to do 3 actions at worst instead of 1000. Edited July 31, 2023 by Fabian1648 Share this post Link to post
Rollo62 539 Posted August 2, 2023 On 7/25/2023 at 10:01 AM, Fabian1648 said: Hi, 1. If I don't use Livebindings, it's because I can't use Livebindings (the source is a dataset > I extract some columns of this dataset to display these in a visual component) 2. This speed problem occurs with all visual components (Combobox, Listbox, etc...) If speed is your issue here, then maybe because you try to use too many items in the ListBox. Perhaps, better switch to TListView then ? Share this post Link to post