PageRenderTime 55ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-toolkit/Microsoft.Phone.Controls.Toolkit/Effects/TurnstileFeatherEffect.cs

https://bitbucket.org/jeremejevs/milk-manager
C# | 1113 lines | 607 code | 159 blank | 347 comment | 67 complexity | 687f436de07d79672c48ecdc526e2fc7 MD5 | raw file
  1. // (c) Copyright Microsoft Corporation.
  2. // This source is subject to the Microsoft Public License (Ms-PL).
  3. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
  4. // All other rights reserved.
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Diagnostics.CodeAnalysis;
  8. using System.Globalization;
  9. using System.Linq;
  10. using System.Windows;
  11. using System.Windows.Controls;
  12. using System.Windows.Media;
  13. using System.Windows.Media.Animation;
  14. using Microsoft.Phone.Controls.Primitives;
  15. namespace Microsoft.Phone.Controls
  16. {
  17. /// <summary>
  18. /// Provides attached properties to feather FrameworkElements in
  19. /// and out during page transitions. The result is a 'turnstile feather' effect
  20. /// added to the select elements.
  21. /// </summary>
  22. /// <QualityBand>Preview</QualityBand>
  23. public sealed class TurnstileFeatherEffect : DependencyObject
  24. {
  25. /// <summary>
  26. /// The center of rotation on X for elements that are feathered.
  27. /// </summary>
  28. private const double FeatheringCenterOfRotationX = -0.2;
  29. /// <summary>
  30. /// The duration in milliseconds that each element takes
  31. /// to feather forward in.
  32. /// </summary>
  33. private const double ForwardInFeatheringDuration = 350.0;
  34. /// <summary>
  35. /// The initial angle position for an element
  36. /// that feathers forward in.
  37. /// </summary>
  38. private const double ForwardInFeatheringAngle = -80.0;
  39. /// <summary>
  40. /// The delay in milliseconds between each element that
  41. /// feathers forward in.
  42. /// </summary>
  43. private const double ForwardInFeatheringDelay = 40.0;
  44. /// <summary>
  45. /// The duration in milliseconds that each element takes
  46. /// to feather forward out.
  47. /// </summary>
  48. private const double ForwardOutFeatheringDuration = 250.0;
  49. /// <summary>
  50. /// The final angle position for an element
  51. /// that feathers forward out.
  52. /// </summary>
  53. private const double ForwardOutFeatheringAngle = 50.0;
  54. /// <summary>
  55. /// The delay in milliseconds between each element that
  56. /// feathers forward out.
  57. /// </summary>
  58. private const double ForwardOutFeatheringDelay = 50.0;
  59. /// <summary>
  60. /// The duration in milliseconds that each element takes
  61. /// to feather backward in.
  62. /// </summary>
  63. private const double BackwardInFeatheringDuration = 350.0;
  64. /// <summary>
  65. /// The initial angle position for an element
  66. /// that feathers backward in.
  67. /// </summary>
  68. private const double BackwardInFeatheringAngle = 50.0;
  69. /// <summary>
  70. /// The delay in milliseconds between each element that
  71. /// feathers backward in.
  72. /// </summary>
  73. private const double BackwardInFeatheringDelay = 50.0;
  74. /// <summary>
  75. /// The duration in milliseconds that each element takes
  76. /// to feather backward out.
  77. /// </summary>
  78. private const double BackwardOutFeatheringDuration = 250.0;
  79. /// <summary>
  80. /// The initial angle position for an element
  81. /// that feathers backward out.
  82. /// </summary>
  83. private const double BackwardOutFeatheringAngle = -80.0;
  84. /// <summary>
  85. /// The delay in milliseconds between each element that
  86. /// feathers backward out.
  87. /// </summary>
  88. private const double BackwardOutFeatheringDelay = 40.0;
  89. /// <summary>
  90. /// The easing function that defines the exponential inwards
  91. /// interpolation of the storyboards.
  92. /// </summary>
  93. private static readonly ExponentialEase TurnstileFeatheringExponentialEaseIn = new ExponentialEase() { EasingMode = EasingMode.EaseIn, Exponent = 6 };
  94. /// <summary>
  95. /// The easing function that defines the exponential outwards
  96. /// interpolation of the storyboards.
  97. /// </summary>
  98. private static readonly ExponentialEase TurnstileFeatheringExponentialEaseOut = new ExponentialEase() { EasingMode = EasingMode.EaseOut, Exponent = 6 };
  99. /// <summary>
  100. /// The property path used to map the animation's target property
  101. /// to the RotationY property of the plane projection of a UI element.
  102. /// </summary>
  103. private static readonly PropertyPath RotationYPropertyPath = new PropertyPath("(UIElement.Projection).(PlaneProjection.RotationY)");
  104. /// <summary>
  105. /// The property path used to map the animation's target property
  106. /// to the Opacity property of a UI element.
  107. /// </summary>
  108. private static readonly PropertyPath OpacityPropertyPath = new PropertyPath("(UIElement.Opacity)");
  109. /// <summary>
  110. /// A point with coordinate (0, 0).
  111. /// </summary>
  112. private static readonly Point Origin = new Point(0, 0);
  113. /// <summary>
  114. /// Private manager that represents a correlation between pages
  115. /// and the indexed elements it contains.
  116. /// </summary>
  117. private static Dictionary<PhoneApplicationPage, List<WeakReference>> _pagesToReferences = new Dictionary<PhoneApplicationPage, List<WeakReference>>();
  118. /// <summary>
  119. /// Identifies the set of framework elements that are targeted
  120. /// to be feathered.
  121. /// </summary>
  122. private static IList<WeakReference> _featheringTargets;
  123. /// <summary>
  124. /// Indicates whether the targeted framework elements need their
  125. /// projections and transforms to be restored.
  126. /// </summary>
  127. private static bool _pendingRestore;
  128. /// <summary>
  129. /// Default list of types that cannot be feathered.
  130. /// </summary>
  131. private static IList<Type> _nonPermittedTypes = new List<Type>()
  132. {
  133. typeof(PhoneApplicationFrame),
  134. typeof(PhoneApplicationPage),
  135. typeof(PivotItem),
  136. typeof(Panorama),
  137. typeof(PanoramaItem)
  138. };
  139. /// <summary>
  140. /// Default list of types that cannot be feathered.
  141. /// </summary>
  142. public static IList<Type> NonPermittedTypes
  143. {
  144. get { return _nonPermittedTypes; }
  145. }
  146. #region FeatheringIndex DependencyProperty
  147. /// <summary>
  148. /// Gets the feathering index of the specified dependency object.
  149. /// </summary>
  150. /// <param name="obj">The dependency object.</param>
  151. /// <returns>The feathering index.</returns>
  152. [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Standard pattern.")]
  153. public static int GetFeatheringIndex(DependencyObject obj)
  154. {
  155. return (int)obj.GetValue(FeatheringIndexProperty);
  156. }
  157. /// <summary>
  158. /// Sets the feathering index of the specified dependency object.
  159. /// </summary>
  160. /// <param name="obj">The dependency object.</param>
  161. /// <param name="value">The feathering index.</param>
  162. [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Standard pattern.")]
  163. public static void SetFeatheringIndex(DependencyObject obj, int value)
  164. {
  165. obj.SetValue(FeatheringIndexProperty, value);
  166. }
  167. /// <summary>
  168. /// Identifies the feathering index of the current element,
  169. /// which represents its place in the feathering order sequence.
  170. /// </summary>
  171. public static readonly DependencyProperty FeatheringIndexProperty =
  172. DependencyProperty.RegisterAttached("FeatheringIndex", typeof(int), typeof(TurnstileFeatherEffect), new PropertyMetadata(-1, OnFeatheringIndexPropertyChanged));
  173. /// <summary>
  174. /// Subscribes an element to the private manager.
  175. /// </summary>
  176. /// <param name="obj">The event sender.</param>
  177. /// <param name="e">The event arguments.</param>
  178. private static void OnFeatheringIndexPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  179. {
  180. FrameworkElement target = obj as FrameworkElement;
  181. if (target == null)
  182. {
  183. string message = string.Format(CultureInfo.InvariantCulture, "The dependency object must be of the type {0}.", typeof(FrameworkElement));
  184. throw new InvalidOperationException(message);
  185. }
  186. CheckForTypePermission(target);
  187. int index = (int)e.NewValue;
  188. if (index < 0)
  189. {
  190. // Dettach event handlers.
  191. if (TurnstileFeatherEffect.GetHasEventsAttached(target))
  192. {
  193. target.SizeChanged -= Target_SizeChanged;
  194. target.Unloaded -= Target_Unloaded;
  195. TurnstileFeatherEffect.SetHasEventsAttached(target, false);
  196. }
  197. UnsubscribeFrameworkElement(target);
  198. }
  199. else
  200. {
  201. // Attach event handlers.
  202. if (!TurnstileFeatherEffect.GetHasEventsAttached(target))
  203. {
  204. target.SizeChanged += Target_SizeChanged;
  205. target.Unloaded += Target_Unloaded;
  206. TurnstileFeatherEffect.SetHasEventsAttached(target, true);
  207. }
  208. SubscribeFrameworkElement(target);
  209. }
  210. }
  211. #endregion
  212. #region ParentPage DependencyProperty
  213. /// <summary>
  214. /// Gets the parent page of the specified dependency object.
  215. /// </summary>
  216. /// <param name="obj">The dependency object.</param>
  217. /// <returns>The page.</returns>
  218. private static PhoneApplicationPage GetParentPage(DependencyObject obj)
  219. {
  220. return (PhoneApplicationPage)obj.GetValue(ParentPageProperty);
  221. }
  222. /// <summary>
  223. /// Sets the parent page of the specified dependency object.
  224. /// </summary>
  225. /// <param name="obj">The depedency object.</param>
  226. /// <param name="value">The page.</param>
  227. private static void SetParentPage(DependencyObject obj, PhoneApplicationPage value)
  228. {
  229. obj.SetValue(ParentPageProperty, value);
  230. }
  231. /// <summary>
  232. /// Identifies the ParentPage dependency property.
  233. /// </summary>
  234. private static readonly DependencyProperty ParentPageProperty =
  235. DependencyProperty.RegisterAttached("ParentPage", typeof(PhoneApplicationPage), typeof(TurnstileFeatherEffect), new PropertyMetadata(null, OnParentPagePropertyChanged));
  236. /// <summary>
  237. /// Manages subscription to a page.
  238. /// </summary>
  239. /// <param name="obj">The dependency object.</param>
  240. /// <param name="e">The event arguments.</param>
  241. private static void OnParentPagePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  242. {
  243. FrameworkElement target = (FrameworkElement)obj;
  244. PhoneApplicationPage oldPage = (PhoneApplicationPage)e.OldValue;
  245. PhoneApplicationPage newPage = (PhoneApplicationPage)e.NewValue;
  246. List<WeakReference> references;
  247. if (newPage != null)
  248. {
  249. if (!_pagesToReferences.TryGetValue(newPage, out references))
  250. {
  251. references = new List<WeakReference>();
  252. _pagesToReferences.Add(newPage, references);
  253. }
  254. else
  255. {
  256. WeakReferenceHelper.RemoveNullTargetReferences(references);
  257. }
  258. if (!WeakReferenceHelper.ContainsTarget(references, target))
  259. {
  260. references.Add(new WeakReference(target));
  261. }
  262. references.Sort(SortReferencesByIndex);
  263. }
  264. else
  265. {
  266. if(_pagesToReferences.TryGetValue(oldPage, out references))
  267. {
  268. WeakReferenceHelper.TryRemoveTarget(references, target);
  269. if (references.Count == 0)
  270. {
  271. _pagesToReferences.Remove(oldPage);
  272. }
  273. }
  274. }
  275. }
  276. #endregion
  277. #region IsSubscribed DependencyProperty
  278. /// <summary>
  279. /// Gets whether the specified dependency object
  280. /// is subscribed to the private manager or not.
  281. /// </summary>
  282. /// <param name="obj">The dependency object.</param>
  283. /// <returns>The value.</returns>
  284. private static bool GetIsSubscribed(DependencyObject obj)
  285. {
  286. return (bool)obj.GetValue(IsSubscribedProperty);
  287. }
  288. /// <summary>
  289. /// Sets whether the specified dependency object
  290. /// is subscribed to the private manager or not.
  291. /// </summary>
  292. /// <param name="obj">The dependency object.</param>
  293. /// <param name="value">The value.</param>
  294. private static void SetIsSubscribed(DependencyObject obj, bool value)
  295. {
  296. obj.SetValue(IsSubscribedProperty, value);
  297. }
  298. /// <summary>
  299. /// Identifies the IsSubscribed dependency property.
  300. /// </summary>
  301. private static readonly DependencyProperty IsSubscribedProperty =
  302. DependencyProperty.RegisterAttached("IsSubscribed", typeof(bool), typeof(TurnstileFeatherEffect), new PropertyMetadata(false));
  303. #endregion
  304. #region HasEventsAttached DependencyProperty
  305. /// <summary>
  306. /// Gets whether the specified dependency object
  307. /// has events attached to it or not.
  308. /// </summary>
  309. /// <param name="obj">The dependency object.</param>
  310. /// <returns>The value.</returns>
  311. private static bool GetHasEventsAttached(DependencyObject obj)
  312. {
  313. return (bool)obj.GetValue(HasEventsAttachedProperty);
  314. }
  315. /// <summary>
  316. /// Sets whether the specified dependency object
  317. /// has events attached to it or not.
  318. /// </summary>
  319. /// <param name="obj">The dependency object.</param>
  320. /// <param name="value">The value.</param>
  321. private static void SetHasEventsAttached(DependencyObject obj, bool value)
  322. {
  323. obj.SetValue(HasEventsAttachedProperty, value);
  324. }
  325. /// <summary>
  326. /// Identifies the HasEventsAttached dependency property.
  327. /// </summary>
  328. private static readonly DependencyProperty HasEventsAttachedProperty =
  329. DependencyProperty.RegisterAttached("HasEventsAttached", typeof(bool), typeof(TurnstileFeatherEffect), new PropertyMetadata(false));
  330. #endregion
  331. #region OriginalProjection DependencyProperty
  332. /// <summary>
  333. /// Gets the original projection of the specified dependency object
  334. /// after the projection needed to apply the turnstile feather effect
  335. /// has been attached to it.
  336. /// </summary>
  337. /// <param name="obj">The dependency object.</param>
  338. /// <returns>The original projection.</returns>
  339. private static Projection GetOriginalProjection(DependencyObject obj)
  340. {
  341. return (Projection)obj.GetValue(OriginalProjectionProperty);
  342. }
  343. /// <summary>
  344. /// Sets the original projection of the specified dependency object.
  345. /// </summary>
  346. /// <param name="obj">The dependency object.</param>
  347. /// <param name="value">The original projection.</param>
  348. private static void SetOriginalProjection(DependencyObject obj, Projection value)
  349. {
  350. obj.SetValue(OriginalProjectionProperty, value);
  351. }
  352. /// <summary>
  353. /// Identifies the OriginalProjection dependency property.
  354. /// </summary>
  355. private static readonly DependencyProperty OriginalProjectionProperty =
  356. DependencyProperty.RegisterAttached("OriginalProjection", typeof(Projection), typeof(TurnstileFeatherEffect), new PropertyMetadata(null));
  357. #endregion
  358. #region OriginalRenderTransform DependencyProperty
  359. /// <summary>
  360. /// Gets the original render transform of the specified dependency
  361. /// object after the transform needed to apply the turnstile feather
  362. /// effect has been attached to it.
  363. /// </summary>
  364. /// <param name="obj">The dependency object.</param>
  365. /// <returns>The original render transform.</returns>
  366. private static Transform GetOriginalRenderTransform(DependencyObject obj)
  367. {
  368. return (Transform)obj.GetValue(OriginalRenderTransformProperty);
  369. }
  370. /// <summary>
  371. /// Sets the original render transform of the specified
  372. /// dependency object.
  373. /// </summary>
  374. /// <param name="obj">The dependency object.</param>
  375. /// <param name="value">The original render transform.</param>
  376. private static void SetOriginalRenderTransform(DependencyObject obj, Transform value)
  377. {
  378. obj.SetValue(OriginalRenderTransformProperty, value);
  379. }
  380. /// <summary>
  381. /// Identifies the OriginalRenderTransform dependency property.
  382. /// </summary>
  383. private static readonly DependencyProperty OriginalRenderTransformProperty =
  384. DependencyProperty.RegisterAttached("OriginalRenderTransform", typeof(Transform), typeof(TurnstileFeatherEffect), new PropertyMetadata(null));
  385. #endregion
  386. #region OriginalOpacity DependencyProperty
  387. /// <summary>
  388. /// Gets the original opacity of the specified dependency
  389. /// object before the turnstile feather effect is applied to it.
  390. /// </summary>
  391. /// <param name="obj">The dependency object.</param>
  392. /// <returns>The original opacity.</returns>
  393. private static double GetOriginalOpacity(DependencyObject obj)
  394. {
  395. return (double)obj.GetValue(OriginalOpacityProperty);
  396. }
  397. /// <summary>
  398. /// Sets the original opacity of the specified
  399. /// dependency object.
  400. /// </summary>
  401. /// <param name="obj">The dependency object.</param>
  402. /// <param name="value">The original opacity.</param>
  403. private static void SetOriginalOpacity(DependencyObject obj, double value)
  404. {
  405. obj.SetValue(OriginalOpacityProperty, value);
  406. }
  407. /// <summary>
  408. /// Identifies the OriginalOpacity dependency property.
  409. /// </summary>
  410. private static readonly DependencyProperty OriginalOpacityProperty =
  411. DependencyProperty.RegisterAttached("OriginalOpacity", typeof(double), typeof(TurnstileFeatherEffect), new PropertyMetadata(0.0));
  412. #endregion
  413. /// <summary>
  414. /// Called when an element gets resized.
  415. /// </summary>
  416. /// <param name="sender">The event sender.</param>
  417. /// <param name="e">The event information.</param>
  418. /// <remarks>
  419. /// Ideally, the Loaded event should be handled instead of
  420. /// the SizeChanged event. However, the Loaded event does not occur
  421. /// by the time the TransitionFrame tries to animate a forward in transition.
  422. /// Handling the SizeChanged event instead guarantees that
  423. /// the newly created FrameworkElements can be subscribed in time
  424. /// before the transition begins.
  425. /// </remarks>
  426. private static void Target_SizeChanged(object sender, SizeChangedEventArgs e)
  427. {
  428. SubscribeFrameworkElement((FrameworkElement)sender);
  429. }
  430. /// <summary>
  431. /// Called when an element gets unloaded.
  432. /// </summary>
  433. /// <param name="sender">The event sender.</param>
  434. /// <param name="e">The event information.</param>
  435. private static void Target_Unloaded(object sender, RoutedEventArgs e)
  436. {
  437. UnsubscribeFrameworkElement((FrameworkElement)sender);
  438. }
  439. /// <summary>
  440. /// Throws an exception if the object sent as parameter is of a type
  441. /// that is included in the list of non-permitted types.
  442. /// </summary>
  443. /// <param name="obj">The object.</param>
  444. private static void CheckForTypePermission(object obj)
  445. {
  446. Type type = obj.GetType();
  447. if(NonPermittedTypes.Contains(type))
  448. {
  449. string message = string.Format(CultureInfo.InvariantCulture, "Objects of the type {0} cannot be feathered.", type);
  450. throw new InvalidOperationException(message);
  451. }
  452. }
  453. /// <summary>
  454. /// Compares two weak references targeting dependency objects
  455. /// to sort them based on their feathering index.
  456. /// </summary>
  457. /// <param name="x">The first weak reference.</param>
  458. /// <param name="y">The second weak reference.</param>
  459. /// <returns>
  460. /// 0 if both weak references target dependency objects with
  461. /// the same feathering index.
  462. /// 1 if the first reference targets a dependency
  463. /// object with a greater feathering index.
  464. /// -1 if the second reference targets a dependency
  465. /// object with a greater feathering index.
  466. /// </returns>
  467. private static int SortReferencesByIndex(WeakReference x, WeakReference y)
  468. {
  469. DependencyObject targetX = x.Target as DependencyObject;
  470. DependencyObject targetY = y.Target as DependencyObject;
  471. if (targetX == null)
  472. {
  473. if (targetY == null)
  474. {
  475. // If x is null and y is null,
  476. // they're equal.
  477. return 0;
  478. }
  479. else
  480. {
  481. // If x is null and y is not null,
  482. // y is greater.
  483. return -1;
  484. }
  485. }
  486. else
  487. {
  488. // If x is not null and y is null,
  489. // x is greater.
  490. if (targetY == null)
  491. {
  492. return 1;
  493. }
  494. else
  495. {
  496. int xIndex = GetFeatheringIndex(targetX);
  497. int yIndex = GetFeatheringIndex(targetY);
  498. return xIndex.CompareTo(yIndex);
  499. }
  500. }
  501. }
  502. /// <summary>
  503. /// Returns the set of weak references to the items
  504. /// that must be animated.
  505. /// </summary>
  506. /// <returns>
  507. /// A set of weak references to items sorted by their feathering index.
  508. /// </returns>
  509. private static IList<WeakReference> GetTargetsToAnimate()
  510. {
  511. List<WeakReference> references;
  512. List<WeakReference> targets = new List<WeakReference>();
  513. PhoneApplicationPage page = null;
  514. PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
  515. if (frame != null)
  516. {
  517. page = frame.Content as PhoneApplicationPage;
  518. }
  519. if (page == null)
  520. {
  521. return null;
  522. }
  523. if (!_pagesToReferences.TryGetValue(page, out references))
  524. {
  525. return null;
  526. }
  527. foreach (WeakReference r in references)
  528. {
  529. FrameworkElement target = r.Target as FrameworkElement;
  530. // If target is null, skip.
  531. if (target == null)
  532. {
  533. continue;
  534. }
  535. // If target is not on the screen, skip.
  536. if (!IsOnScreen(target))
  537. {
  538. continue;
  539. }
  540. ListBox listBox = r.Target as ListBox;
  541. LongListSelector longListSelector = r.Target as LongListSelector;
  542. Pivot pivot = r.Target as Pivot;
  543. if (listBox != null)
  544. {
  545. // If the target is a ListBox, feather its items individually.
  546. ItemsControlExtensions.GetItemsInViewPort(listBox, targets);
  547. }
  548. else if (longListSelector != null)
  549. {
  550. // If the target is a LongListSelector, feather its items individually.
  551. ListBox child = TemplatedVisualTreeExtensions.GetFirstLogicalChildByType<ListBox>(longListSelector, false);
  552. if (child != null)
  553. {
  554. ItemsControlExtensions.GetItemsInViewPort(child, targets);
  555. }
  556. }
  557. else if (pivot != null)
  558. {
  559. // If the target is a Pivot, feather the title and the headers individually.
  560. ContentPresenter title = TemplatedVisualTreeExtensions.GetFirstLogicalChildByType<ContentPresenter>(pivot, false);
  561. if (title != null)
  562. {
  563. targets.Add(new WeakReference(title));
  564. }
  565. PivotHeadersControl headers = TemplatedVisualTreeExtensions.GetFirstLogicalChildByType<PivotHeadersControl>(pivot, false);
  566. if (headers != null)
  567. {
  568. targets.Add(new WeakReference(headers));
  569. }
  570. }
  571. else
  572. {
  573. // Else, feather the target as a whole.
  574. targets.Add(r);
  575. }
  576. }
  577. return targets;
  578. }
  579. /// <summary>
  580. /// Subscribes an element to the private managers.
  581. /// </summary>
  582. /// <param name="target">The framework element.</param>
  583. private static void SubscribeFrameworkElement(FrameworkElement target)
  584. {
  585. if (!TurnstileFeatherEffect.GetIsSubscribed(target))
  586. {
  587. // Find the parent page.
  588. PhoneApplicationPage page = TemplatedVisualTreeExtensions.GetParentByType<PhoneApplicationPage>(target);
  589. if (page == null)
  590. {
  591. return;
  592. }
  593. TurnstileFeatherEffect.SetParentPage(target, page);
  594. TurnstileFeatherEffect.SetIsSubscribed(target, true);
  595. }
  596. }
  597. /// <summary>
  598. /// Unsubscribes an element from the private manager.
  599. /// </summary>
  600. /// <param name="target">The framework element.</param>
  601. private static void UnsubscribeFrameworkElement(FrameworkElement target)
  602. {
  603. // If element is subscribed, unsubscribe.
  604. if (TurnstileFeatherEffect.GetIsSubscribed(target))
  605. {
  606. TurnstileFeatherEffect.SetParentPage(target, null);
  607. TurnstileFeatherEffect.SetIsSubscribed(target, false);
  608. }
  609. }
  610. /// <summary>
  611. /// Prepares a framework element to be feathered by adding a plane projection
  612. /// and a composite transform to it.
  613. /// </summary>
  614. /// <param name="root">The root visual.</param>
  615. /// <param name="element">The framework element.</param>
  616. private static bool TryAttachProjectionAndTransform(PhoneApplicationFrame root, FrameworkElement element)
  617. {
  618. GeneralTransform generalTransform;
  619. try
  620. {
  621. generalTransform = element.TransformToVisual(root);
  622. }
  623. catch (ArgumentException)
  624. {
  625. return false;
  626. }
  627. Point coordinates = generalTransform.Transform(Origin);
  628. double y = coordinates.Y + element.ActualHeight / 2.0;
  629. double offset = (root.ActualHeight / 2.0) - y;
  630. // Cache original projection and transform.
  631. TurnstileFeatherEffect.SetOriginalProjection(element, element.Projection);
  632. TurnstileFeatherEffect.SetOriginalRenderTransform(element, element.RenderTransform);
  633. // Attach projection.
  634. PlaneProjection projection = new PlaneProjection();
  635. projection.GlobalOffsetY = offset * -1.0;
  636. projection.CenterOfRotationX = FeatheringCenterOfRotationX;
  637. element.Projection = projection;
  638. // Attach transform.
  639. Transform originalTransform = element.RenderTransform;
  640. TranslateTransform translateTransform = new TranslateTransform();
  641. translateTransform.Y = offset;
  642. TransformGroup transformGroup = new TransformGroup();
  643. transformGroup.Children.Add(originalTransform);
  644. transformGroup.Children.Add(translateTransform);
  645. element.RenderTransform = transformGroup;
  646. return true;
  647. }
  648. /// <summary>
  649. /// Restores the original projection and render transform of
  650. /// the targeted framework elements.
  651. /// </summary>
  652. private static void RestoreProjectionsAndTransforms()
  653. {
  654. if (_featheringTargets == null || !_pendingRestore)
  655. {
  656. return;
  657. }
  658. foreach (WeakReference r in _featheringTargets)
  659. {
  660. FrameworkElement element = r.Target as FrameworkElement;
  661. if (element == null)
  662. {
  663. continue;
  664. }
  665. Projection projection = TurnstileFeatherEffect.GetOriginalProjection(element);
  666. Transform transform = TurnstileFeatherEffect.GetOriginalRenderTransform(element);
  667. element.Projection = projection;
  668. element.RenderTransform = transform;
  669. }
  670. _pendingRestore = false;
  671. }
  672. /// <summary>
  673. /// Indicates whether the specified framework element
  674. /// is within the bounds of the application's root visual.
  675. /// </summary>
  676. /// <param name="element">The framework element.</param>
  677. /// <returns>
  678. /// True if the rectangular bounds of the framework element
  679. /// are completely outside the bounds of the application's root visual.
  680. /// </returns>
  681. private static bool IsOnScreen(FrameworkElement element)
  682. {
  683. PhoneApplicationFrame root = Application.Current.RootVisual as PhoneApplicationFrame;
  684. if (root == null)
  685. {
  686. return false;
  687. }
  688. GeneralTransform generalTransform;
  689. double height = root.ActualHeight;
  690. double width = root.ActualWidth;
  691. try
  692. {
  693. generalTransform = element.TransformToVisual(root);
  694. }
  695. catch (ArgumentException)
  696. {
  697. return false;
  698. }
  699. Rect bounds = new Rect(
  700. generalTransform.Transform(Origin),
  701. generalTransform.Transform(new Point(element.ActualWidth, element.ActualHeight)));
  702. bool isParentTransparent = false;
  703. IList<FrameworkElement> ancestors = element.GetVisualAncestors().ToList();
  704. if (ancestors != null)
  705. {
  706. for (int i = 0; i < ancestors.Count; i++)
  707. {
  708. if (ancestors[i].Opacity <= 0.001)
  709. {
  710. isParentTransparent = true;
  711. break;
  712. }
  713. }
  714. }
  715. return (bounds.Bottom > 0) && (bounds.Top < height)
  716. && (bounds.Right > 0) && (bounds.Left < width)
  717. && !isParentTransparent;
  718. }
  719. /// <summary>
  720. /// Adds a set of animations corresponding to the
  721. /// turnstile feather forward in effect.
  722. /// </summary>
  723. /// <param name="storyboard">
  724. /// The storyboard where the animations
  725. /// will be added.
  726. /// </param>
  727. private static void ComposeForwardInStoryboard(Storyboard storyboard)
  728. {
  729. int counter = 0;
  730. PhoneApplicationFrame root = Application.Current.RootVisual as PhoneApplicationFrame;
  731. foreach (WeakReference r in _featheringTargets)
  732. {
  733. FrameworkElement element = (FrameworkElement)r.Target;
  734. double originalOpacity = element.Opacity;
  735. TurnstileFeatherEffect.SetOriginalOpacity(element, originalOpacity);
  736. // Hide the element until the storyboard is begun.
  737. element.Opacity = 0.0;
  738. if(!TryAttachProjectionAndTransform(root, element))
  739. {
  740. continue;
  741. }
  742. DoubleAnimation doubleAnimation = new DoubleAnimation()
  743. {
  744. Duration = TimeSpan.FromMilliseconds(ForwardInFeatheringDuration),
  745. From = ForwardInFeatheringAngle,
  746. To = 0.0,
  747. BeginTime = TimeSpan.FromMilliseconds(ForwardInFeatheringDelay * counter),
  748. EasingFunction = TurnstileFeatheringExponentialEaseOut
  749. };
  750. Storyboard.SetTarget(doubleAnimation, element);
  751. Storyboard.SetTargetProperty(doubleAnimation, RotationYPropertyPath);
  752. storyboard.Children.Add(doubleAnimation);
  753. doubleAnimation = new DoubleAnimation()
  754. {
  755. Duration = TimeSpan.Zero,
  756. From = 0.0,
  757. To = TurnstileFeatherEffect.GetOriginalOpacity(element),
  758. BeginTime = TimeSpan.FromMilliseconds(ForwardInFeatheringDelay * counter)
  759. };
  760. Storyboard.SetTarget(doubleAnimation, element);
  761. Storyboard.SetTargetProperty(doubleAnimation, OpacityPropertyPath);
  762. storyboard.Children.Add(doubleAnimation);
  763. counter++;
  764. }
  765. }
  766. /// <summary>
  767. /// Adds a set of animations corresponding to the
  768. /// turnstile feather forward out effect.
  769. /// </summary>
  770. /// <param name="storyboard">
  771. /// The storyboard where the animations
  772. /// will be added.
  773. /// </param>
  774. private static void ComposeForwardOutStoryboard(Storyboard storyboard)
  775. {
  776. int counter = 0;
  777. PhoneApplicationFrame root = Application.Current.RootVisual as PhoneApplicationFrame;
  778. foreach (WeakReference r in _featheringTargets)
  779. {
  780. FrameworkElement element = (FrameworkElement)r.Target;
  781. double originalOpacity = element.Opacity;
  782. TurnstileFeatherEffect.SetOriginalOpacity(element, originalOpacity);
  783. if (!TryAttachProjectionAndTransform(root, element))
  784. {
  785. continue;
  786. }
  787. DoubleAnimation doubleAnimation = new DoubleAnimation()
  788. {
  789. Duration = TimeSpan.FromMilliseconds(ForwardOutFeatheringDuration),
  790. From = 0.0,
  791. To = ForwardOutFeatheringAngle,
  792. BeginTime = TimeSpan.FromMilliseconds(ForwardOutFeatheringDelay * counter),
  793. EasingFunction = TurnstileFeatheringExponentialEaseIn
  794. };
  795. Storyboard.SetTarget(doubleAnimation, element);
  796. Storyboard.SetTargetProperty(doubleAnimation, RotationYPropertyPath);
  797. storyboard.Children.Add(doubleAnimation);
  798. doubleAnimation = new DoubleAnimation()
  799. {
  800. Duration = TimeSpan.Zero,
  801. From = originalOpacity,
  802. To = 0.0,
  803. BeginTime = TimeSpan.FromMilliseconds(ForwardOutFeatheringDelay * counter + ForwardOutFeatheringDuration)
  804. };
  805. Storyboard.SetTarget(doubleAnimation, element);
  806. Storyboard.SetTargetProperty(doubleAnimation, OpacityPropertyPath);
  807. storyboard.Children.Add(doubleAnimation);
  808. counter++;
  809. }
  810. }
  811. /// <summary>
  812. /// Adds a set of animations corresponding to the
  813. /// turnstile feather backward in effect.
  814. /// </summary>
  815. /// <param name="storyboard">
  816. /// The storyboard where the animations
  817. /// will be added.
  818. /// </param>
  819. private static void ComposeBackwardInStoryboard(Storyboard storyboard)
  820. {
  821. int counter = 0;
  822. PhoneApplicationFrame root = Application.Current.RootVisual as PhoneApplicationFrame;
  823. foreach (WeakReference r in _featheringTargets)
  824. {
  825. FrameworkElement element = (FrameworkElement)r.Target;
  826. double originalOpacity = element.Opacity;
  827. TurnstileFeatherEffect.SetOriginalOpacity(element, originalOpacity);
  828. // Hide the element until the storyboard is begun.
  829. element.Opacity = 0.0;
  830. if (!TryAttachProjectionAndTransform(root, element))
  831. {
  832. continue;
  833. }
  834. DoubleAnimation doubleAnimation = new DoubleAnimation()
  835. {
  836. Duration = TimeSpan.FromMilliseconds(BackwardInFeatheringDuration),
  837. From = BackwardInFeatheringAngle,
  838. To = 0.0,
  839. BeginTime = TimeSpan.FromMilliseconds(BackwardInFeatheringDelay * counter),
  840. EasingFunction = TurnstileFeatheringExponentialEaseOut
  841. };
  842. Storyboard.SetTarget(doubleAnimation, element);
  843. Storyboard.SetTargetProperty(doubleAnimation, RotationYPropertyPath);
  844. storyboard.Children.Add(doubleAnimation);
  845. doubleAnimation = new DoubleAnimation()
  846. {
  847. Duration = TimeSpan.Zero,
  848. From = 0.0,
  849. To = originalOpacity,
  850. BeginTime = TimeSpan.FromMilliseconds(BackwardInFeatheringDelay * counter)
  851. };
  852. Storyboard.SetTarget(doubleAnimation, element);
  853. Storyboard.SetTargetProperty(doubleAnimation, OpacityPropertyPath);
  854. storyboard.Children.Add(doubleAnimation);
  855. counter++;
  856. }
  857. }
  858. /// <summary>
  859. /// Adds a set of animations corresponding to the
  860. /// turnstile feather backward out effect.
  861. /// </summary>
  862. /// <param name="storyboard">
  863. /// The storyboard where the animations
  864. /// will be added.
  865. /// </param>
  866. private static void ComposeBackwardOutStoryboard(Storyboard storyboard)
  867. {
  868. int counter = 0;
  869. PhoneApplicationFrame root = Application.Current.RootVisual as PhoneApplicationFrame;
  870. foreach (WeakReference r in _featheringTargets)
  871. {
  872. FrameworkElement element = (FrameworkElement)r.Target;
  873. double originalOpacity = element.Opacity;
  874. TurnstileFeatherEffect.SetOriginalOpacity(element, originalOpacity);
  875. if (!TryAttachProjectionAndTransform(root, element))
  876. {
  877. continue;
  878. }
  879. DoubleAnimation doubleAnimation = new DoubleAnimation()
  880. {
  881. Duration = TimeSpan.FromMilliseconds(BackwardOutFeatheringDuration),
  882. From = 0.0,
  883. To = BackwardOutFeatheringAngle,
  884. BeginTime = TimeSpan.FromMilliseconds(BackwardOutFeatheringDelay * counter),
  885. EasingFunction = TurnstileFeatheringExponentialEaseIn
  886. };
  887. Storyboard.SetTarget(doubleAnimation, element);
  888. Storyboard.SetTargetProperty(doubleAnimation, RotationYPropertyPath);
  889. storyboard.Children.Add(doubleAnimation);
  890. doubleAnimation = new DoubleAnimation()
  891. {
  892. Duration = TimeSpan.Zero,
  893. From = originalOpacity,
  894. To = 0.0,
  895. BeginTime = TimeSpan.FromMilliseconds((BackwardOutFeatheringDelay * counter) + BackwardOutFeatheringDuration)
  896. };
  897. Storyboard.SetTarget(doubleAnimation, element);
  898. Storyboard.SetTargetProperty(doubleAnimation, OpacityPropertyPath);
  899. storyboard.Children.Add(doubleAnimation);
  900. counter++;
  901. }
  902. }
  903. /// <summary>
  904. /// Adds a set of animations corresponding to the
  905. /// turnstile feather effect.
  906. /// </summary>
  907. /// <param name="storyboard">
  908. /// The storyboard where the animations
  909. /// will be added.</param>
  910. /// <param name="beginTime">
  911. /// The time at which the storyboard should begin.</param>
  912. /// <param name="mode">
  913. /// The mode of the turnstile feather effect.
  914. /// </param>
  915. internal static void ComposeStoryboard(Storyboard storyboard, TimeSpan? beginTime, TurnstileFeatherTransitionMode mode)
  916. {
  917. RestoreProjectionsAndTransforms();
  918. _featheringTargets = GetTargetsToAnimate();
  919. if (_featheringTargets == null)
  920. {
  921. return;
  922. }
  923. _pendingRestore = true;
  924. switch (mode)
  925. {
  926. case TurnstileFeatherTransitionMode.ForwardIn:
  927. ComposeForwardInStoryboard(storyboard);
  928. break;
  929. case TurnstileFeatherTransitionMode.ForwardOut:
  930. ComposeForwardOutStoryboard(storyboard);
  931. break;
  932. case TurnstileFeatherTransitionMode.BackwardIn:
  933. ComposeBackwardInStoryboard(storyboard);
  934. break;
  935. case TurnstileFeatherTransitionMode.BackwardOut:
  936. ComposeBackwardOutStoryboard(storyboard);
  937. break;
  938. default:
  939. break;
  940. }
  941. storyboard.BeginTime = beginTime;
  942. storyboard.Completed += (s, e) =>
  943. {
  944. foreach (WeakReference r in _featheringTargets)
  945. {
  946. FrameworkElement element = (FrameworkElement)r.Target;
  947. double originalOpacity = TurnstileFeatherEffect.GetOriginalOpacity(element);
  948. element.Opacity = originalOpacity;
  949. }
  950. RestoreProjectionsAndTransforms();
  951. };
  952. }
  953. }
  954. }