Virtual TListView Item->SubItems->Assign() during OnData triggers refresh and then never ending updates
Virtual TListView Item->SubItems->Assign() during OnData triggers refresh and hence never ending updates
使用 C++ Builder 2009 维护旧项目
在 TListView
实例 (ViewStyle = vsReport
) 中,设置为运行虚拟 (OwnerData = true
) 我想尝试尽可能提高速度。每一点点都有帮助。在 OnData
事件中,我注意到 Item->SubItems->Capacity
= 0 开始,随着子项目的添加,它每增加 4 个。我在文档中读到 Capacity
是只读的,但我想尽可能避免 TStrings
' 内部重新分配。因为我还需要进行缓存,所以我想我会使用 TStringList
作为已经增长到所需容量的缓存。我 assume TStrings
Assign()
然后会立即分配一个足够大的数组来存储所需数量的字符串 ?
Item->SubItems->Assign(Cache.SubItems) ;
虽然这有效,但我注意到这会触发 ListView 一次又一次地调用 OnData 并且......导致它永远不会停止。
通过这样做很容易修复:
for (int x = 0 ; x < Cache.SubItems->Count ; x++)
Item->SubItems->Add(Cache.SubItems->Strings[x]) ;
当然,重点是能够从一开始就告诉 SubItems
字符串的数量。
我意识到我可能 运行 反对旧的 VCL 问题?那早就解决了?或者我现在不明白这种行为有什么意义吗?
有没有办法 'enable' Capacity
接受输入?以便它为将要添加的字符串分配足够的 space ?
只要 ListView 需要给定列表项的数据,例如(但不限于)绘图操作,就会触发 TListView::OnData
事件。
注意当OnData
事件被触发时,TListItem::SubItems
已经预先Clear()
了。 TStringList::Clear()
将 Capacity
设置为 0,释放其当前 string
数组。这就是输入 OnData
处理程序时 Count
和 Capacity
始终为 0 的原因。
SubItems
属性 作为 TSubItems
对象实现,它派生自 TStringList
。 TStrings::Capacity
属性 setter 在 TStringList
中实现,并按照您的预期预分配 string
数组。但仅此而已——为数组分配 VCL 内存,仅此而已。在 Win32 API 层仍然存在更新 ListView 子项本身的方面,并且必须单独完成每个 string
被添加到 SubItems
.
当您的 OnData
处理程序调用 SubItems->Assign()
时,您正在调用 TStrings::Assign()
(因为 TStringList
和 TSubItems
不会覆盖它)。但是,TStrings::Assign()
不会 将 string
数组预分配到源 TStrings
对象的大小,正如人们所期望的那样(至少,不在 CB2009 中,我不知道现代版本是否有)。在内部,Assign()
仅调用 Clear()
,然后调用 TStrings::AddStrings()
(TStringList
和 TSubItems
都不会重写以预分配数组)。 AddStrings()
仅在循环中调用 TStrings::AddObject()
(TStringList
和 TSubItems
都覆盖)。
所有这些清除和添加逻辑都包含在一对 TStrings::(Begin|End)Update()
调用中。这一点很重要,因为 TSubItems
对更新计数器做出反应。当计数器降为 0 时,TSubItems
触发 TListView
进行一些内部更新,其中包括调用 Invalidate()
自身,这会触发整个重绘,从而触发一系列新的 OnData
需要重新绘制的列表项的事件。
另一方面,当您在自己的手动循环中调用 SubItems->Add()
并省略 (Begin|End)Update()
调用时,您将跳过整个 ListView 的重绘。 TSubItems
覆盖 TStrings::Add/Object()
以(除其他外)仅更新它链接到的特定 ListView 项目。它不会重新绘制整个 ListView。
因此,如果您真的想要,您应该能够在进入手动循环之前设置 Capacity
:
Item->SubItems->Capacity = Cache.SubItems->Count;
for (int x = 0; x < Cache.SubItems->Count; ++x)
Item->SubItems->Add(Cache.SubItems->Strings[x]);
在这种情况下,您可以使用 AddStrings()
而不是手动循环:
Item->SubItems->Capacity = Cache.SubItems->Count;
Item->SubItems->AddStrings(Cache.SubItems);
使用 C++ Builder 2009 维护旧项目
在 TListView
实例 (ViewStyle = vsReport
) 中,设置为运行虚拟 (OwnerData = true
) 我想尝试尽可能提高速度。每一点点都有帮助。在 OnData
事件中,我注意到 Item->SubItems->Capacity
= 0 开始,随着子项目的添加,它每增加 4 个。我在文档中读到 Capacity
是只读的,但我想尽可能避免 TStrings
' 内部重新分配。因为我还需要进行缓存,所以我想我会使用 TStringList
作为已经增长到所需容量的缓存。我 assume TStrings
Assign()
然后会立即分配一个足够大的数组来存储所需数量的字符串 ?
Item->SubItems->Assign(Cache.SubItems) ;
虽然这有效,但我注意到这会触发 ListView 一次又一次地调用 OnData 并且......导致它永远不会停止。
通过这样做很容易修复:
for (int x = 0 ; x < Cache.SubItems->Count ; x++)
Item->SubItems->Add(Cache.SubItems->Strings[x]) ;
当然,重点是能够从一开始就告诉 SubItems
字符串的数量。
我意识到我可能 运行 反对旧的 VCL 问题?那早就解决了?或者我现在不明白这种行为有什么意义吗?
有没有办法 'enable' Capacity
接受输入?以便它为将要添加的字符串分配足够的 space ?
只要 ListView 需要给定列表项的数据,例如(但不限于)绘图操作,就会触发 TListView::OnData
事件。
注意当OnData
事件被触发时,TListItem::SubItems
已经预先Clear()
了。 TStringList::Clear()
将 Capacity
设置为 0,释放其当前 string
数组。这就是输入 OnData
处理程序时 Count
和 Capacity
始终为 0 的原因。
SubItems
属性 作为 TSubItems
对象实现,它派生自 TStringList
。 TStrings::Capacity
属性 setter 在 TStringList
中实现,并按照您的预期预分配 string
数组。但仅此而已——为数组分配 VCL 内存,仅此而已。在 Win32 API 层仍然存在更新 ListView 子项本身的方面,并且必须单独完成每个 string
被添加到 SubItems
.
当您的 OnData
处理程序调用 SubItems->Assign()
时,您正在调用 TStrings::Assign()
(因为 TStringList
和 TSubItems
不会覆盖它)。但是,TStrings::Assign()
不会 将 string
数组预分配到源 TStrings
对象的大小,正如人们所期望的那样(至少,不在 CB2009 中,我不知道现代版本是否有)。在内部,Assign()
仅调用 Clear()
,然后调用 TStrings::AddStrings()
(TStringList
和 TSubItems
都不会重写以预分配数组)。 AddStrings()
仅在循环中调用 TStrings::AddObject()
(TStringList
和 TSubItems
都覆盖)。
所有这些清除和添加逻辑都包含在一对 TStrings::(Begin|End)Update()
调用中。这一点很重要,因为 TSubItems
对更新计数器做出反应。当计数器降为 0 时,TSubItems
触发 TListView
进行一些内部更新,其中包括调用 Invalidate()
自身,这会触发整个重绘,从而触发一系列新的 OnData
需要重新绘制的列表项的事件。
另一方面,当您在自己的手动循环中调用 SubItems->Add()
并省略 (Begin|End)Update()
调用时,您将跳过整个 ListView 的重绘。 TSubItems
覆盖 TStrings::Add/Object()
以(除其他外)仅更新它链接到的特定 ListView 项目。它不会重新绘制整个 ListView。
因此,如果您真的想要,您应该能够在进入手动循环之前设置 Capacity
:
Item->SubItems->Capacity = Cache.SubItems->Count;
for (int x = 0; x < Cache.SubItems->Count; ++x)
Item->SubItems->Add(Cache.SubItems->Strings[x]);
在这种情况下,您可以使用 AddStrings()
而不是手动循环:
Item->SubItems->Capacity = Cache.SubItems->Count;
Item->SubItems->AddStrings(Cache.SubItems);