449 lines
17 KiB
C#
449 lines
17 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Data;
|
|
using System.Windows.Documents;
|
|
using System.Windows.Media;
|
|
using System.Windows.Media.Animation;
|
|
using System.Windows.Shapes;
|
|
using HandyControl.Data;
|
|
|
|
namespace HandyControl.Interactivity
|
|
{
|
|
public sealed class FluidMoveBehavior : FluidMoveBehaviorBase
|
|
{
|
|
private static readonly DependencyProperty CacheDuringOverlayProperty =
|
|
DependencyProperty.RegisterAttached("CacheDuringOverlay", typeof(object),
|
|
typeof(FluidMoveBehavior), new PropertyMetadata(null));
|
|
|
|
public static readonly DependencyProperty DurationProperty = DependencyProperty.Register("Duration",
|
|
typeof(Duration), typeof(FluidMoveBehavior),
|
|
new PropertyMetadata(new Duration(TimeSpan.FromSeconds(1.0))));
|
|
|
|
public static readonly DependencyProperty EaseXProperty = DependencyProperty.Register("EaseX",
|
|
typeof(IEasingFunction), typeof(FluidMoveBehavior), new PropertyMetadata(null));
|
|
|
|
public static readonly DependencyProperty EaseYProperty = DependencyProperty.Register("EaseY",
|
|
typeof(IEasingFunction), typeof(FluidMoveBehavior), new PropertyMetadata(null));
|
|
|
|
public static readonly DependencyProperty FloatAboveProperty =
|
|
DependencyProperty.Register("FloatAbove", typeof(bool), typeof(FluidMoveBehavior),
|
|
new PropertyMetadata(ValueBoxes.TrueBox));
|
|
|
|
private static readonly DependencyProperty HasTransformWrapperProperty =
|
|
DependencyProperty.RegisterAttached("HasTransformWrapper", typeof(bool),
|
|
typeof(FluidMoveBehavior), new PropertyMetadata(ValueBoxes.FalseBox));
|
|
|
|
private static readonly DependencyProperty InitialIdentityTagProperty =
|
|
DependencyProperty.RegisterAttached("InitialIdentityTag", typeof(object),
|
|
typeof(FluidMoveBehavior), new PropertyMetadata(null));
|
|
|
|
public static readonly DependencyProperty InitialTagPathProperty =
|
|
DependencyProperty.Register("InitialTagPath", typeof(string), typeof(FluidMoveBehavior),
|
|
new PropertyMetadata(string.Empty));
|
|
|
|
public static readonly DependencyProperty InitialTagProperty =
|
|
DependencyProperty.Register("InitialTag", typeof(TagType), typeof(FluidMoveBehavior),
|
|
new PropertyMetadata(TagType.Element));
|
|
|
|
private static readonly DependencyProperty OverlayProperty =
|
|
DependencyProperty.RegisterAttached("Overlay", typeof(object), typeof(FluidMoveBehavior),
|
|
new PropertyMetadata(null));
|
|
|
|
private static readonly Dictionary<object, Storyboard> TransitionStoryboardDictionary =
|
|
new Dictionary<object, Storyboard>();
|
|
|
|
public Duration Duration
|
|
{
|
|
get =>
|
|
(Duration) GetValue(DurationProperty);
|
|
set => SetValue(DurationProperty, value);
|
|
}
|
|
|
|
public IEasingFunction EaseX
|
|
{
|
|
get =>
|
|
(IEasingFunction) GetValue(EaseXProperty);
|
|
set => SetValue(EaseXProperty, value);
|
|
}
|
|
|
|
public IEasingFunction EaseY
|
|
{
|
|
get =>
|
|
(IEasingFunction) GetValue(EaseYProperty);
|
|
set => SetValue(EaseYProperty, value);
|
|
}
|
|
|
|
public bool FloatAbove
|
|
{
|
|
get =>
|
|
(bool) GetValue(FloatAboveProperty);
|
|
set => SetValue(FloatAboveProperty, value);
|
|
}
|
|
|
|
public TagType InitialTag
|
|
{
|
|
get =>
|
|
(TagType) GetValue(InitialTagProperty);
|
|
set => SetValue(InitialTagProperty, value);
|
|
}
|
|
|
|
public string InitialTagPath
|
|
{
|
|
get =>
|
|
(string) GetValue(InitialTagPathProperty);
|
|
set => SetValue(InitialTagPathProperty, value);
|
|
}
|
|
|
|
protected override bool ShouldSkipInitialLayout
|
|
{
|
|
get
|
|
{
|
|
if (!base.ShouldSkipInitialLayout) return InitialTag == TagType.DataContext;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private static void AddTransform(FrameworkElement child, Transform transform)
|
|
{
|
|
if (!(child.RenderTransform is TransformGroup renderTransform))
|
|
{
|
|
renderTransform = new TransformGroup
|
|
{
|
|
Children = {child.RenderTransform}
|
|
};
|
|
child.RenderTransform = renderTransform;
|
|
SetHasTransformWrapper(child, true);
|
|
}
|
|
|
|
renderTransform.Children.Add(transform);
|
|
}
|
|
|
|
private Storyboard CreateTransitionStoryboard(FrameworkElement child, bool usingBeforeLoaded,
|
|
ref Rect layoutRect, ref Rect currentRect)
|
|
{
|
|
var duration = Duration;
|
|
var storyboard = new Storyboard
|
|
{
|
|
Duration = duration
|
|
};
|
|
var num = !usingBeforeLoaded || Math.Abs(layoutRect.Width) < 0.001
|
|
? 1.0
|
|
: currentRect.Width / layoutRect.Width;
|
|
var num2 = !usingBeforeLoaded || Math.Abs(layoutRect.Height) < 0.001
|
|
? 1.0
|
|
: currentRect.Height / layoutRect.Height;
|
|
var num3 = currentRect.Left - layoutRect.Left;
|
|
var num4 = currentRect.Top - layoutRect.Top;
|
|
var group = new TransformGroup();
|
|
var transform = new ScaleTransform
|
|
{
|
|
ScaleX = num,
|
|
ScaleY = num2
|
|
};
|
|
group.Children.Add(transform);
|
|
var transform2 = new TranslateTransform
|
|
{
|
|
X = num3,
|
|
Y = num4
|
|
};
|
|
group.Children.Add(transform2);
|
|
AddTransform(child, group);
|
|
var str = "(FrameworkElement.RenderTransform).";
|
|
if (child.RenderTransform is TransformGroup renderTransform && GetHasTransformWrapper(child))
|
|
{
|
|
object obj2 = str;
|
|
str = string.Concat(obj2, "(TransformGroup.Children)[", renderTransform.Children.Count - 1,
|
|
"].");
|
|
}
|
|
|
|
if (usingBeforeLoaded)
|
|
{
|
|
if (Math.Abs(num - 1.0) > 0.001)
|
|
{
|
|
var element = new DoubleAnimation
|
|
{
|
|
Duration = duration,
|
|
From = num,
|
|
To = 1.0
|
|
};
|
|
Storyboard.SetTarget(element, child);
|
|
Storyboard.SetTargetProperty(element,
|
|
new PropertyPath(str + "(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"));
|
|
element.EasingFunction = EaseX;
|
|
storyboard.Children.Add(element);
|
|
}
|
|
|
|
if (Math.Abs(num2 - 1.0) > 0.001)
|
|
{
|
|
var animation3 = new DoubleAnimation
|
|
{
|
|
Duration = duration,
|
|
From = num2,
|
|
To = 1.0
|
|
};
|
|
Storyboard.SetTarget(animation3, child);
|
|
Storyboard.SetTargetProperty(animation3,
|
|
new PropertyPath(str + "(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"));
|
|
animation3.EasingFunction = EaseY;
|
|
storyboard.Children.Add(animation3);
|
|
}
|
|
}
|
|
|
|
if (Math.Abs(num3) > 0.001)
|
|
{
|
|
var animation5 = new DoubleAnimation
|
|
{
|
|
Duration = duration,
|
|
From = num3,
|
|
To = 0.0
|
|
};
|
|
Storyboard.SetTarget(animation5, child);
|
|
Storyboard.SetTargetProperty(animation5,
|
|
new PropertyPath(str + "(TransformGroup.Children)[1].(TranslateTransform.X)"));
|
|
animation5.EasingFunction = EaseX;
|
|
storyboard.Children.Add(animation5);
|
|
}
|
|
|
|
if (Math.Abs(num4) > 0.001)
|
|
{
|
|
var animation7 = new DoubleAnimation
|
|
{
|
|
Duration = duration,
|
|
From = num4,
|
|
To = 0.0
|
|
};
|
|
Storyboard.SetTarget(animation7, child);
|
|
Storyboard.SetTargetProperty(animation7,
|
|
new PropertyPath(str + "(TransformGroup.Children)[1].(TranslateTransform.Y)"));
|
|
animation7.EasingFunction = EaseY;
|
|
storyboard.Children.Add(animation7);
|
|
}
|
|
|
|
return storyboard;
|
|
}
|
|
|
|
protected override void EnsureTags(FrameworkElement child)
|
|
{
|
|
base.EnsureTags(child);
|
|
if (InitialTag == TagType.DataContext &&
|
|
!(child.ReadLocalValue(InitialIdentityTagProperty) is BindingExpression))
|
|
child.SetBinding(InitialIdentityTagProperty, new Binding(InitialTagPath));
|
|
}
|
|
|
|
private static bool GetHasTransformWrapper(DependencyObject obj)
|
|
{
|
|
return (bool) obj.GetValue(HasTransformWrapperProperty);
|
|
}
|
|
|
|
private static object GetInitialIdentityTag(DependencyObject obj)
|
|
{
|
|
return obj.GetValue(InitialIdentityTagProperty);
|
|
}
|
|
|
|
private static object GetOverlay(DependencyObject obj)
|
|
{
|
|
return obj.GetValue(OverlayProperty);
|
|
}
|
|
|
|
private static Transform GetTransform(FrameworkElement child)
|
|
{
|
|
if (child.RenderTransform is TransformGroup renderTransform && renderTransform.Children.Count > 0)
|
|
return renderTransform.Children[renderTransform.Children.Count - 1];
|
|
return new TranslateTransform();
|
|
}
|
|
|
|
private static bool IsClose(double a, double b)
|
|
{
|
|
return Math.Abs(a - b) < 1E-07;
|
|
}
|
|
|
|
private static bool IsEmptyRect(Rect rect)
|
|
{
|
|
if (!rect.IsEmpty && !double.IsNaN(rect.Left)) return double.IsNaN(rect.Top);
|
|
return true;
|
|
}
|
|
|
|
private static void RemoveTransform(FrameworkElement child)
|
|
{
|
|
if (child.RenderTransform is TransformGroup renderTransform)
|
|
{
|
|
if (GetHasTransformWrapper(child))
|
|
{
|
|
child.RenderTransform = renderTransform.Children[0];
|
|
SetHasTransformWrapper(child, false);
|
|
}
|
|
else
|
|
{
|
|
renderTransform.Children.RemoveAt(renderTransform.Children.Count - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void SetHasTransformWrapper(DependencyObject obj, bool value)
|
|
{
|
|
obj.SetValue(HasTransformWrapperProperty, value);
|
|
}
|
|
|
|
private static void SetOverlay(DependencyObject obj, object value)
|
|
{
|
|
obj.SetValue(OverlayProperty, value);
|
|
}
|
|
|
|
private static void TransferLocalValue(FrameworkElement element, DependencyProperty source,
|
|
DependencyProperty dest)
|
|
{
|
|
var obj2 = element.ReadLocalValue(source);
|
|
if (obj2 is BindingExpressionBase base2)
|
|
element.SetBinding(dest, base2.ParentBindingBase);
|
|
else if (obj2 == DependencyProperty.UnsetValue)
|
|
element.ClearValue(dest);
|
|
else
|
|
element.SetValue(dest, element.GetAnimationBaseValue(source));
|
|
element.ClearValue(source);
|
|
}
|
|
|
|
internal override void UpdateLayoutTransitionCore(FrameworkElement child, FrameworkElement root,
|
|
object tag, TagData newTagData)
|
|
{
|
|
Rect empty;
|
|
var flag = false;
|
|
var usingBeforeLoaded = false;
|
|
var initialIdentityTag = GetInitialIdentityTag(child);
|
|
var flag3 = TagDictionary.TryGetValue(tag, out var data);
|
|
if (flag3 && data.InitialTag != initialIdentityTag)
|
|
{
|
|
flag3 = false;
|
|
TagDictionary.Remove(tag);
|
|
}
|
|
|
|
if (!flag3)
|
|
{
|
|
if (initialIdentityTag != null && TagDictionary.TryGetValue(initialIdentityTag, out var data2))
|
|
{
|
|
empty = TranslateRect(data2.AppRect, root, newTagData.Parent);
|
|
flag = true;
|
|
usingBeforeLoaded = true;
|
|
}
|
|
else
|
|
{
|
|
empty = Rect.Empty;
|
|
}
|
|
|
|
data = new TagData
|
|
{
|
|
ParentRect = Rect.Empty,
|
|
AppRect = Rect.Empty,
|
|
Parent = newTagData.Parent,
|
|
Child = child,
|
|
Timestamp = DateTime.Now,
|
|
InitialTag = initialIdentityTag
|
|
};
|
|
TagDictionary.Add(tag, data);
|
|
}
|
|
else if (!Equals(data.Parent, VisualTreeHelper.GetParent(child)))
|
|
{
|
|
empty = TranslateRect(data.AppRect, root, newTagData.Parent);
|
|
flag = true;
|
|
}
|
|
else
|
|
{
|
|
empty = data.ParentRect;
|
|
}
|
|
|
|
var originalChild = child;
|
|
if (!IsEmptyRect(empty) && !IsEmptyRect(newTagData.ParentRect) &&
|
|
(!IsClose(empty.Left, newTagData.ParentRect.Left) ||
|
|
!IsClose(empty.Top, newTagData.ParentRect.Top)) || !Equals(child, data.Child) &&
|
|
TransitionStoryboardDictionary.ContainsKey(tag))
|
|
{
|
|
var rect = empty;
|
|
var flag4 = false;
|
|
if (TransitionStoryboardDictionary.TryGetValue(tag, out var storyboard))
|
|
{
|
|
var obj3 = GetOverlay(data.Child);
|
|
var adorner = (AdornerContainer) obj3;
|
|
flag4 = obj3 != null;
|
|
var element = data.Child;
|
|
if (obj3 != null)
|
|
{
|
|
if (adorner.Child is Canvas canvas) element = canvas.Children[0] as FrameworkElement;
|
|
}
|
|
|
|
if (!usingBeforeLoaded) rect = GetTransform(element).TransformBounds(rect);
|
|
TransitionStoryboardDictionary.Remove(tag);
|
|
storyboard.Stop();
|
|
RemoveTransform(element);
|
|
if (obj3 != null)
|
|
{
|
|
AdornerLayer.GetAdornerLayer(root).Remove(adorner);
|
|
TransferLocalValue(data.Child, CacheDuringOverlayProperty,
|
|
UIElement.RenderTransformProperty);
|
|
SetOverlay(data.Child, null);
|
|
}
|
|
}
|
|
|
|
object overlay = null;
|
|
if (flag4 || flag && FloatAbove)
|
|
{
|
|
var canvas2 = new Canvas
|
|
{
|
|
Width = newTagData.ParentRect.Width,
|
|
Height = newTagData.ParentRect.Height,
|
|
IsHitTestVisible = false
|
|
};
|
|
var rectangle = new Rectangle
|
|
{
|
|
Width = newTagData.ParentRect.Width,
|
|
Height = newTagData.ParentRect.Height,
|
|
IsHitTestVisible = false,
|
|
Fill = new VisualBrush(child)
|
|
};
|
|
canvas2.Children.Add(rectangle);
|
|
var container2 = new AdornerContainer(child)
|
|
{
|
|
Child = canvas2
|
|
};
|
|
overlay = container2;
|
|
SetOverlay(originalChild, overlay);
|
|
AdornerLayer.GetAdornerLayer(root).Add(container2);
|
|
TransferLocalValue(child, UIElement.RenderTransformProperty, CacheDuringOverlayProperty);
|
|
child.RenderTransform = new TranslateTransform(-10000.0, -10000.0);
|
|
canvas2.RenderTransform = new TranslateTransform(10000.0, 10000.0);
|
|
child = rectangle;
|
|
}
|
|
|
|
var parentRect = newTagData.ParentRect;
|
|
var transitionStoryboard =
|
|
CreateTransitionStoryboard(child, usingBeforeLoaded, ref parentRect, ref rect);
|
|
TransitionStoryboardDictionary.Add(tag, transitionStoryboard);
|
|
transitionStoryboard.Completed += delegate
|
|
{
|
|
if (TransitionStoryboardDictionary.TryGetValue(tag, out var storyboard1) &&
|
|
Equals(storyboard1, transitionStoryboard))
|
|
{
|
|
TransitionStoryboardDictionary.Remove(tag);
|
|
transitionStoryboard.Stop();
|
|
RemoveTransform(child);
|
|
child.InvalidateMeasure();
|
|
if (overlay != null)
|
|
{
|
|
AdornerLayer.GetAdornerLayer(root).Remove((AdornerContainer) overlay);
|
|
TransferLocalValue(originalChild, CacheDuringOverlayProperty,
|
|
UIElement.RenderTransformProperty);
|
|
SetOverlay(originalChild, null);
|
|
}
|
|
}
|
|
};
|
|
transitionStoryboard.Begin();
|
|
}
|
|
|
|
data.ParentRect = newTagData.ParentRect;
|
|
data.AppRect = newTagData.AppRect;
|
|
data.Parent = newTagData.Parent;
|
|
data.Child = newTagData.Child;
|
|
data.Timestamp = newTagData.Timestamp;
|
|
}
|
|
}
|
|
} |