I had a brainwave for another solution which I'm not quite sure is appropriate. I would really appreciate any feedback on this.
Two rationales led to this solution:
- Firstly I wanted the flexibility of creating rows like any other control.
- Secondly the lists that would use this approach only intend to display brief chunks of data, never more than say 20 items - for anything larger, ListViews are used.
So anyway, what I decided to do was cache a set number of the Panels (I've referred to the custom controls or rows as Panels throughout the code) and to build up this cache as the control is created. When populating the control with BusinessObjects, the existing cached Panels are displayed with their bound BusinessObject. You can see how this works exactly from the code below, so there is no need for a in-depth description.
The fact of the matter is that I've managed to reduce the data population time (after the initial cache setup of around 180ms for 10 Panels) from 500ms to 150ms for the list shown in the image above.
private int cacheSize = 10;
private List<P> cachedPanels = new List<P>(10);
private void InitItems()
{
this.contentPanel.SuspendLayout();
// Create the cached panels from the default cache value.
for (int i = 0; i < cacheSize; i++)
cachedPanels.Add(new P() { Margin = new Padding(0), Visible = false });
this.contentPanel.Controls.AddRange(cachedPanels.ToArray());
this.contentPanel.ResumeLayout(true);
}
private void PopulateListFromCache()
{
this.contentPanel.SuspendLayout();
// Iterate against both BusinessObjects and Panels to ensure that nothing is missed, for
// instance, where there are too many panels, the rest are hidden, and too many Business
// Objects, then more Panels are created.
for (int i = 0; i < this.businessObjects.Count || i < this.cachedPanels.Count; i++)
{
if (i >= this.cachedPanels.Count)
{
// Here, we have more BusinessObjects than Panels, thus we must create
// and assign a new panel.
this.cachedPanels.Add(new P() { Margin = new Padding(0) });
this.cachedPanels[i].Item = this.businessObjects[i];
this.contentPanel.Controls.Add(this.cachedPanels[i]);
}
else if (i >= this.businessObjects.Count)
{
// Here, we still have Panels cached but have run out of BusinessObjects,
// let's just hide them and clear their bindings.
this.cachedPanels[i].Item = default(T);
this.cachedPanels[i].Visible = false;
}
else
{
// Here, we have both BusinessObjects and Panels to put them in, so just
// update the binding and ensure the Panel is visible.
this.cachedPanels[i].Item = this.businessObjects[i];
this.cachedPanels[i].Visible = true;
}
}
this.contentPanel.ResumeLayout(true);
}
Obviously, more optimizations can be made, such as un-caching Panels after a certain amount of time of not being used etc. Also, I'm not entirely sure if keeping these controls - which are rather simple - in a cache will affect memory usage much.
If anyone can think of any other pointers then please, be my guest. Oh, and if you got this far, then thank you for reading this.