2

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;

image

Why is this?

4

1 回答 1

7

TListView.OnInsert当新项目添加到 ListView 时,确实会触发该事件。但是,在调用Item时添加到 ListView TListView.Items.Add(),而不是在Button1Click()退出时。在仍在运行时OnInsert调用事件处理程序(以响应LVN_INSERTITEM通知) 。Add()因此,当然,事件处理程序中的IteminOnInsert将始终为空,因为您尚未为其分配任何值。


更新:当将 aTListItem添加到 ListView 时,未启用LVIF_TEXT底层的标志。和文本LVITEM的显示,被设计为依赖于标志而不是:TListItem.CaptionTListItem.SubItemsTListViewListView_SetItemText()LPSTR_TEXTCALLBACK

该参数可以是LPSTR_TEXTCALLBACK,表示父窗口存储文本的回调项。LVN_GETDISPINFO在这种情况下,列表视图控件在需要文本时向父级发送通知代码。

如果您分配TListItem.CaptionorTListItem.SubItems属性,而TListItem实际上尚未在 ListView 中,则该LPSTR_TEXTCALLBACK标志将不会应用于这些字段。 LVN_GETDISPINFO不会在没有的情况下查询TListView第一列的文本LPSTR_TEXTCALLBACK(因为列 0 在操作系统层具有特殊含义),但它会查询第二列的文本(即使LPSTR_TEXTCALLBACK未应用于它)。这就是为什么您的第二个示例缺少'foo2'UI 中的标题文本,而不是'bar2'文本。

实际的'foo2'标题字符串存储在TListItem对象中,这就是您ShowMessage()能够工作的原因。

因此,如果您在将项目添加到 ListView 之前创建一个新项目TListItem并对其进行修改,则必须手动调用以启用标题的标志,例如:CaptionListView_SetItemText()LPSTR_TEXTCALLBACK

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; 

或者,临时重置Caption属性值(属性设置器在调用之前检查重复的字符串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; 

请注意,无论哪种方式,TListItem.Caption文本都不会出现在 UI 中,直到第OnInsert一次调用事件之后,因为它是在AddItem()运行时触发的。

我在 XE2 中复制了这个。如果问题在 10.2 Tokyo 中仍然存在,我建议向 Embarcadero提交错误报告AddItem()应该LPSTR_TEXTCALLBACK在插入任何已经分配的字符串字段之后强制,或者至少应该强制Caption

于 2017-08-21T22:53:16.467 回答