带有很多控件的假滚动容器

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 子类来简化布局并使其无闪烁。 关闭AutoSizeAutoScroll 在 FLP 的右侧添加 VScrollBar 并保持其 Height 与 FLP 的相同 计算UserControlHeight(加上Margins)。我假设您将控件添加到 UC 中,以使事情变得更容易。 计算寻呼号码 创建一个List<yourUserControlClass> theUCs 现在创建您的 UC,但仅将它们添加到列表 theUCs 编写scrollTo(int ucIndex) 函数,该函数清除 FLP 的控件并从列表中添加正确的范围。 为 FLP 编码 KeyPreview 以允许使用键盘滚动。

VScrollBar 的属性设置正确的值,即Minimum, Maximum, Value, SmallChange, LargeChange 有点棘手,并且必须在FLP 调整大小 或元素 时设置页面大小已添加从列表中删除

在我的测试中,设置和滚动结果是即时的。从顶部只能看到 完整 UC,这对我来说很好。我在PanelLabelCheckedListBox 中添加了 1000 个带有位图的 UC。

这是我如何计算Maximum 的设置:

float pageSize =  flowLayoutPanel2.ClientSize.Height / 
                  (uc1.Height + uc1.Margin.Top + uc1.Margin.Bottom);
vScrollBar1.Maximum = (int)( 1f * theUCs.Count / (pageSize)) + 9;

额外的9ScrollBar 的理论值和实际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 滚动动作控件

如何在 Flutter 中滚动带有粘性标题的堆叠容器?

如何给Winform 的Panel控件添加滚动条

如何使用 swift 创建带有页面控件的滚动视图? [关闭]

如何使用带有垂直滚动的转发器控件修复表头?

带有可滚动内容的容器完全静止高度(Tailwind)