带有很多控件的假滚动容器
Posted
技术标签:
【中文标题】带有很多控件的假滚动容器【英文标题】:Fake-scrolling containers with very many controls 【发布时间】:2021-11-14 17:31:28 【问题描述】:我正在尝试优化 FlowLayoutPanel 的填充和滚动,但我之前遇到过类似控件的问题,如果它们内部的控件太多,则容器需要很长时间才能填充并准备好使用(并且滚动条越来越短,您可能对此很熟悉)。
我了解到您可以在容器矩形的可见边界内使用控件池,并通过使用相应的内容重新填充它们来模拟滚动,就好像它们没有这种优化一样。所以你像往常一样滚动,但人口并不需要那么长时间。但是对于一般情况,我该如何实现呢?
我正在使用自定义控件来填充 FlowLayoutPanel 容器,因此我正在寻找一个足够通用的解决方案,可以同时应用于我的控件和标准 .Net 控件。
【问题讨论】:
将SuspendLayout()
和ResumeLayout()
放在设置循环之外,看看是否有足够的帮助。还请告诉我们您最终添加的控件数量!至于虚拟滚动,除了分页之外,您需要非常清楚您想要使用哪些 ui 功能:小步滚动,在所有行上快速拖动电梯?..
不,这没有用,我知道这个技巧。恢复后它仍然要做布局。我正在使用数百个控件。我所追求的是完整的滚动条功能。我曾经为一个游戏开发过类似的解决方案,但它没有使用任何winforms,并且整个窗口管理逻辑是自定义的,因此体验并不能很好地转化,但至少我知道它应该是可能的。
我的同情。我已经在 C、C++ 和 C#(以及 VB 和 CLisp)中使用 differential execution 完成了这项工作。
【参考方案1】:
显示和滚动性能是尝试虚拟分页的好理由,尽管可以通过将 Controls.Add
替换为 Controls.AddRange
调用和双缓冲容器来克服它们。. p>
..但还有另一个:任何 Winforms 控件的显示尺寸限制为 32k 像素。即使您将其放大,也不会显示超出此限制的内容。
以下是实现虚拟分页时要做的事情的快速列表:
使用双缓冲FlowLayoutPanel
子类来简化布局并使其无闪烁。
关闭AutoSize
和AutoScroll
在 FLP 的右侧添加 VScrollBar
并保持其 Height
与 FLP 的相同
计算UserControl
的Height
(加上Margins
)。我假设您将控件添加到 UC 中,以使事情变得更容易。
计算寻呼号码
创建一个List<yourUserControlClass> theUCs
现在创建您的 UC,但仅将它们添加到列表 theUCs
编写scrollTo(int ucIndex)
函数,该函数清除 FLP 的控件并从列表中添加正确的范围。
为 FLP 编码 KeyPreview
以允许使用键盘滚动。
为VScrollBar
的属性设置正确的值,即Minimum, Maximum, Value, SmallChange, LargeChange
有点棘手,并且必须在FLP 调整大小 或元素 时设置页面大小已添加或从列表中删除。
在我的测试中,设置和滚动结果是即时的。从顶部只能看到 完整 UC,这对我来说很好。我在Panel
、Label
和CheckedListBox
中添加了 1000 个带有位图的 UC。
这是我如何计算Maximum
的设置:
float pageSize = flowLayoutPanel2.ClientSize.Height /
(uc1.Height + uc1.Margin.Top + uc1.Margin.Bottom);
vScrollBar1.Maximum = (int)( 1f * theUCs.Count / (pageSize)) + 9;
额外的9
是ScrollBar
的理论值和实际Maximum
值的古怪偏移的解决方法。
在ValueChanged
事件中我写道:
private void vScrollBar1_ValueChanged(object sender, EventArgs e)
int pageSize = flowLayoutPanel1.ClientSize.Height / theUCs.First().Height;
int v = Math.Min(theUCs.Count, vScrollBar1.Value);
flowLayoutPanel1.SuspendLayout();
flowLayoutPanel1.Controls.Clear();
flowLayoutPanel1.Controls.AddRange(theUCs.Skip( (v- 1) * pageSize)
.Take(pageSize + 1).ToArray());
flowLayoutPanel1.ResumeLayout();
这会滚动到某个项目:
void scrollTo(int item)
int pageSize = flowLayoutPanel1.ClientSize.Height / theUCs.First().Height;
int p = item / pageSize + 1;
vScrollBar1.Value = p;
为了更流畅的滚动,使用DoubleBuffered
子类:
class DrawFLP : FlowLayoutPanel
public DrawFLP() DoubleBuffered = true;
这可能有点粗略,但我希望它能让你走上正轨。
【讨论】:
我一直在玩 SuspendLayout,但它似乎没有任何作用。不初始化控件,不将它们添加到容器中,但仍然需要相同的时间。然后我开始分析,结果发现是调试构建类型搞砸了事情的速度。需要注意的事情。以上是关于带有很多控件的假滚动容器的主要内容,如果未能解决你的问题,请参考以下文章
带有 pagingEnabled 的 UICollectionView 滚动动作控件