TListView:添加项目?

TListView: on item added?

当一个项目被添加到 TListView 时,我如何捕捉事件?

根据文档,我认为 OnInsert 事件可以完成这项工作。它甚至将实际的 TListItem 对象传递给处理程序:

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

这是我的代码:

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;

但令人惊讶的是,Item.Caption 总是空的。对我来说似乎是胡说八道。

编辑:

按照建议切换到 Items.AddItem(),会导致另一个奇怪的问题。 OnInsert 事件处理程序现在按预期工作,但是 TListView 不显示 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;

这是为什么?

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


Update:当一个TListItem添加到ListView时,底层LVITEM is not enabled. To display the TListItem.Caption and TListItem.SubItems text, TListView is designed to rely on ListView_SetItemText()LVIF_TEXT标志与LPSTR_TEXTCALLBACK 标志改为:

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.

如果您分配 TListItem.CaptionTListItem.SubItems 属性 而 TListItem 实际上尚未在 ListView 中,则 LPSTR_TEXTCALLBACK 标志将不会获得应用于那些领域。 LVN_GETDISPINFO 不会在 TListView 中查询没有 LPSTR_TEXTCALLBACK 的第 1 列的文本(因为第 0 列在 OS 层具有特殊含义),但它会查询第二列的文本(即使未应用 LPSTR_TEXTCALLBACK)。这就是为什么您的第二个示例缺少 UI 中的 'foo2' 标题文本,而不是 'bar2' 文本。

实际的 'foo2' 字幕字符串存储在 TListItem object 中,这就是为什么您的 ShowMessage() 能够工作的原因。

因此,如果您创建一个新的 TListItem 并在项目添加到 ListView 之前修改其 Caption,您将必须手动调用 ListView_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 属性 值(属性 setter 在调用 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 事件之后,因为它是在 [=51= 时触发的] 是 运行.

我在 XE2 中复制了这个。如果问题仍然发生在 10.2 Tokyo 中,我建议使用 Embarcadero filing a bug reportAddItem() 应该在插入任何 already-assigned 字符串字段后强制 LPSTR_TEXTCALLBACK,或者至少 Caption,无论如何。