Reputation: 897
How can I catch the event when an item is added to TListView?
I thought the OnInsert
event would do the job, according to the documentation. It even passes the actual TListItem
object to the handler:
OnInsert Occurs immediately after a new item is inserted into the list view.
Write an OnInsert event handler to respond when an item has just been added to the list. The Item parameter is the TListItem object that was added to the Items property
Here is my code:
procedure TForm1.Button1Click(Sender: TObject);
begin
with ListView1.Items.Add do
begin
Caption := 'foo';
SubItems.Add('bar');
end;
end;
procedure TForm1.TListView1Insert(Sender: TObject; Item: TListItem);
begin
//Item is empty
ShowMessage(Item.Caption);
end;
But surprisingly, the Item.Caption
is always empty. Seems nonsense to me.
EDIT:
Switching to Items.AddItem()
, as suggested, leads to another weird issue.
The OnInsert
event handler now works as expected, however TListView
does not display the TListItem.Caption
.
procedure TForm1.Button1Click(Sender: TObject);
begin
with ListView1.Items.Add do
begin
Caption := 'foo1';
SubItems.Add('bar1');
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
item: TListItem;
begin
item := TListItem.Create(ListView1.Items);
item.Caption := 'foo2';
item.Subitems.Add('bar2');
ListView1.Items.AddItem(item);
end;
procedure TForm1.ListView1Insert(Sender: TObject; Item: TListItem);
begin
//this now works as expected
ShowMessage(Item.Caption);
end;
Why is this?
Upvotes: 2
Views: 2667
Reputation: 596256
The TListView.OnInsert
event is indeed triggered when a new item is added to the ListView. However, the Item
is added to the ListView when TListView.Items.Add()
is called, not when Button1Click()
exits. The OnInsert
event handler is called (in response to a LVN_INSERTITEM
notification) while Add()
is still running. So, of course the Item
in the OnInsert
event handler will always be empty, as you haven't assigned any values to it yet.
Update: When a TListItem
is added to the ListView, the LVIF_TEXT
flag of the underlying LVITEM
is not enabled. To display the TListItem.Caption
and TListItem.SubItems
text, TListView
is designed to rely on ListView_SetItemText()
with the LPSTR_TEXTCALLBACK
flag instead:
This parameter can be LPSTR_TEXTCALLBACK to indicate a callback item for which the parent window stores the text. In this case, the list-view control sends the parent an
LVN_GETDISPINFO
notification code when it needs the text.
If you assign the TListItem.Caption
or TListItem.SubItems
property while the TListItem
is not actually in the ListView yet, the LPSTR_TEXTCALLBACK
flag will not get applied to those fields. LVN_GETDISPINFO
will not query the TListView
for the text of the 1st column without LPSTR_TEXTCALLBACK
(as column 0 has special meaning at the OS layer), but it does query for the text of the 2nd column (even if LPSTR_TEXTCALLBACK
is not applied to it). That is why your second example is missing the 'foo2'
caption text in the UI, but not the 'bar2'
text.
The actual 'foo2'
caption string is stored in the TListItem
object, which is why your ShowMessage()
is able to work.
So, if you create a new TListItem
and modify its Caption
before the item has been added to the ListView, you will have to call ListView_SetItemText()
manually to enable the LPSTR_TEXTCALLBACK
flag for the caption, eg:
uses
Commctrl;
procedure TForm1.Button2Click(Sender: TObject);
var
item: TListItem;
begin
item := TListItem.Create(ListView1.Items);
item.Caption := 'foo2';
item.Subitems.Add('bar2');
ListView1.Items.AddItem(item);
ListView_SetItemText(ListView1.Handle, item.Index, 0, LPSTR_TEXTCALLBACK);
end;
Or, reset the Caption
property value temporarily (the property setter checks for a duplicate string before calling ListView_SetItemText()
):
procedure TForm1.Button2Click(Sender: TObject);
var
item: TListItem;
begin
item := TListItem.Create(ListView1.Items);
item.Caption := 'foo2';
item.Subitems.Add('bar2');
ListView1.Items.AddItem(item);
item.Caption := '';
item.Caption := 'foo2';
end;
Just note that either way, the TListItem.Caption
text will not appear in the UI until after the OnInsert
event is called first, since it is triggered while AddItem()
is running.
I reproduced this in XE2. If the problem still happens in 10.2 Tokyo, I would suggest filing a bug report with Embarcadero. AddItem()
should probably be forcing LPSTR_TEXTCALLBACK
after insertion for any already-assigned string fields, or at least the Caption
, anyway.
Upvotes: 7