In Silverlight and WPF, dynamically creating large numbers of child elements in a Panel (such as Canvas) can hit performance pretty hard. First-time element initialization can be particularly heavy, so it’s worth minimizing that wherever possible.
Here’s a little helper class that will recycle a Panel’s child elements (comments removed for brevity). Just tell it the number of elements you want to have / keep, and it’ll return them through an enumerator. Internally it’ll keep track of “unused” elements it’s removed from the Panel and will add them again later if needed.
public class ElementRecycler<T> where T : UIElement, new() { private Panel panel; private Stack<T> unused = new Stack<T>(); public ElementRecycler( Panel panel ) { this.panel = panel; } public IEnumerable<T> RecycleChildren( int count ) { return RecycleChildren( panel, count, unused ).ToArray(); } public static IEnumerable<T> RecycleChildren( Panel panel, int count, Stack<T> unused ) { var elementEnum = panel.Children.OfType<T>().ToArray().AsEnumerable().GetEnumerator(); while ( count-- > 0 ) { if ( elementEnum.MoveNext() ) { yield return elementEnum.Current; } else if ( unused.Count > 0 ) { var recycled = unused.Pop(); panel.Children.Add( recycled ); yield return recycled; } else { var element = new T(); panel.Children.Add( element ); yield return element; } } while ( elementEnum.MoveNext() ) { panel.Children.Remove( elementEnum.Current ); unused.Push( elementEnum.Current ); } } }
Here’s a short example of how it could be used:
var ticks = Axis.Ticks.ToArray(); var tickElementEnum = tickRecycler.RecycleChildren( ticks.Length ).GetEnumerator(); foreach ( var tick in Axis.Ticks ) { if ( tickElementEnum.MoveNext() ) { var tickElement = tickElementEnum.Current; var points = new PointCollection(); points.Add( tickTransform.Transform( new Point( tick.Position, 0 ) ) ); points.Add( tickTransform.Transform( new Point( tick.Position, tick.Type == TickType.Major ? 1 : 0.5 ) ) ); tickElement.Stroke = tickBrush; tickElement.Points = points; } }
To see an example of this class at work, take a look here. ElementRecycler is used for the axis ticks and labels (drag the retirement age to the right to watch the scale change; note it’s still a little buggy and eats memory, but will be fixed soon 🙂 ).
I’m having a bit of trouble opening the sample located at http://localhost:59979/CalcEnginePlanner… 🙂
But a good idea
[…] more here […]
Oops, fixed my lousy broken link 🙂
[…] ElementRecycler for Silverlight and WPF (Chris Cavanagh) […]
[…] ElementRecycler for Silverlight and WPF […]