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?
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.
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..
..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 subclass to simplify the layout and make it flicker-free.AutoSize and AutoScrollVScrollBar to the right of the FLP and keep its Height the same as the FLP'sHeight (plus Margins) of your UserControl. I assume you add your control wrapped up in a UC, to make things easier.List<yourUserControlClass> theUCstheUCsscrollTo(int ucIndex) function, which clears the FLP's controls and adds the right range from the list.KeyPreview for the FLP to allow scrolling with the keyboard.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.
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.
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;
The extra 9 is a workaround for the quirky offset of a ScrollBar's theoretical and actual Maximum values.
In the ValueChanged event I wrote:
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();
}
This scrolls to a certain item:
void scrollTo(int item)
{
int pageSize = flowLayoutPanel1.ClientSize.Height / theUCs.First().Height;
int p = item / pageSize + 1;
vScrollBar1.Value = p;
}
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.

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With