hall2000
asked on
Save a Tlistview
Hello,
I have a Tlistview with 4 columns, I need to save it to a file. and load it when the app loads again.
what I did is create 4 stringlists and save the values of each column in a stringlist, and then save those stringlists to a file. The only problem is that when I have 1000's of items on the listview it gets slow and takes a while to save it.
Is there any better way to do this?
I have a Tlistview with 4 columns, I need to save it to a file. and load it when the app loads again.
what I did is create 4 stringlists and save the values of each column in a stringlist, and then save those stringlists to a file. The only problem is that when I have 1000's of items on the listview it gets slow and takes a while to save it.
Is there any better way to do this?
In the first para, for treeview, read listview...
ASKER
But it still uses the loop that is making it slow... I am trying to eliminate the loop (if posible). thanks.
I doubt you will avoid the loop, and I doubt that it is the loop that is causing the delay. If you try getting Delphi to iterate through a loop of 100,000, I doubt you will see it take much time at all. I would suggest that it is the accessing of the items in the list view or the writing out to disk.
To illustrate the access issue I mentioned earlier, think about your listview having 10,000 items. Now, if your loop is accessing elements from the listview as in the first example, if you use the listview[i] notation, each time you reference the element, Delphi, behind the scenes is going to search through the treeview for the element you require. So, for element 1, you get 1 node access. For element 2, you get 1 (the first access in your loop) + 2 (the 2 because Delphi has had to scan item 1 again). For element 3, you will get 1 (your first iteration) + 2 (your second iteration) + 3 (delphi). You are therefore iterating through the list by a factorial value of the number of elements. Now, if you reference the item list three or four times in your loop, imagine the work that is going on...
My figures may not be precise, but safe to say that each time you reference the items array using an index, it takes an increasing amount of time for larger lists. My suggestion effectively 'caches' the element you are working on and does improve performance measurably. My findings are based on having spent time looking into what was causing delays in writing a treeview out to disk.
How about pasting the code in your loop so we can take a look?
To illustrate the access issue I mentioned earlier, think about your listview having 10,000 items. Now, if your loop is accessing elements from the listview as in the first example, if you use the listview[i] notation, each time you reference the element, Delphi, behind the scenes is going to search through the treeview for the element you require. So, for element 1, you get 1 node access. For element 2, you get 1 (the first access in your loop) + 2 (the 2 because Delphi has had to scan item 1 again). For element 3, you will get 1 (your first iteration) + 2 (your second iteration) + 3 (delphi). You are therefore iterating through the list by a factorial value of the number of elements. Now, if you reference the item list three or four times in your loop, imagine the work that is going on...
My figures may not be precise, but safe to say that each time you reference the items array using an index, it takes an increasing amount of time for larger lists. My suggestion effectively 'caches' the element you are working on and does improve performance measurably. My findings are based on having spent time looking into what was causing delays in writing a treeview out to disk.
How about pasting the code in your loop so we can take a look?
Have you considered using the list view in virtual mode?
I have found that once you get above ~1000 or so items in the list view, adding, clearing, etc. can be very time consuming. In many cases I have gone from a 10 sec load time down to 10-20 ms.
The nice part is, very little is required to run this way:
1.) The ListView.OwnerData must be set to true, and you must handle the OnData event (I will provide an example), and optionally the OnDataFind event.
1.) You must provide the data backing for the list view. In your case, you already create the TStringLists for loading and saving. So why not keep them around as the data backing?
2.) Instead of adding data to the list view (though items), you would add data to your stringlists, and then set the ListView.Item.Count to reflect the number of rows of data that you are backing. (ListView.Item.Count:=Stri
For example;
var
DataCols: Array [0..3] of StringList;
// OnData event for list view control
procedure TForm1.ListView1Data(Sende
begin
// Make sure Item is in range
if (Item.Index >= DataCols[0].Count) then Exit;
// Set the list item properties
Item.Caption:=DataCols[0][
Item.SubItems.Add(DataCols
Item.SubItems.Add(DataCols
Item.SubItems.Add(DataCols
end;
// OnDataFind event for list view control
procedure TForm1.ListViewDataFind(Se
StartIndex: Integer; Direction: TSearchDirection; Wrap: Boolean; var Index: Integer);
var
i: Integer;
Found: Boolean;
begin
i:=StartIndex;
if (Find = ifExactString) or (Find = ifPartialString) then
begin
repeat
if (i = DataCols[0].Count-1) then
if Wrap then i:= 0 else Exit;
Found:=Pos(UpperCase(FindS
Inc(i);
until Found or (i = StartIndex);
if Found then Index:=i-1;
end;
end;
-----
If you would like to see a more in-depth example, let me know.
Russell
Hi
I think this is the code that solve your problem best. So try this one.
//------------------------ -----
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, StrUtils;
type
TForm1 = class(TForm)
ListView1: TListView;
LoadFromFile: TButton;
SaveToFile: TButton;
procedure FormCreate(Sender: TObject);
procedure LoadFromFileClick(Sender: TObject);
procedure SaveToFileClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.LoadFromFileClick(S ender: TObject);
var
S, S1 : TStringList;
i, j : Integer;
LI : TListItem;
begin
S := TStringList.Create;
S1 := TStringList.Create;
S.LoadFromFile('Test2.txt' );
For i:=0 to S.Count-1 do
begin
S1.CommaText := S.Strings[i];
LI := ListView1.Items.Add;
for j:=0 to S1.Count-1 do if j=0 then LI.Caption := S1.Strings[j] else LI.SubItems.Add(S1.Strings [j]);
S1.Clear;
end;
S1.Free;
S.Free;
end;
procedure TForm1.SaveToFileClick(Sen der: TObject);
var
S : TStringList;
i : Integer;
begin
S := TStringList.Create;
for i:=0 to ListView1.Items.Count-1 do
begin
S.Add(ListView1.Items.Item [0].Captio n + ','+ListView1.Items.Item[0 ].SubItems .CommaText )
end;
S.SaveToFile('Test2.txt');
FreeAndNil(S);
end;
end.
//------------------------ -----
With Best Regards
Fareed.
I think this is the code that solve your problem best. So try this one.
//------------------------
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, StrUtils;
type
TForm1 = class(TForm)
ListView1: TListView;
LoadFromFile: TButton;
SaveToFile: TButton;
procedure FormCreate(Sender: TObject);
procedure LoadFromFileClick(Sender: TObject);
procedure SaveToFileClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.LoadFromFileClick(S
var
S, S1 : TStringList;
i, j : Integer;
LI : TListItem;
begin
S := TStringList.Create;
S1 := TStringList.Create;
S.LoadFromFile('Test2.txt'
For i:=0 to S.Count-1 do
begin
S1.CommaText := S.Strings[i];
LI := ListView1.Items.Add;
for j:=0 to S1.Count-1 do if j=0 then LI.Caption := S1.Strings[j] else LI.SubItems.Add(S1.Strings
S1.Clear;
end;
S1.Free;
S.Free;
end;
procedure TForm1.SaveToFileClick(Sen
var
S : TStringList;
i : Integer;
begin
S := TStringList.Create;
for i:=0 to ListView1.Items.Count-1 do
begin
S.Add(ListView1.Items.Item
end;
S.SaveToFile('Test2.txt');
FreeAndNil(S);
end;
end.
//------------------------
With Best Regards
Fareed.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
while i < ListView1.Items.count do begin
// lots of references to ListView1[i]
end;
try:
var
li: TListItem;
while i < ListView1.Items.count do begin
li := ListView1.Items[i];
// lots of references to li
end;
When I did this, I found that the manipulation processing speed was increased dramatically. Basically, it avoids the node list from being accessed (in what appears to be sequential order) each time you wish to process a node.
HTH.