in src/Microsoft.Xaml.Behaviors/Layout/FluidMoveBehavior.cs [444:612]
internal override void UpdateLayoutTransitionCore(FrameworkElement child, FrameworkElement root, object tag, TagData newTagData)
{
TagData tagData;
Rect previousRect;
bool parentChange = false;
bool usingBeforeLoaded = false;
object initialTag = GetInitialIdentityTag(child);
// Locate the previous tag, and the parent-relative previous rect. The previous rect is computed using the app-relative rect if switching parents.
// Note that we do not use the app-relative rect all time time, because when the parent itself moves, it accounts for all the motion and we do not have to.
bool gotData = TagDictionary.TryGetValue(tag, out tagData);
// if spawn point has changed then throw away the old one
if (gotData && tagData.InitialTag != initialTag)
{
gotData = false;
TagDictionary.Remove(tag);
}
if (!gotData)
{
TagData spawnData;
if (initialTag != null && TagDictionary.TryGetValue(initialTag, out spawnData))
{
previousRect = TranslateRect(spawnData.AppRect, root, newTagData.Parent);
parentChange = true;
usingBeforeLoaded = true;
}
else
{
previousRect = Rect.Empty;
}
tagData = new TagData() { ParentRect = Rect.Empty, AppRect = Rect.Empty, Parent = newTagData.Parent, Child = child, Timestamp = DateTime.Now, InitialTag = initialTag };
TagDictionary.Add(tag, tagData);
}
else if (tagData.Parent != VisualTreeHelper.GetParent(child))
{
previousRect = TranslateRect(tagData.AppRect, root, newTagData.Parent);
parentChange = true;
}
else
{
previousRect = tagData.ParentRect;
}
FrameworkElement originalChild = child;
if ((!FluidMoveBehavior.IsEmptyRect(previousRect) && !FluidMoveBehavior.IsEmptyRect(newTagData.ParentRect)) && (!IsClose(previousRect.Left, newTagData.ParentRect.Left) || !IsClose(previousRect.Top, newTagData.ParentRect.Top)) ||
(child != tagData.Child && transitionStoryboardDictionary.ContainsKey(tag)))
{
Rect currentRect = previousRect;
bool forceFloatAbove = false;
// If this element was animating before, append its current transform to the start position and kill the old animation.
// Note that in an overlay scenario, the animation is on the image in the overlay.
Storyboard oldTransitionStoryboard = null;
if (transitionStoryboardDictionary.TryGetValue(tag, out oldTransitionStoryboard))
{
object tagOverlay = GetOverlay(tagData.Child);
AdornerContainer adornerContainer = (AdornerContainer)tagOverlay;
forceFloatAbove = (tagOverlay != null); // if floating before, we need to keep floating
FrameworkElement elementWithTransform = tagData.Child;
if (tagOverlay != null)
{
Canvas overlayCanvas = adornerContainer.Child as Canvas;
if (overlayCanvas != null)
{
elementWithTransform = overlayCanvas.Children[0] as FrameworkElement;
}
}
// if we're picking a specific starting point, don't append this transform
if (!usingBeforeLoaded)
{
Transform transform = GetTransform(elementWithTransform);
currentRect = transform.TransformBounds(currentRect);
}
transitionStoryboardDictionary.Remove(tag);
oldTransitionStoryboard.Stop();
oldTransitionStoryboard = null;
RemoveTransform(elementWithTransform);
if (tagOverlay != null)
{
System.Windows.Documents.AdornerLayer.GetAdornerLayer(root).Remove(adornerContainer);
TransferLocalValue(tagData.Child, FluidMoveBehavior.cacheDuringOverlayProperty, FrameworkElement.RenderTransformProperty);
SetOverlay(tagData.Child, null);
}
}
object overlay = null;
// If we need to float this element, then we have to:
// 1. Take a picture of it
// 2. Put that picture in an Image in a popup
// 3. Hide the original element (opacity=0 so we do not disturb layout)
// 4. Animate the image
// 5. Keep track of all the info we need to unwind this later
if (forceFloatAbove || (parentChange && this.FloatAbove))
{
Canvas canvas = new Canvas() { Width = newTagData.ParentRect.Width, Height = newTagData.ParentRect.Height, IsHitTestVisible = false };
Rectangle rectangle = new Rectangle() { Width = newTagData.ParentRect.Width, Height = newTagData.ParentRect.Height, IsHitTestVisible = false };
rectangle.Fill = new VisualBrush(child);
canvas.Children.Add(rectangle);
AdornerContainer adornerContainer = new AdornerContainer(child) { Child = canvas };
overlay = adornerContainer;
// remember this overlay so we can get info from it
SetOverlay(originalChild, overlay);
System.Windows.Documents.AdornerLayer adorners = System.Windows.Documents.AdornerLayer.GetAdornerLayer(root);
adorners.Add(adornerContainer);
// Note: Not using this approach currently because the bitmap is not ready yet
// To remove use of VisualBrush, have to fill in bitmap after a render
//RenderTargetBitmap bitmap = new RenderTargetBitmap((int)child.ActualWidth, (int)child.ActualHeight, 96, 96, PixelFormats.Pbgra32);
//bitmap.Render(parent);
//image.Source = bitmap;
// can't animate this or it will flash, have to set the value outright
TransferLocalValue(child, FrameworkElement.RenderTransformProperty, FluidMoveBehavior.cacheDuringOverlayProperty);
child.RenderTransform = new TranslateTransform(-10000, -10000);
canvas.RenderTransform = new TranslateTransform(10000, 10000);
// change value here so that the animations will be applied to the image
child = rectangle;
}
// OK, now build the actual animation
Rect parentRect = newTagData.ParentRect;
Storyboard transitionStoryboard = CreateTransitionStoryboard(child, usingBeforeLoaded, ref parentRect, ref currentRect);
// Put this storyboard in the running dictionary so we can detect reentrancy
transitionStoryboardDictionary.Add(tag, transitionStoryboard);
transitionStoryboard.Completed += delegate (object sender, EventArgs e)
{
Storyboard currentlyRunningStoryboard;
if (transitionStoryboardDictionary.TryGetValue(tag, out currentlyRunningStoryboard) && currentlyRunningStoryboard == transitionStoryboard)
{
transitionStoryboardDictionary.Remove(tag);
transitionStoryboard.Stop();
RemoveTransform(child);
child.InvalidateMeasure();
if (overlay != null)
{
System.Windows.Documents.AdornerLayer.GetAdornerLayer(root).Remove((AdornerContainer)overlay);
TransferLocalValue(originalChild, FluidMoveBehavior.cacheDuringOverlayProperty, FrameworkElement.RenderTransformProperty);
SetOverlay(originalChild, null);
}
}
};
transitionStoryboard.Begin();
}
// Store current tag status
tagData.ParentRect = newTagData.ParentRect;
tagData.AppRect = newTagData.AppRect;
tagData.Parent = newTagData.Parent;
tagData.Child = newTagData.Child;
tagData.Timestamp = newTagData.Timestamp;
}