使用临时 TListItems 填充 TListView

populating TListView by using temp TListItems

我想在后台刷新列表视图列表,为此我创建了一个临时 TListItems,但我无法将它分配给我的列表视图。如果我创建 TListItems 和 TListItem,则会发生访问冲突错误;

 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;

如果我在创建 TListItems 时使用 ListView1 作为所有者,则不会出现新行;

 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;

所以我想在后台准备一个新列表并将其分配给列表视图,我该怎么做?

注意:准备列表需要很长时间,这就是我在后台准备的原因。 (我正在用线程填充列表并使用 TRTLCriticalSection 保护它)

TListItemsTListItemTListView 密切相关,因此不能像您尝试的那样以独立方式使用。 TListItemsTListItem 都委托给 TListView 来处理他们的工作。

您在第一个示例中获得了 AV,因为没有分配 TListView 来处理该工作。

在你的第二个例子中,新的列表项没有正确显示,因为当你分配它的 Caption 时它还没有被添加到 ListView,所以 属性 setter更新。 AddItem() 不会将预先存在的 属性 值应用到新插入的列表项。必须使用单独的 属性 setter。

你必须首先使用TListItems.Add()方法而不是直接调用AddItem(),然后你可以根据需要修改新的TListItem,例如:

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;

但是,正如 David H 在评论中所说,在虚拟模式下使用 ListView(将 OwnerData 属性 设置为 true 并使用 TListView.OnData... 事件)是处理这种情况的更好方法:

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;