【问题标题】:populating TListView by using temp TListItems使用临时 TListItems 填充 TListView
【发布时间】:2018-07-11 16:17:41
【问题描述】:

我想在后台刷新列表视图列表,为此我创建了一个临时 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;

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

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

【问题讨论】:

  • TListView 包装了 Windows 的列表视图。这些项目通过 Windows API 进行管理。您不能以您想要的方式创建TListItems 的实例。您必须将自己的对象列表保存在某个地方。
  • 您可以简单地创建一个临时 TListView 而无需将其放置在表单上,​​向其中添加项目,然后将其项目重新分配给您的原始 TListView。或者,您也可以尝试不使用原生 Windows 小部件的出色 TVirtualTree。
  • 使用虚拟列表视图。您可以使用本机 VCL 控件执行此操作。当您说背景时,您的意思是线程。你知道vcl线程规则吗?
  • 谢谢大家。我将使用@DavidHeffernan 推荐的虚拟列表视图。还有大卫,我在 vcl 线程之前体验过,再次感谢你。

标签: delphi delphi-xe2


【解决方案1】:

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

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

在您的第二个示例中,新列表项未正确显示,因为在您分配其 Caption 时它尚未添加到 ListView,因此属性设置器没有任何内容需要更新。并且AddItem() 不会将预先存在的属性值应用于新插入的列表项。必须使用单独的属性设置器来代替。

您必须先使用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 在 cmets 中所说,在虚拟模式下使用 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;

【讨论】:

  • 谢谢雷米。我将使用虚拟列表视图并通过在工作线程中使用“同步”来填充它。
猜你喜欢
  • 2023-01-01
  • 2012-08-04
  • 2013-06-07
  • 2017-01-11
  • 2018-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多