FluidMoveBehavior.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Data;
  6. using System.Windows.Documents;
  7. using System.Windows.Media;
  8. using System.Windows.Media.Animation;
  9. using System.Windows.Shapes;
  10. using HandyControl.Data;
  11. namespace HandyControl.Interactivity;
  12. public sealed class FluidMoveBehavior : FluidMoveBehaviorBase
  13. {
  14. private static readonly DependencyProperty CacheDuringOverlayProperty =
  15. DependencyProperty.RegisterAttached("CacheDuringOverlay", typeof(object),
  16. typeof(FluidMoveBehavior), new PropertyMetadata(null));
  17. public static readonly DependencyProperty DurationProperty = DependencyProperty.Register("Duration",
  18. typeof(Duration), typeof(FluidMoveBehavior),
  19. new PropertyMetadata(new Duration(TimeSpan.FromSeconds(1.0))));
  20. public static readonly DependencyProperty EaseXProperty = DependencyProperty.Register("EaseX",
  21. typeof(IEasingFunction), typeof(FluidMoveBehavior), new PropertyMetadata(null));
  22. public static readonly DependencyProperty EaseYProperty = DependencyProperty.Register("EaseY",
  23. typeof(IEasingFunction), typeof(FluidMoveBehavior), new PropertyMetadata(null));
  24. public static readonly DependencyProperty FloatAboveProperty =
  25. DependencyProperty.Register("FloatAbove", typeof(bool), typeof(FluidMoveBehavior),
  26. new PropertyMetadata(ValueBoxes.TrueBox));
  27. private static readonly DependencyProperty HasTransformWrapperProperty =
  28. DependencyProperty.RegisterAttached("HasTransformWrapper", typeof(bool),
  29. typeof(FluidMoveBehavior), new PropertyMetadata(ValueBoxes.FalseBox));
  30. private static readonly DependencyProperty InitialIdentityTagProperty =
  31. DependencyProperty.RegisterAttached("InitialIdentityTag", typeof(object),
  32. typeof(FluidMoveBehavior), new PropertyMetadata(null));
  33. public static readonly DependencyProperty InitialTagPathProperty =
  34. DependencyProperty.Register("InitialTagPath", typeof(string), typeof(FluidMoveBehavior),
  35. new PropertyMetadata(string.Empty));
  36. public static readonly DependencyProperty InitialTagProperty =
  37. DependencyProperty.Register("InitialTag", typeof(TagType), typeof(FluidMoveBehavior),
  38. new PropertyMetadata(TagType.Element));
  39. private static readonly DependencyProperty OverlayProperty =
  40. DependencyProperty.RegisterAttached("Overlay", typeof(object), typeof(FluidMoveBehavior),
  41. new PropertyMetadata(null));
  42. private static readonly Dictionary<object, Storyboard> TransitionStoryboardDictionary =
  43. new();
  44. public Duration Duration
  45. {
  46. get =>
  47. (Duration) GetValue(DurationProperty);
  48. set => SetValue(DurationProperty, value);
  49. }
  50. public IEasingFunction EaseX
  51. {
  52. get =>
  53. (IEasingFunction) GetValue(EaseXProperty);
  54. set => SetValue(EaseXProperty, value);
  55. }
  56. public IEasingFunction EaseY
  57. {
  58. get =>
  59. (IEasingFunction) GetValue(EaseYProperty);
  60. set => SetValue(EaseYProperty, value);
  61. }
  62. public bool FloatAbove
  63. {
  64. get =>
  65. (bool) GetValue(FloatAboveProperty);
  66. set => SetValue(FloatAboveProperty, ValueBoxes.BooleanBox(value));
  67. }
  68. public TagType InitialTag
  69. {
  70. get =>
  71. (TagType) GetValue(InitialTagProperty);
  72. set => SetValue(InitialTagProperty, value);
  73. }
  74. public string InitialTagPath
  75. {
  76. get =>
  77. (string) GetValue(InitialTagPathProperty);
  78. set => SetValue(InitialTagPathProperty, value);
  79. }
  80. protected override bool ShouldSkipInitialLayout
  81. {
  82. get
  83. {
  84. if (!base.ShouldSkipInitialLayout) return InitialTag == TagType.DataContext;
  85. return true;
  86. }
  87. }
  88. private static void AddTransform(FrameworkElement child, Transform transform)
  89. {
  90. if (!(child.RenderTransform is TransformGroup renderTransform))
  91. {
  92. renderTransform = new TransformGroup
  93. {
  94. Children = { child.RenderTransform }
  95. };
  96. child.RenderTransform = renderTransform;
  97. SetHasTransformWrapper(child, true);
  98. }
  99. renderTransform.Children.Add(transform);
  100. }
  101. private Storyboard CreateTransitionStoryboard(FrameworkElement child, bool usingBeforeLoaded,
  102. ref Rect layoutRect, ref Rect currentRect)
  103. {
  104. var duration = Duration;
  105. var storyboard = new Storyboard
  106. {
  107. Duration = duration
  108. };
  109. var num = !usingBeforeLoaded || Math.Abs(layoutRect.Width) < 0.001
  110. ? 1.0
  111. : currentRect.Width / layoutRect.Width;
  112. var num2 = !usingBeforeLoaded || Math.Abs(layoutRect.Height) < 0.001
  113. ? 1.0
  114. : currentRect.Height / layoutRect.Height;
  115. var num3 = currentRect.Left - layoutRect.Left;
  116. var num4 = currentRect.Top - layoutRect.Top;
  117. var group = new TransformGroup();
  118. var transform = new ScaleTransform
  119. {
  120. ScaleX = num,
  121. ScaleY = num2
  122. };
  123. group.Children.Add(transform);
  124. var transform2 = new TranslateTransform
  125. {
  126. X = num3,
  127. Y = num4
  128. };
  129. group.Children.Add(transform2);
  130. AddTransform(child, group);
  131. var str = "(FrameworkElement.RenderTransform).";
  132. if (child.RenderTransform is TransformGroup renderTransform && GetHasTransformWrapper(child))
  133. {
  134. object obj2 = str;
  135. str = string.Concat(obj2, "(TransformGroup.Children)[", renderTransform.Children.Count - 1,
  136. "].");
  137. }
  138. if (usingBeforeLoaded)
  139. {
  140. if (Math.Abs(num - 1.0) > 0.001)
  141. {
  142. var element = new DoubleAnimation
  143. {
  144. Duration = duration,
  145. From = num,
  146. To = 1.0
  147. };
  148. Storyboard.SetTarget(element, child);
  149. Storyboard.SetTargetProperty(element,
  150. new PropertyPath(str + "(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"));
  151. element.EasingFunction = EaseX;
  152. storyboard.Children.Add(element);
  153. }
  154. if (Math.Abs(num2 - 1.0) > 0.001)
  155. {
  156. var animation3 = new DoubleAnimation
  157. {
  158. Duration = duration,
  159. From = num2,
  160. To = 1.0
  161. };
  162. Storyboard.SetTarget(animation3, child);
  163. Storyboard.SetTargetProperty(animation3,
  164. new PropertyPath(str + "(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"));
  165. animation3.EasingFunction = EaseY;
  166. storyboard.Children.Add(animation3);
  167. }
  168. }
  169. if (Math.Abs(num3) > 0.001)
  170. {
  171. var animation5 = new DoubleAnimation
  172. {
  173. Duration = duration,
  174. From = num3,
  175. To = 0.0
  176. };
  177. Storyboard.SetTarget(animation5, child);
  178. Storyboard.SetTargetProperty(animation5,
  179. new PropertyPath(str + "(TransformGroup.Children)[1].(TranslateTransform.X)"));
  180. animation5.EasingFunction = EaseX;
  181. storyboard.Children.Add(animation5);
  182. }
  183. if (Math.Abs(num4) > 0.001)
  184. {
  185. var animation7 = new DoubleAnimation
  186. {
  187. Duration = duration,
  188. From = num4,
  189. To = 0.0
  190. };
  191. Storyboard.SetTarget(animation7, child);
  192. Storyboard.SetTargetProperty(animation7,
  193. new PropertyPath(str + "(TransformGroup.Children)[1].(TranslateTransform.Y)"));
  194. animation7.EasingFunction = EaseY;
  195. storyboard.Children.Add(animation7);
  196. }
  197. return storyboard;
  198. }
  199. protected override void EnsureTags(FrameworkElement child)
  200. {
  201. base.EnsureTags(child);
  202. if (InitialTag == TagType.DataContext &&
  203. !(child.ReadLocalValue(InitialIdentityTagProperty) is BindingExpression))
  204. child.SetBinding(InitialIdentityTagProperty, new Binding(InitialTagPath));
  205. }
  206. private static bool GetHasTransformWrapper(DependencyObject obj)
  207. {
  208. return (bool) obj.GetValue(HasTransformWrapperProperty);
  209. }
  210. private static object GetInitialIdentityTag(DependencyObject obj)
  211. {
  212. return obj.GetValue(InitialIdentityTagProperty);
  213. }
  214. private static object GetOverlay(DependencyObject obj)
  215. {
  216. return obj.GetValue(OverlayProperty);
  217. }
  218. private static Transform GetTransform(FrameworkElement child)
  219. {
  220. if (child.RenderTransform is TransformGroup renderTransform && renderTransform.Children.Count > 0)
  221. return renderTransform.Children[renderTransform.Children.Count - 1];
  222. return new TranslateTransform();
  223. }
  224. private static bool IsClose(double a, double b)
  225. {
  226. return Math.Abs(a - b) < 1E-07;
  227. }
  228. private static bool IsEmptyRect(Rect rect)
  229. {
  230. if (!rect.IsEmpty && !double.IsNaN(rect.Left)) return double.IsNaN(rect.Top);
  231. return true;
  232. }
  233. private static void RemoveTransform(FrameworkElement child)
  234. {
  235. if (child.RenderTransform is TransformGroup renderTransform)
  236. {
  237. if (GetHasTransformWrapper(child))
  238. {
  239. child.RenderTransform = renderTransform.Children[0];
  240. SetHasTransformWrapper(child, false);
  241. }
  242. else
  243. {
  244. renderTransform.Children.RemoveAt(renderTransform.Children.Count - 1);
  245. }
  246. }
  247. }
  248. private static void SetHasTransformWrapper(DependencyObject obj, bool value)
  249. {
  250. obj.SetValue(HasTransformWrapperProperty, ValueBoxes.BooleanBox(value));
  251. }
  252. private static void SetOverlay(DependencyObject obj, object value)
  253. {
  254. obj.SetValue(OverlayProperty, value);
  255. }
  256. private static void TransferLocalValue(FrameworkElement element, DependencyProperty source,
  257. DependencyProperty dest)
  258. {
  259. var obj2 = element.ReadLocalValue(source);
  260. if (obj2 is BindingExpressionBase base2)
  261. element.SetBinding(dest, base2.ParentBindingBase);
  262. else if (obj2 == DependencyProperty.UnsetValue)
  263. element.ClearValue(dest);
  264. else
  265. element.SetValue(dest, element.GetAnimationBaseValue(source));
  266. element.ClearValue(source);
  267. }
  268. internal override void UpdateLayoutTransitionCore(FrameworkElement child, FrameworkElement root,
  269. object tag, TagData newTagData)
  270. {
  271. Rect empty;
  272. var flag = false;
  273. var usingBeforeLoaded = false;
  274. var initialIdentityTag = GetInitialIdentityTag(child);
  275. var flag3 = TagDictionary.TryGetValue(tag, out var data);
  276. if (flag3 && data.InitialTag != initialIdentityTag)
  277. {
  278. flag3 = false;
  279. TagDictionary.Remove(tag);
  280. }
  281. if (!flag3)
  282. {
  283. if (initialIdentityTag != null && TagDictionary.TryGetValue(initialIdentityTag, out var data2))
  284. {
  285. empty = TranslateRect(data2.AppRect, root, newTagData.Parent);
  286. flag = true;
  287. usingBeforeLoaded = true;
  288. }
  289. else
  290. {
  291. empty = Rect.Empty;
  292. }
  293. data = new TagData
  294. {
  295. ParentRect = Rect.Empty,
  296. AppRect = Rect.Empty,
  297. Parent = newTagData.Parent,
  298. Child = child,
  299. Timestamp = DateTime.Now,
  300. InitialTag = initialIdentityTag
  301. };
  302. TagDictionary.Add(tag, data);
  303. }
  304. else if (!Equals(data.Parent, VisualTreeHelper.GetParent(child)))
  305. {
  306. empty = TranslateRect(data.AppRect, root, newTagData.Parent);
  307. flag = true;
  308. }
  309. else
  310. {
  311. empty = data.ParentRect;
  312. }
  313. var originalChild = child;
  314. if (!IsEmptyRect(empty) && !IsEmptyRect(newTagData.ParentRect) &&
  315. (!IsClose(empty.Left, newTagData.ParentRect.Left) ||
  316. !IsClose(empty.Top, newTagData.ParentRect.Top)) || !Equals(child, data.Child) &&
  317. TransitionStoryboardDictionary.ContainsKey(tag))
  318. {
  319. var rect = empty;
  320. var flag4 = false;
  321. if (TransitionStoryboardDictionary.TryGetValue(tag, out var storyboard))
  322. {
  323. var obj3 = GetOverlay(data.Child);
  324. var adorner = (AdornerContainer) obj3;
  325. flag4 = obj3 != null;
  326. var element = data.Child;
  327. if (obj3 != null)
  328. {
  329. if (adorner.Child is Canvas canvas) element = canvas.Children[0] as FrameworkElement;
  330. }
  331. if (!usingBeforeLoaded) rect = GetTransform(element).TransformBounds(rect);
  332. TransitionStoryboardDictionary.Remove(tag);
  333. storyboard.Stop();
  334. RemoveTransform(element);
  335. if (obj3 != null)
  336. {
  337. AdornerLayer.GetAdornerLayer(root).Remove(adorner);
  338. TransferLocalValue(data.Child, CacheDuringOverlayProperty,
  339. UIElement.RenderTransformProperty);
  340. SetOverlay(data.Child, null);
  341. }
  342. }
  343. object overlay = null;
  344. if (flag4 || flag && FloatAbove)
  345. {
  346. var canvas2 = new Canvas
  347. {
  348. Width = newTagData.ParentRect.Width,
  349. Height = newTagData.ParentRect.Height,
  350. IsHitTestVisible = false
  351. };
  352. var rectangle = new Rectangle
  353. {
  354. Width = newTagData.ParentRect.Width,
  355. Height = newTagData.ParentRect.Height,
  356. IsHitTestVisible = false,
  357. Fill = new VisualBrush(child)
  358. };
  359. canvas2.Children.Add(rectangle);
  360. var container2 = new AdornerContainer(child)
  361. {
  362. Child = canvas2
  363. };
  364. overlay = container2;
  365. SetOverlay(originalChild, overlay);
  366. AdornerLayer.GetAdornerLayer(root).Add(container2);
  367. TransferLocalValue(child, UIElement.RenderTransformProperty, CacheDuringOverlayProperty);
  368. child.RenderTransform = new TranslateTransform(-10000.0, -10000.0);
  369. canvas2.RenderTransform = new TranslateTransform(10000.0, 10000.0);
  370. child = rectangle;
  371. }
  372. var parentRect = newTagData.ParentRect;
  373. var transitionStoryboard =
  374. CreateTransitionStoryboard(child, usingBeforeLoaded, ref parentRect, ref rect);
  375. TransitionStoryboardDictionary.Add(tag, transitionStoryboard);
  376. transitionStoryboard.Completed += delegate
  377. {
  378. if (TransitionStoryboardDictionary.TryGetValue(tag, out var storyboard1) &&
  379. Equals(storyboard1, transitionStoryboard))
  380. {
  381. TransitionStoryboardDictionary.Remove(tag);
  382. transitionStoryboard.Stop();
  383. RemoveTransform(child);
  384. child.InvalidateMeasure();
  385. if (overlay != null)
  386. {
  387. AdornerLayer.GetAdornerLayer(root).Remove((AdornerContainer) overlay);
  388. TransferLocalValue(originalChild, CacheDuringOverlayProperty,
  389. UIElement.RenderTransformProperty);
  390. SetOverlay(originalChild, null);
  391. }
  392. }
  393. };
  394. transitionStoryboard.Begin();
  395. }
  396. data.ParentRect = newTagData.ParentRect;
  397. data.AppRect = newTagData.AppRect;
  398. data.Parent = newTagData.Parent;
  399. data.Child = newTagData.Child;
  400. data.Timestamp = newTagData.Timestamp;
  401. }
  402. }