带有很多控件的假滚动容器 [英] Fake-scrolling containers with very many controls
问题描述
我正在尝试优化 FlowLayoutPanel 的填充和滚动,但我之前遇到过类似控件的问题,如果它们内部有太多控件,则容器需要很长时间才能填充并准备好使用(并且滚动条越来越短,您可能对此很熟悉).
I'm trying to optimize populating and scrolling a FlowLayoutPanel, but I've had issues with similar controls before, where if they have too many controls inside, it takes a really long while for the container to populate and get ready for use (and the scroller gets shorter and shorter, you might be familiar with that).
我已经读到,您可以仅使用容器矩形可见边界内的控件池,并通过用相应的内容重新填充它们来模拟滚动,就好像它们没有这种优化一样.所以你像往常一样滚动,但人口几乎不需要那么长时间.但是我如何在一般情况下实现它?
I've read that you can use a pool of just the controls within visible boundaries of the container rectangle and simulate scrolling by repopulating them with corresponding contents, as if they would be without this optimization. So you scroll as usual but the population doesn't take nearly as long. But how do I implement that for a general case?
我正在使用自定义控件来填充 FlowLayoutPanel 容器,因此我正在寻找一种足够通用的解决方案,它可以同时应用于我的控件和标准 .Net 控件.
I'm using custom controls to populate the FlowLayoutPanel container, so I'm looking for a generic enough solution that can be applied to both my control and the standard .Net controls.
推荐答案
显示和滚动性能是尝试虚拟分页的好理由,尽管可以通过替换 控件来克服这些问题.使用
..Controls.AddRange
调用和双缓冲容器添加
Display and scrolling performance are good reasons to try a virtual paging, although they can be overcome by replacing the Controls.Add
with a Controls.AddRange
call and a double-buffered container..
..但还有另一个:任何 Winforms 控件的显示尺寸限制为 32k 像素.即使您将其放大,也不会显示超出此限制的内容.
..but there is another: Any Winforms control is limited to 32k pixels in its display dimensions. Even if you make it larger nothing will be displayed beyond this limit.
以下是实现虚拟分页时要做的事情的快速列表:
Here is a quick list of things to do, when implementing virtual paging:
- 使用双缓冲
FlowLayoutPanel
子类来简化布局并使其无闪烁. - 关闭
AutoSize
和AutoScroll
- 在 FLP 的右侧添加一个
VScrollBar
并保持其Height
与 FLP 的相同 - 计算
UserControl
的Height
(加上Margins
).我假设您将控件添加到 UC 中,以简化操作. - 计算分页数
- 创建一个
List
UCs - 现在创建您的 UC,但仅将它们添加到列表
theUCs
- 编写一个
scrollTo(int ucIndex)
函数,该函数清除 FLP 的控件并从列表中添加正确的范围. - 代码
KeyPreview
用于 FLP 以允许使用键盘滚动.
- Use a double-buffered
FlowLayoutPanel
subclass to simplify the layout and make it flicker-free. - Turn off
AutoSize
andAutoScroll
- Add a
VScrollBar
to the right of the FLP and keep itsHeight
the same as the FLP's - Calculate the
Height
(plusMargins
) of yourUserControl
. I assume you add your control wrapped up in a UC, to make things easier. - Calculate the paging numbers
- Create a
List<yourUserControlClass> theUCs
- Now create your UCs but add them only to the list
theUCs
- Code a
scrollTo(int ucIndex)
function, which clears the FLP's controls and adds the right range from the list. - code
KeyPreview
for the FLP to allow scrolling with the keyboard.
为VScrollBar
的属性设置正确的值,即Minimum、Maximum、Value、SmallChange、LargeChange
有点棘手,必须设置页面大小每当 FLP 被调整大小或元素添加到列表中或删除时.
Setting the right values for the VScrollBar
's properties, i.e. Minimum, Maximum, Value, SmallChange, LargeChange
is a little tricky and setting the page size must be done whenever the FLP is resized or elements are added to or removed from the list.
在我的测试中,设置和滚动结果都是即时的.从顶部只能看到完整 UC,这对我来说很好.我在 Panel
、Label
和 CheckedListBox
中添加了 1000 个带有位图的 UC.
In my test the setting up and the scrolling results were instantaneous. Only complete UCs are visible from the top, which is fine with me. I have added 1000 UCs with a bitmap in a Panel
, a Label
and a CheckedListBox
.
这里是我如何计算 Maximum
的设置:
Here is how I calculate the setting for 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
值的奇怪偏移的一种解决方法.
The extra 9
is a workaround for the quirky offset of a ScrollBar
's theoretical and actual Maximum
values.
在 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
子类:
For even smoother scrolling use a DoubleBuffered
subclass:
class DrawFLP : FlowLayoutPanel
{
public DrawFLP() { DoubleBuffered = true; }
}
这可能有点粗糙,但我希望它能让你走上正轨.
This is probably a bit rough at the edges, but I hope it'll get you on the right track.
这篇关于带有很多控件的假滚动容器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!