title: Is 141 the maximum number of WPF panel items that can be animated simultaneously?
body: <p>I have designed several animated <code>Panel</code>s in C# that all inherit from one base class that performs the actual animations on the <code>Panel.Children</code>. Up until recently, they've all worked perfectly... but recently, I was using one with a collection that had a large number of items in it and I started getting some strange visual errors.</p>
  
  <p>When the application first loads the items, the first x number of items display correctly and then the remainder are all displayed in the first position (on top of each other). If I resize the window or select an item, they all rearrange themselves to display correctly. (Using the example code below, you will have to resize the window to see them correct themselves.)</p>
  
  <p>After <strong>a lot</strong> of head scratching and debugging, I discovered that the correct values were still getting through to the animation code for each item, but the last x items were simply not being animated. Each item definitely passes through the animation code, but the last x items stay in the position set in the animated <code>Panel.ArrangeOverride</code> method.</p>
  
  <p>In order to try to locate a problem in my code, I created a new <code>UserControl</code> and animated <code>Panel</code> class with the minimum code required to recreate the problem. Having done so, I was surprised that the problem was still reproducible. With 141 items in my example code, there was no display problem, but with any more, the problem appears.</p>
  
  <p>Please excuse the long lines of code, but there is so much to display.</p>
  
  <p><strong>AnimatedStackPanel.cs class</strong></p>
  
  <pre><code>public class AnimatedStackPanel : Panel
  {
      protected override Size MeasureOverride(Size availableSize)
      {
          double x = 0, y = 0;
          foreach (UIElement child in Children)
          {
              child.Measure(availableSize);
              x = Math.Max(x, availableSize.Width == double.PositiveInfinity ? child.DesiredSize.Width : availableSize.Width);
              y += child.DesiredSize.Height;
          }
          return new Size(availableSize.Width == double.PositiveInfinity ? x : availableSize.Width, availableSize.Height == double.PositiveInfinity ? y : availableSize.Height);
      }
  
      protected override Size ArrangeOverride(Size finalSize)
      {
          if (this.Children == null || this.Children.Count == 0) return finalSize;
          Size previousChildSize = new Size();
          Point endPosition = new Point(0, 0);
          foreach (UIElement child in Children)
          {
              endPosition.Y += previousChildSize.Height;
              double childWidth = finalSize.Width;
              child.Arrange(new Rect(0, 0, childWidth, child.DesiredSize.Height));
              Point startPosition = new Point(endPosition.X, endPosition.Y + finalSize.Height);
              AnimatePosition(child, startPosition, endPosition, new TimeSpan(0, 0, 1));
              previousChildSize = child.DesiredSize;
          }
          return finalSize;
      }
  
      private void AnimatePosition(UIElement child, Point startPosition, Point endPosition, TimeSpan animationDuration)
      {
          DoubleAnimation xAnimation = new DoubleAnimation(startPosition.X, endPosition.X, animationDuration);
          DoubleAnimation yAnimation = new DoubleAnimation(startPosition.Y, endPosition.Y, animationDuration);
          TransformGroup transformGroup = child.RenderTransform as TransformGroup;
          if (transformGroup == null)
          {
              TranslateTransform translatationTransform = new TranslateTransform();
              transformGroup = new TransformGroup();
              transformGroup.Children.Add(translatationTransform);
              child.RenderTransform = transformGroup;
              child.RenderTransformOrigin = new Point(0, 0);
          }
          TranslateTransform translateTransform = (TranslateTransform)transformGroup.Children[0];
          translateTransform.BeginAnimation(TranslateTransform.XProperty, xAnimation, HandoffBehavior.Compose);
          translateTransform.BeginAnimation(TranslateTransform.YProperty, yAnimation, HandoffBehavior.Compose);
          //child.Arrange(new Rect(endPosition.X, endPosition.Y, child.DesiredSize.Width, child.DesiredSize.Height));
      }
  }
  </code></pre>
  
  <p><strong>AnimatedListBox.xaml <code>UserControl</code></strong></p>
  
  <pre><code>&lt;UserControl x:Class="WpfTest.Views.AnimatedListBox"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:Controls="clr-namespace:WpfTest.Views.Controls"&gt;
      &lt;UserControl.Resources&gt;
          &lt;ItemsPanelTemplate x:Key="AnimatedStackPanel"&gt;
              &lt;Controls:AnimatedStackPanel /&gt;
          &lt;/ItemsPanelTemplate&gt;
      &lt;/UserControl.Resources&gt;
      &lt;Grid&gt;
          &lt;ListBox ItemsSource="{Binding Data}" ItemsPanel="{StaticResource AnimatedStackPanel}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" /&gt;
      &lt;/Grid&gt;
  &lt;/UserControl&gt;
  </code></pre>
  
  <p><strong>AnimatedListBox.xaml.cs <code>UserControl</code> code behind</strong></p>
  
  <pre><code>public partial class AnimatedListBox : UserControl
  {
      public AnimatedListBox()
      {
          InitializeComponent();
          Data = new ObservableCollection&lt;int&gt;();
          // change this 150 below to anything less than 142 and the problem disappears!!
          for (int count = 1; count &lt;= 150; count++) Data.Add(count);
          DataContext = this;
      }
  
      public static DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(ObservableCollection&lt;int&gt;), typeof(AnimatedListBox));
  
      public ObservableCollection&lt;int&gt; Data
      {
          get { return (ObservableCollection&lt;int&gt;)GetValue(DataProperty); }
          set { SetValue(DataProperty, value); }
      }
  }
  </code></pre>
  
  <p>While experimenting, I found something else that seems strange. If I change the position of the item in the <code>Panel.ArrangeOverride</code> method to the following, there are more unexpected results.</p>
  
  <pre><code>child.Arrange(new Rect(endPosition.X, endPosition.Y, childWidth, child.DesiredSize.Height));
  </code></pre>
  
  <p>Somehow, this changes the actual (animation) end positions, ie. where the items end up being positioned. It introduces some kind of double spacing of the items. How can arranging the item in its end position before it starts its animation change its final end position? Am I missing something obvious?</p>
  
  <p>Also, if you uncomment the commented line in the <code>AnimatedStackPanel.AnimatePosition</code> method and comment out the two lines above (ie. cut out the animation and move the items directly to the same end positions that the animations would have moved them to) then you will see the problem disappear again... this applies no matter how many items are in the collection. This is what led me to think that the problem was animation related.</p>
  
  <p>One last thing that I found out is that the problem exists with or without using <code>DataTemplate</code>s for the item data type... I'm pretty sure that it's animation related. If anyone can see what I'm doing wrong, or find a solution for this, I'd be very grateful because this one has had me baffled. Can anyone even reproduce this problem with the example code? Many thanks in advance.</p>