Files
HandyControl/src/Shared/HandyControl_Shared/Interactivity/FluidMoveBehaviorBase.cs
T
2019-04-05 22:23:19 +08:00

196 lines
7.0 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 HandyControl.Data;
namespace HandyControl.Interactivity
{
public abstract class FluidMoveBehaviorBase : Behavior<FrameworkElement>
{
public static readonly DependencyProperty AppliesToProperty = DependencyProperty.Register("AppliesTo",
typeof(FluidMoveScope), typeof(FluidMoveBehaviorBase), new PropertyMetadata(FluidMoveScope.Self));
protected static readonly DependencyProperty IdentityTagProperty =
DependencyProperty.RegisterAttached("IdentityTag", typeof(object), typeof(FluidMoveBehaviorBase),
new PropertyMetadata(null));
public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register("IsActive",
typeof(bool), typeof(FluidMoveBehaviorBase), new PropertyMetadata(ValueBoxes.TrueBox));
private static DateTime LastPurgeTick = DateTime.MinValue;
private static readonly TimeSpan MinTickDelta = TimeSpan.FromSeconds(0.5);
private static DateTime NextToLastPurgeTick = DateTime.MinValue;
internal static Dictionary<object, TagData> TagDictionary = new Dictionary<object, TagData>();
public static readonly DependencyProperty TagPathProperty = DependencyProperty.Register("TagPath",
typeof(string), typeof(FluidMoveBehaviorBase), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty TagProperty = DependencyProperty.Register("Tag",
typeof(TagType), typeof(FluidMoveBehaviorBase), new PropertyMetadata(TagType.Element));
public FluidMoveScope AppliesTo
{
get =>
(FluidMoveScope) GetValue(AppliesToProperty);
set => SetValue(AppliesToProperty, value);
}
public bool IsActive
{
get =>
(bool) GetValue(IsActiveProperty);
set => SetValue(IsActiveProperty, value);
}
protected virtual bool ShouldSkipInitialLayout =>
Tag == TagType.DataContext;
public TagType Tag
{
get =>
(TagType) GetValue(TagProperty);
set => SetValue(TagProperty, value);
}
public string TagPath
{
get =>
(string) GetValue(TagPathProperty);
set => SetValue(TagPathProperty, value);
}
private void AssociatedObject_LayoutUpdated(object sender, EventArgs e)
{
if (IsActive)
{
if (DateTime.Now - LastPurgeTick >= MinTickDelta)
{
List<object> list = null;
foreach (var pair in TagDictionary)
if (pair.Value.Timestamp < NextToLastPurgeTick)
{
if (list == null) list = new List<object>();
list.Add(pair.Key);
}
if (list != null)
foreach (var obj2 in list)
TagDictionary.Remove(obj2);
NextToLastPurgeTick = LastPurgeTick;
LastPurgeTick = DateTime.Now;
}
if (AppliesTo == FluidMoveScope.Self)
{
UpdateLayoutTransition(AssociatedObject);
}
else
{
if (AssociatedObject is Panel associatedObject)
foreach (FrameworkElement element in associatedObject.Children)
UpdateLayoutTransition(element);
}
}
}
protected virtual void EnsureTags(FrameworkElement child)
{
if (Tag == TagType.DataContext &&
!(child.ReadLocalValue(IdentityTagProperty) is BindingExpression))
child.SetBinding(IdentityTagProperty, new Binding(TagPath));
}
protected static object GetIdentityTag(DependencyObject obj)
{
return obj.GetValue(IdentityTagProperty);
}
private static FrameworkElement GetVisualRoot(FrameworkElement child)
{
while (true)
{
if (!(VisualTreeHelper.GetParent(child) is FrameworkElement parent)) return child;
if (AdornerLayer.GetAdornerLayer(parent) == null) return child;
child = parent;
}
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.LayoutUpdated += AssociatedObject_LayoutUpdated;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.LayoutUpdated -= AssociatedObject_LayoutUpdated;
}
protected static void SetIdentityTag(DependencyObject obj, object value)
{
obj.SetValue(IdentityTagProperty, value);
}
internal static Rect TranslateRect(Rect rect, FrameworkElement from, FrameworkElement to)
{
if (from == null || to == null) return rect;
var point = new Point(rect.Left, rect.Top);
point = from.TransformToVisual(to).Transform(point);
return new Rect(point.X, point.Y, rect.Width, rect.Height);
}
private void UpdateLayoutTransition(FrameworkElement child)
{
if (child.Visibility != Visibility.Collapsed && child.IsLoaded || !ShouldSkipInitialLayout)
{
var visualRoot = GetVisualRoot(child);
var newTagData = new TagData
{
Parent = VisualTreeHelper.GetParent(child) as FrameworkElement,
ParentRect = ExtendedVisualStateManager.GetLayoutRect(child),
Child = child,
Timestamp = DateTime.Now
};
try
{
newTagData.AppRect = TranslateRect(newTagData.ParentRect, newTagData.Parent, visualRoot);
}
catch (ArgumentException)
{
if (ShouldSkipInitialLayout) return;
}
EnsureTags(child);
var identityTag = GetIdentityTag(child) ?? child;
UpdateLayoutTransitionCore(child, visualRoot, identityTag, newTagData);
}
}
internal abstract void UpdateLayoutTransitionCore(FrameworkElement child, FrameworkElement root,
object tag, TagData newTagData);
internal class TagData
{
public Rect AppRect { get; set; }
public FrameworkElement Child { get; set; }
public object InitialTag { get; set; }
public FrameworkElement Parent { get; set; }
public Rect ParentRect { get; set; }
public DateTime Timestamp { get; set; }
}
}
}