Reputation: 1115
I want to refresh the list of listview in background, to do that i created a temporary TListItems, but i can not assign it to my listview. If i create TListItems and TListItem an access violation error occurs;
var
lis:TListItems;
li:TListItem;
begin
lis := TListItems.Create(nil);
try
li := TListItem.Create(nil);
li.Caption := 'test'; // at this line av occurs
lis.AddItem(li);
ListView1.Items.BeginUpdate;
try
ListView1.Items.Assign(lis);
finally
ListView1.Items.EndUpdate;
end;
finally
lis.Destroy();
end;
If i use ListView1 as Owner while creating TListItems new line does not appear;
var
lis:TListItems;
li:TListItem;
begin
lis := TListItems.Create(ListView1);
try
li := TListItem.Create(lis);
li.Caption := 'test';
lis.AddItem(li);
ListView1.Items.BeginUpdate;
try
ListView1.Items.Assign(lis);
finally
ListView1.Items.EndUpdate;
end;
finally
lis.Destroy();
end;
So i'want to prepare a new list in background and assign it to listview, how can i do it?
Note: preparing the list takes long time, this is why i'm preparing it in background. (i'm filling the list by a thread and protect it by using TRTLCriticalSection)
Upvotes: 1
Views: 1183
Reputation: 595367
TListItems
and TListItem
are very intimately tied to TListView
and thus cannot be used in a standalone manner like you are attempting to do. TListItems
and TListItem
both delegate to TListView
to handle their work.
You get an AV in your first example because there is no TListView
assigned to handle the work.
In your second example, the new list item does not appear correctly because it has not been added to the ListView yet when you assign its Caption
, so there is nothing for the property setter to update. And AddItem()
doesn't apply pre-existing property values to a newly inserted list item. The individual property setters must be used for that instead.
You must use the TListItems.Add()
method FIRST instead of calling AddItem()
directly, and THEN you can modify the new TListItem
as needed, eg:
var
NewList: TStringList;
Lock: TCriticalSection;
...
// in a worker thread...
Lock.Enter;
try
NewList.Clear;
NewList.Add('test');
...
finally
Lock.Leave;
end;
// signal main UI thread that a new list is ready ...
...
// in the main UI thread when the signal is received...
var
li: TListItem;
i: Integer;
begin
Lock.Enter;
try
ListView1.Items.BeginUpdate;
try
ListView1.Items.Clear;
for i := 0 to NewList.Count-1 do
begin
li := ListView1.Items.Add;
li.Caption := NewList[i];
...
end;
finally
ListView1.Items.EndUpdate;
end;
finally
Lock.Leave;
end;
end;
But, as David H said in comments, using the ListView in virtual mode (set the OwnerData
property to true
and use the TListView.OnData...
events) is a better way to handle this situation:
var
NewList: TStringList;
Lock: TCriticalSection;
...
// in a worker thread...
Lock.Enter;
try
NewList.Clear;
NewList.Add('test');
...
finally
Lock.Leave;
end;
// signal main UI thread that a new list is ready ...
...
// in the main UI thread...
private
MyListItems: TStringList; // or whatever you want to use to store your item data
...
begin
Lock.Enter;
try
MyListItems.Assign(NewList);
finally
Lock.Leave;
end;
ListView1.Items.Count := MyListItems.Count;
end;
procedure TMyForm.ListView1Data(Sender: TObject; Item: TListItem);
begin
Item.Caption := MyListItems[Item.Index];
...
end;
Upvotes: 2