I'm pretty new to WPF (and completely new to animations), so I would imagine there's just something that I'm missing here. I'm in the process of creating a new layout Panel
that lays the controls out in a particular way; the details aren't (or shouldn't be) especially important here. What I'd like to do is animate the movement of the elements that I'm managing when I call Arrange
on them.
In general, what I do is keep track of the last Rect
that I used to Arrange
each child element. When ArrangeOverride
is called...
- If animation is enabled and there is a previous bounding
Rect
for the element, IArrange
it to the X and Y of that bounding rect with the new Width and Height, then use twoDoubleAnimation
s (one for X and one for Y) to animate it to the X and Y of the new bounds - If animation is disabled or there is no existing bounding
Rect
for the element, I justArrange
it to the new bounds
When I disable animation, everything renders correctly (which is a good thing). When I enable animation, the elements render in the incorrect locations. If I resize the container, they generally animate themselves back into the correct spot.
Edit for clarification
In general, the error appears to be directly related to the coordinates of the bounds of the control. In other words, if the control is farther to the right to begin with, then the TranslateTransform
appears to offset it more than a control that is not as far to the right. The same goes for up/down. Again, resizing seems to correct the problem (in fact, the controls animate themselves into the correct spot, which is frustratingly neat), but only if the entire set of controls is visible in the new size.
This is an abbreviated version of the class:
public class MyPanel : Panel
{
private Dictionary<UIElement, Rect> lastBounds = new Dictionary<UIElement, Rect>();
protected override Size MeasureOverride(Size availableSize)
{
// do all of my measurements here and delete anything from lastBounds that
// isn't on the panel any more. This works fine
}
protected override Size ArrangeOverride(Size finalSize)
{
foreach(UIElement child in Children)
{
Rect newBounds = // get the previously calculated new bounds;
TransformGroup group= child.RenderTransform as TransformGroup;
if (group == null)
{
group = new TransformGroup();
group.Children.Add(new TranslateTransform());
child.RenderTransform = group;
}
Rect lastBounds;
if (!this.lastBounds.TryGetValue(child, out lastBounds))
lastBounds = Rect.Empty;
if (!lastBounds.IsEmpty && EnableAnimation)
{
Rect tempBounds = new Rect(lastBounds.X, lastBounds.Y, newBounds.Width, newBounds.Height);
child.Arrange(tempBounds);
int animationDuration = 300;
DoubleAnimation xAnim = new DoubleAnimation(newBounds.X - lastBounds.X, TimeSpan.FromMilliseconds(animationDuration));
DoubleAnimation yAnim = new DoubleAnimation(newBounds.Y - lastBounds.Y, TimeSpan.FromMilliseconds(animationDuration));
xAnim.AccelerationRatio = yAnim.AccelerationRatio = 0.2;
xAnim.DecelerationRatio = yAnim.DecelerationRatio = 0.7;
TranslateTransform translate = group.Children[0] as TranslateTransform;
translate.BeginAnimation(TranslateTransform.XProperty, xAnim);
translate.BeginAnimation(TranslateTransform.YProperty, yAnim);
}
else
{
child.Arrange(newBounds);
}
}
}
}