TListView:是否已添加项?

TListView:是否已添加项?,listview,delphi,delphi-7,tlistview,Listview,Delphi,Delphi 7,Tlistview,将项目添加到TListView时,如何捕获事件 根据文档,我认为OnInsert事件可以完成这项工作。它甚至将实际的TListItem对象传递给处理程序: 在将新项目插入列表后立即执行OnInsert 视图 编写一个OnInsert事件处理程序,以便在项目刚刚启动时进行响应 添加到列表中项参数是已创建的TListItem对象 添加到项目属性中 这是我的密码: procedure TForm1.Button1Click(Sender: TObject); begin with ListView

将项目添加到TListView时,如何捕获事件

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

在将新项目插入列表后立即执行OnInsert 视图

编写一个OnInsert事件处理程序,以便在项目刚刚启动时进行响应 添加到列表中项参数是已创建的TListItem对象 添加到项目属性中

这是我的密码:

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;
但令人惊讶的是,
项标题总是空的。对我来说似乎是胡说八道

编辑:

按照建议切换到
项。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;


这是为什么?

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


更新:将
t列表项
添加到列表视图时,基础的
LVIF_TEXT
标志未启用。要显示
TListItem.Caption
TListItem.SubItems
text,
TListView
设计为依赖
LPSTR\u TEXTCALLBACK
标志:

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

如果指定
TListItem.Caption
TListItem.SubItems
属性,而
TListItem
实际上还不在列表视图中,则
LPSTR\u TEXTCALLBACK
标志将不会应用于这些字段
LVN_GETDISPINFO
不会在没有
LPSTR_TEXTCALLBACK
的情况下查询
TListView
第一列的文本(因为列0在OS层具有特殊含义),但它会查询第二列的文本(即使
LPSTR_TEXTCALLBACK
未应用于它)。这就是为什么您的第二个示例在UI中缺少
'foo2'
标题文本,而不是
'bar2'
文本的原因

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

因此,如果在将项目添加到ListView之前创建一个新的
TListItem
并修改其
标题
,则必须手动调用
ListView\u SetItemText()
,以启用标题的
LPSTR\u 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; 
或者,临时重置
标题
属性值(属性设置程序在调用
ListView\u SetItemText()
之前检查重复的字符串):

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


我在XE2中复制了这个。如果问题仍然发生在10.2东京,我建议与Embarcadero
AddItem()
在插入任何已分配的字符串字段后,或者至少在插入
标题后,可能会强制执行
LPSTR\u TEXTCALLBACK

它应该是ListView1,而不是TListView1。添加项目时也要使用BeginUpdate和EndUpdate过程。请注意,调用
(Begin | End)Update()
只会影响ListView的绘制(以避免在对项目进行更改时重新绘制),而不会影响其效果events@RemyLebeau是的,当然!我建议使用它们,因为当我处理要编辑的项目时,我总是这样做(特别是如果项目很多的话)。我想它们应该在任何情况下使用——你不能在另一个线程中添加项目。VCL访问需要在主线程中。无论您试图做什么,GUI控件事件都是错误的解决方案。正确。十足的下铺。我担心Zarko写的绝大多数东西都有缺陷。我不会相信你读到的关于他的任何消息。无论如何,我强烈地感到这个问题的整个前提是错误的。您只希望响应事件,因为您在错误的位置添加了项目。如果将与VCL一起工作的代码移动到主线程中(必须这样做),则不需要响应事件。您可以在添加项目的同时执行该操作。那么,我应该如何访问新添加的项目的值?设置一个项目并使用AddItem。不要使用
ListView.Items.add()
来创建一个新的
TListItem
,该项目将传递给
TListView.Items.AddItems()
。改用
TListItem.Create(ListView.Items)
。或者更好:
TListViewAccess(ListView).CreateListItem()
(其中
TListViewAccess
类型TListViewAccess=class(TListView)end;
,因为
CreateListItem()
被声明为
受保护的
)。使用
CreateListItem()
允许用户通过
TListView.OnCreateItemClass
事件,或者通过重写
CreateListItem()
本身,自定义所使用的item类。@RemyLebeau这导致了另一个问题,因此我扩展了这个问题
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;