123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- 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();
- 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, ValueBoxes.BooleanBox(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; }
- }
- }
|