From 78be87a14eafa831bb1ab12b453bf86afe6bcdda Mon Sep 17 00:00:00 2001 From: Zhao Cheng Date: Sat, 3 Mar 2012 19:17:38 +0800 Subject: [PATCH] =?UTF-8?q?=E7=82=B9=E5=87=BB=E6=A0=87=E9=A2=98=E5=88=B7?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Images/circle.png | Bin 0 -> 849 bytes MainPage.xaml | 28 +- MainPage.xaml.cs | 14 + MetroInMotion/LinqToVisualTree.cs | 387 ++++++++++++++++++++++ MetroInMotion/MetroInMotion.cs | 524 ++++++++++++++++++++++++++++++ sbbs-client-wp7.csproj | 3 + 6 files changed, 954 insertions(+), 2 deletions(-) create mode 100644 Images/circle.png create mode 100644 MetroInMotion/LinqToVisualTree.cs create mode 100644 MetroInMotion/MetroInMotion.cs diff --git a/Images/circle.png b/Images/circle.png new file mode 100644 index 0000000000000000000000000000000000000000..0f6fb460b61891aea2b4170595ffc78b789e4d49 GIT binary patch literal 849 zcmV-X1FrmuP)mAED4vsVF`5AQ82Y zLrCAijC?+4WOjEpnf1Y!U3Rl?zHjHv?CyIrH5!cq$5tXluoX5W9KqI^-ErXK0ijN) z5*}J!^N#SA@QQGrs&&x=32>M2gm5-WZ)eiiy(C-C z&7A)tv>XuUXi48_?KA=+!X9&YOekkSUP1WovNrc5m)HXMo$q8woTFpg6aR{Nw2d*6 zSJ+@2R%``S*nsU!fV{3JZgjJg?g4edVm_EKj)BjGnvH z-A>@IA-BikM+tZYCtiCj;0^3Ai68MjP@wG(dGIG;I`G>B*Mi)i7Co>4Cwqbah8!1! zfH^quIR^qh!T!7uFbfC1sU!e=}e(1%`_2_@+sbWh1+@+BbOQz0bm0RB|-SO9W6z|>-H zU#D~bK@&m&VjH@r?P2&Jr|sXF@BiteUYAx`^nm=LZCXW_*y?&9pc^N#rBaInxM+8R zw=&$k#L!`DzY`*EB`Gf$dF-TV_4gltr&nlv%Gd{!ZhRS(6mHTZE^ELifa~H-J67$<5+4SmIbU5E*&g2xRk!ew$#A1*h3^+E0))MlsVYci#=QjK#MGw+YDVb bcq_mFnhtL@t5esb00000NkvXXu0mjfsG@ + + @@ -75,7 +93,10 @@ --> - + + + + - + + + + + /// Adapts a DependencyObject to provide methods required for generate + /// a Linq To Tree API + /// + public class VisualTreeAdapter : ILinqTree + { + private DependencyObject _item; + + public VisualTreeAdapter(DependencyObject item) + { + _item = item; + } + + public IEnumerable Children() + { + int childrenCount = VisualTreeHelper.GetChildrenCount(_item); + for (int i = 0; i < childrenCount; i++) + { + yield return VisualTreeHelper.GetChild(_item, i); + } + } + + public DependencyObject Parent + { + get + { + return VisualTreeHelper.GetParent(_item); + } + } + } +} + +namespace LinqToVisualTree +{ + /// + /// Defines an interface that must be implemented to generate the LinqToTree methods + /// + /// + public interface ILinqTree + { + IEnumerable Children(); + + T Parent { get; } + } + + public static class TreeExtensions + { + /// + /// Returns a collection of descendant elements. + /// + public static IEnumerable Descendants(this DependencyObject item) + { + ILinqTree adapter = new VisualTreeAdapter(item); + foreach (var child in adapter.Children()) + { + yield return child; + + foreach (var grandChild in child.Descendants()) + { + yield return grandChild; + } + } + } + + /// + /// Returns a collection containing this element and all descendant elements. + /// + public static IEnumerable DescendantsAndSelf(this DependencyObject item) + { + yield return item; + + foreach (var child in item.Descendants()) + { + yield return child; + } + } + + /// + /// Returns a collection of ancestor elements. + /// + public static IEnumerable Ancestors(this DependencyObject item) + { + ILinqTree adapter = new VisualTreeAdapter(item); + + var parent = adapter.Parent; + while (parent != null) + { + yield return parent; + adapter = new VisualTreeAdapter(parent); + parent = adapter.Parent; + } + } + + /// + /// Returns a collection containing this element and all ancestor elements. + /// + public static IEnumerable AncestorsAndSelf(this DependencyObject item) + { + yield return item; + + foreach (var ancestor in item.Ancestors()) + { + yield return ancestor; + } + } + + /// + /// Returns a collection of child elements. + /// + public static IEnumerable Elements(this DependencyObject item) + { + ILinqTree adapter = new VisualTreeAdapter(item); + foreach (var child in adapter.Children()) + { + yield return child; + } + } + + /// + /// Returns a collection of the sibling elements before this node, in document order. + /// + public static IEnumerable ElementsBeforeSelf(this DependencyObject item) + { + if (item.Ancestors().FirstOrDefault() == null) + yield break; + foreach (var child in item.Ancestors().First().Elements()) + { + if (child.Equals(item)) + break; + yield return child; + } + } + + /// + /// Returns a collection of the after elements after this node, in document order. + /// + public static IEnumerable ElementsAfterSelf(this DependencyObject item) + { + if (item.Ancestors().FirstOrDefault() == null) + yield break; + bool afterSelf = false; + foreach (var child in item.Ancestors().First().Elements()) + { + if (afterSelf) + yield return child; + + if (child.Equals(item)) + afterSelf = true; + } + } + + /// + /// Returns a collection containing this element and all child elements. + /// + public static IEnumerable ElementsAndSelf(this DependencyObject item) + { + yield return item; + + foreach (var child in item.Elements()) + { + yield return child; + } + } + + /// + /// Returns a collection of descendant elements which match the given type. + /// + public static IEnumerable Descendants(this DependencyObject item) + { + return item.Descendants().Where(i => i is T).Cast(); + } + + /// + /// Returns a collection of the sibling elements before this node, in document order + /// which match the given type. + /// + public static IEnumerable ElementsBeforeSelf(this DependencyObject item) + { + return item.ElementsBeforeSelf().Where(i => i is T).Cast(); + } + + /// + /// Returns a collection of the after elements after this node, in document order + /// which match the given type. + /// + public static IEnumerable ElementsAfterSelf(this DependencyObject item) + { + return item.ElementsAfterSelf().Where(i => i is T).Cast(); + } + + /// + /// Returns a collection containing this element and all descendant elements + /// which match the given type. + /// + public static IEnumerable DescendantsAndSelf(this DependencyObject item) + { + return item.DescendantsAndSelf().Where(i => i is T).Cast(); + } + + /// + /// Returns a collection of ancestor elements which match the given type. + /// + public static IEnumerable Ancestors(this DependencyObject item) + { + return item.Ancestors().Where(i => i is T).Cast(); + } + + /// + /// Returns a collection containing this element and all ancestor elements + /// which match the given type. + /// + public static IEnumerable AncestorsAndSelf(this DependencyObject item) + { + return item.AncestorsAndSelf().Where(i => i is T).Cast(); + } + + /// + /// Returns a collection of child elements which match the given type. + /// + public static IEnumerable Elements(this DependencyObject item) + { + return item.Elements().Where(i => i is T).Cast(); + } + + /// + /// Returns a collection containing this element and all child elements. + /// which match the given type. + /// + public static IEnumerable ElementsAndSelf(this DependencyObject item) + { + return item.ElementsAndSelf().Where(i => i is T).Cast(); + } + + } + + public static class EnumerableTreeExtensions + { + /// + /// Applies the given function to each of the items in the supplied + /// IEnumerable. + /// + private static IEnumerable DrillDown(this IEnumerable items, + Func> function) + { + foreach (var item in items) + { + foreach (var itemChild in function(item)) + { + yield return itemChild; + } + } + } + + /// + /// Applies the given function to each of the items in the supplied + /// IEnumerable, which match the given type. + /// + public static IEnumerable DrillDown(this IEnumerable items, + Func> function) + where T : DependencyObject + { + foreach (var item in items) + { + foreach (var itemChild in function(item)) + { + if (itemChild is T) + { + yield return (T)itemChild; + } + } + } + } + + /// + /// Returns a collection of descendant elements. + /// + public static IEnumerable Descendants(this IEnumerable items) + { + return items.DrillDown(i => i.Descendants()); + } + + /// + /// Returns a collection containing this element and all descendant elements. + /// + public static IEnumerable DescendantsAndSelf(this IEnumerable items) + { + return items.DrillDown(i => i.DescendantsAndSelf()); + } + + /// + /// Returns a collection of ancestor elements. + /// + public static IEnumerable Ancestors(this IEnumerable items) + { + return items.DrillDown(i => i.Ancestors()); + } + + /// + /// Returns a collection containing this element and all ancestor elements. + /// + public static IEnumerable AncestorsAndSelf(this IEnumerable items) + { + return items.DrillDown(i => i.AncestorsAndSelf()); + } + + /// + /// Returns a collection of child elements. + /// + public static IEnumerable Elements(this IEnumerable items) + { + return items.DrillDown(i => i.Elements()); + } + + /// + /// Returns a collection containing this element and all child elements. + /// + public static IEnumerable ElementsAndSelf(this IEnumerable items) + { + return items.DrillDown(i => i.ElementsAndSelf()); + } + + /// + /// Returns a collection of descendant elements which match the given type. + /// + public static IEnumerable Descendants(this IEnumerable items) + where T : DependencyObject + { + return items.DrillDown(i => i.Descendants()); + } + + /// + /// Returns a collection containing this element and all descendant elements. + /// which match the given type. + /// + public static IEnumerable DescendantsAndSelf(this IEnumerable items) + where T : DependencyObject + { + return items.DrillDown(i => i.DescendantsAndSelf()); + } + + /// + /// Returns a collection of ancestor elements which match the given type. + /// + public static IEnumerable Ancestors(this IEnumerable items) + where T : DependencyObject + { + return items.DrillDown(i => i.Ancestors()); + } + + /// + /// Returns a collection containing this element and all ancestor elements. + /// which match the given type. + /// + public static IEnumerable AncestorsAndSelf(this IEnumerable items) + where T : DependencyObject + { + return items.DrillDown(i => i.AncestorsAndSelf()); + } + + /// + /// Returns a collection of child elements which match the given type. + /// + public static IEnumerable Elements(this IEnumerable items) + where T : DependencyObject + { + return items.DrillDown(i => i.Elements()); + } + + /// + /// Returns a collection containing this element and all child elements. + /// which match the given type. + /// + public static IEnumerable ElementsAndSelf(this IEnumerable items) + where T : DependencyObject + { + return items.DrillDown(i => i.ElementsAndSelf()); + } + } +} \ No newline at end of file diff --git a/MetroInMotion/MetroInMotion.cs b/MetroInMotion/MetroInMotion.cs new file mode 100644 index 0000000..2249609 --- /dev/null +++ b/MetroInMotion/MetroInMotion.cs @@ -0,0 +1,524 @@ +using System; +using System.Net; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Microsoft.Phone.Controls; +using LinqToVisualTree; +using System.Diagnostics; +using System.Collections.Generic; +using System.Windows.Controls.Primitives; +using System.Windows.Media.Imaging; + +namespace MetroInMotionUtils +{ + public static class MetroInMotion + { + #region AnimationLevel + + public static int GetAnimationLevel(DependencyObject obj) + { + return (int)obj.GetValue(AnimationLevelProperty); + } + + public static void SetAnimationLevel(DependencyObject obj, int value) + { + obj.SetValue(AnimationLevelProperty, value); + } + + + public static readonly DependencyProperty AnimationLevelProperty = + DependencyProperty.RegisterAttached("AnimationLevel", typeof(int), + typeof(MetroInMotion), new PropertyMetadata(-1)); + + #endregion + + #region Tilt + + public static double GetTilt(DependencyObject obj) + { + return (double)obj.GetValue(TiltProperty); + } + + public static void SetTilt(DependencyObject obj, double value) + { + obj.SetValue(TiltProperty, value); + } + + + public static readonly DependencyProperty TiltProperty = + DependencyProperty.RegisterAttached("Tilt", typeof(double), + typeof(MetroInMotion), new PropertyMetadata(2.0, OnTiltChanged)); + + /// + /// The extent of the tilt action, the larger the number, the bigger the tilt + /// + private static double TiltAngleFactor = 4; + + /// + /// The extent of the scaling action, the smaller the number, the greater the scaling. + /// + private static double ScaleFactor = 100; + + private static void OnTiltChanged(DependencyObject d, + DependencyPropertyChangedEventArgs args) + { + FrameworkElement targetElement = d as FrameworkElement; + + double tiltFactor = GetTilt(d); + + // create the required transformations + var projection = new PlaneProjection(); + var scale = new ScaleTransform(); + var translate = new TranslateTransform(); + + var transGroup = new TransformGroup(); + transGroup.Children.Add(scale); + transGroup.Children.Add(translate); + + // associate with the target element + targetElement.Projection = projection; + targetElement.RenderTransform = transGroup; + targetElement.RenderTransformOrigin = new Point(0.5, 0.5); + + targetElement.MouseLeftButtonDown += (s, e) => + { + var clickPosition = e.GetPosition(targetElement); + + // find the maximum of width / height + double maxDimension = Math.Max(targetElement.ActualWidth, targetElement.ActualHeight); + + // compute the normalised horizontal distance from the centre + double distanceFromCenterX = targetElement.ActualWidth / 2 - clickPosition.X; + double normalisedDistanceX = 2 * distanceFromCenterX / maxDimension; + + // rotate around the Y axis + projection.RotationY = normalisedDistanceX * TiltAngleFactor * tiltFactor; + + // compute the normalised vertical distance from the centre + double distanceFromCenterY = targetElement.ActualHeight / 2 - clickPosition.Y; + double normalisedDistanceY = 2 * distanceFromCenterY / maxDimension; + + // rotate around the X axis, + projection.RotationX = -normalisedDistanceY * TiltAngleFactor * tiltFactor; + + // find the distance to centre + double distanceToCentre = Math.Sqrt(normalisedDistanceX * normalisedDistanceX + + normalisedDistanceY * normalisedDistanceY); + + // scale accordingly + double scaleVal = tiltFactor * (1 - distanceToCentre) / ScaleFactor; + scale.ScaleX = 1 - scaleVal; + scale.ScaleY = 1 - scaleVal; + + // offset the plane transform + var rootElement = Application.Current.RootVisual as FrameworkElement; + var relativeToCentre = (targetElement.GetRelativePosition(rootElement).Y - rootElement.ActualHeight / 2) / 2; + translate.Y = -relativeToCentre; + projection.LocalOffsetY = +relativeToCentre; + + }; + + targetElement.ManipulationCompleted += (s, e) => + { + var sb = new Storyboard(); + sb.Children.Add(CreateAnimation(null, 0, 0.1, "RotationY", projection)); + sb.Children.Add(CreateAnimation(null, 0, 0.1, "RotationX", projection)); + sb.Children.Add(CreateAnimation(null, 1, 0.1, "ScaleX", scale)); + sb.Children.Add(CreateAnimation(null, 1, 0.1, "ScaleY", scale)); + sb.Begin(); + + translate.Y = 0; + projection.LocalOffsetY = 0; + }; + + } + + + #endregion + + #region IsPivotAnimated + + public static bool GetIsPivotAnimated(DependencyObject obj) + { + return (bool)obj.GetValue(IsPivotAnimatedProperty); + } + + public static void SetIsPivotAnimated(DependencyObject obj, bool value) + { + obj.SetValue(IsPivotAnimatedProperty, value); + } + + public static readonly DependencyProperty IsPivotAnimatedProperty = + DependencyProperty.RegisterAttached("IsPivotAnimated", typeof(bool), + typeof(MetroInMotion), new PropertyMetadata(false, OnIsPivotAnimatedChanged)); + + private static void OnIsPivotAnimatedChanged(DependencyObject d, DependencyPropertyChangedEventArgs args) + { + ItemsControl list = d as ItemsControl; + + list.Loaded += (s2, e2) => + { + // locate the pivot control that this list is within + Pivot pivot = list.Ancestors().Single() as Pivot; + + // and its index within the pivot + int pivotIndex = pivot.Items.IndexOf(list.Ancestors().Single()); + + bool selectionChanged = false; + + pivot.SelectionChanged += (s3, e3) => + { + selectionChanged = true; + }; + + // handle manipulation events which occur when the user + // moves between pivot items + pivot.ManipulationCompleted += (s, e) => + { + if (!selectionChanged) + return; + + selectionChanged = false; + + if (pivotIndex != pivot.SelectedIndex) + return; + + // determine which direction this tab will be scrolling in from + bool fromRight = e.TotalManipulation.Translation.X <= 0; + + + // iterate over each of the items in view + var items = list.GetItemsInView().ToList(); + for (int index = 0; index < items.Count; index++ ) + { + var lbi = items[index]; + + list.Dispatcher.BeginInvoke(() => + { + var animationTargets = lbi.Descendants() + .Where(p => MetroInMotion.GetAnimationLevel(p) > -1); + foreach (FrameworkElement target in animationTargets) + { + // trigger the required animation + GetSlideAnimation(target, fromRight).Begin(); + } + }); + }; + + }; + }; + } + + + #endregion + + /// + /// Animates each element in order, creating a 'peel' effect. The supplied action + /// is invoked when the animation ends. + /// + public static void Peel(this IEnumerable elements, Action endAction) + { + var elementList = elements.ToList(); + var lastElement = elementList.Last(); + + // iterate over all the elements, animating each of them + double delay = 0; + foreach (FrameworkElement element in elementList) + { + var sb = GetPeelAnimation(element, delay); + + // add a Completed event handler to the last element + if (element.Equals(lastElement)) + { + sb.Completed += (s, e) => + { + endAction(); + }; + } + + sb.Begin(); + delay += 50; + } + } + + + /// + /// Enumerates all the items that are currently visible in am ItemsControl. This implementation assumes + /// that a VirtualizingStackPanel is being used as the ItemsPanel. + /// + public static IEnumerable GetItemsInView(this ItemsControl itemsControl) + { + // locate the stack panel that hosts the items + VirtualizingStackPanel vsp = itemsControl.Descendants().First() as VirtualizingStackPanel; + + // iterate over each of the items in view + int firstVisibleItem = (int)vsp.VerticalOffset; + int visibleItemCount = (int)vsp.ViewportHeight; + for (int index = firstVisibleItem; index <= firstVisibleItem + visibleItemCount + 1; index++) + { + var item = itemsControl.ItemContainerGenerator.ContainerFromIndex(index) as FrameworkElement; + if (item == null) + continue; + + yield return item; + } + } + + /// + /// Creates a PlaneProjection and associates it with the given element, returning + /// a Storyboard which will animate the PlaneProjection to 'peel' the item + /// from the screen. + /// + private static Storyboard GetPeelAnimation(FrameworkElement element, double delay) + { + Storyboard sb; + + var projection = new PlaneProjection() + { + CenterOfRotationX = -0.1 + }; + element.Projection = projection; + + // compute the angle of rotation required to make this element appear + // at a 90 degree angle at the edge of the screen. + var width = element.ActualWidth; + var targetAngle = Math.Atan(1000 / (width / 2)); + targetAngle = targetAngle * 180 / Math.PI; + + // animate the projection + sb = new Storyboard(); + sb.BeginTime = TimeSpan.FromMilliseconds(delay); + sb.Children.Add(CreateAnimation(0, -(180 - targetAngle), 0.3, "RotationY", projection)); + sb.Children.Add(CreateAnimation(0, 23, 0.3, "RotationZ", projection)); + sb.Children.Add(CreateAnimation(0, -23, 0.3, "GlobalOffsetZ", projection)); + return sb; + } + + private static DoubleAnimation CreateAnimation(double? from, double? to, double duration, + string targetProperty, DependencyObject target) + { + var db = new DoubleAnimation(); + db.To = to; + db.From = from; + db.EasingFunction = new SineEase(); + db.Duration = TimeSpan.FromSeconds(duration); + Storyboard.SetTarget(db, target); + Storyboard.SetTargetProperty(db, new PropertyPath(targetProperty)); + return db; + } + + /// + /// Creates a TranslateTransform and associates it with the given element, returning + /// a Storyboard which will animate the TranslateTransform with a SineEase function + /// + private static Storyboard GetSlideAnimation(FrameworkElement element, bool fromRight) + { + double from = fromRight ? 80 : -80; + + Storyboard sb; + double delay = (MetroInMotion.GetAnimationLevel(element)) * 0.1 + 0.1; + + TranslateTransform trans = new TranslateTransform() { X = from }; + element.RenderTransform = trans; + + sb = new Storyboard(); + sb.BeginTime = TimeSpan.FromSeconds(delay); + sb.Children.Add(CreateAnimation(from, 0, 0.8, "X", trans)); + return sb; + } + + } + + public static class ExtensionMethods + { + public static Point GetRelativePosition(this UIElement element, UIElement other) + { + return element.TransformToVisual(other) + .Transform(new Point(0, 0)); + } + } + + /// + /// Animates an element so that it flies out and flies in! + /// + public class ItemFlyInAndOutAnimations + { + private Popup _popup; + + private Canvas _popupCanvas; + + private FrameworkElement _targetElement; + + private Point _targetElementPosition; + + private Image _targetElementClone; + + private Rectangle _backgroundMask; + + private static TimeSpan _flyInSpeed = TimeSpan.FromMilliseconds(200); + + private static TimeSpan _flyOutSpeed = TimeSpan.FromMilliseconds(300); + + public ItemFlyInAndOutAnimations() + { + // construct a popup, with a Canvas as its child + _popup = new Popup(); + _popupCanvas = new Canvas(); + _popup.Child = _popupCanvas; + } + + public static void TitleFlyIn(FrameworkElement title) + { + TranslateTransform trans = new TranslateTransform(); + trans.X = 300; + trans.Y = -50; + title.RenderTransform = trans; + + var sb = new Storyboard(); + + // animate the X position + var db = CreateDoubleAnimation(300, 0, + new SineEase(), trans, TranslateTransform.XProperty, _flyInSpeed); + sb.Children.Add(db); + + // animate the Y position + db = CreateDoubleAnimation(-100, 0, + new SineEase(), trans, TranslateTransform.YProperty, _flyInSpeed); + sb.Children.Add(db); + + sb.Begin(); + } + + /// + /// Animate the previously 'flown-out' element back to its original location. + /// + public void ItemFlyIn() + { + if (_popupCanvas.Children.Count != 2) + return; + + _popup.IsOpen = true; + _backgroundMask.Opacity = 0.0; + + Image animatedImage = _popupCanvas.Children[1] as Image; + + var sb = new Storyboard(); + + // animate the X position + var db = CreateDoubleAnimation(_targetElementPosition.X - 100, _targetElementPosition.X, + new SineEase(), + _targetElementClone, Canvas.LeftProperty, _flyInSpeed); + sb.Children.Add(db); + + // animate the Y position + db = CreateDoubleAnimation(_targetElementPosition.Y - 50, _targetElementPosition.Y, + new SineEase(), + _targetElementClone, Canvas.TopProperty, _flyInSpeed); + sb.Children.Add(db); + + sb.Completed += (s, e) => + { + // when the animation has finished, hide the popup once more + _popup.IsOpen = false; + + // restore the element we have animated + _targetElement.Opacity = 1.0; + + // and get rid of our clone + _popupCanvas.Children.Clear(); + }; + + sb.Begin(); + } + + + /// + /// Animate the given element so that it flies off screen, fading + /// everything else that is on screen. + /// + public void ItemFlyOut(FrameworkElement element, Action action) + { + _targetElement = element; + var rootElement = Application.Current.RootVisual as FrameworkElement; + + _backgroundMask = new Rectangle() + { + Fill = new SolidColorBrush(Colors.Black), + Opacity = 0.0, + Width = rootElement.ActualWidth, + Height = rootElement.ActualHeight + }; + _popupCanvas.Children.Add(_backgroundMask); + + _targetElementClone = new Image() + { + Source = new WriteableBitmap(element, null) + }; + _popupCanvas.Children.Add(_targetElementClone); + + _targetElementPosition = element.GetRelativePosition(rootElement); + Canvas.SetTop(_targetElementClone, _targetElementPosition.Y); + Canvas.SetLeft(_targetElementClone, _targetElementPosition.X); + + var sb = new Storyboard(); + + // animate the X position + var db = CreateDoubleAnimation(_targetElementPosition.X, _targetElementPosition.X + 500, + new SineEase() { EasingMode = EasingMode.EaseIn }, + _targetElementClone, Canvas.LeftProperty, _flyOutSpeed); + sb.Children.Add(db); + + // animate the Y position + db = CreateDoubleAnimation(_targetElementPosition.Y, _targetElementPosition.Y + 50, + new SineEase() { EasingMode = EasingMode.EaseOut }, + _targetElementClone, Canvas.TopProperty, _flyOutSpeed); + sb.Children.Add(db); + + // fade out the other elements + db = CreateDoubleAnimation(0, 1, + null, _backgroundMask, UIElement.OpacityProperty, _flyOutSpeed); + sb.Children.Add(db); + + sb.Completed += (s, e2) => + { + action(); + + // hide the popup, by placing a task on the dispatcher queue, this + // should be executed after the navigation has occurred + element.Dispatcher.BeginInvoke(() => + { + _popup.IsOpen = false; + }); + }; + + // hide the element we have 'cloned' into the popup + element.Opacity = 0.0; + + // open the popup + _popup.IsOpen = true; + + // begin the animation + sb.Begin(); + } + + public static DoubleAnimation CreateDoubleAnimation(double from, double to, IEasingFunction easing, + DependencyObject target, object propertyPath, TimeSpan duration) + { + var db = new DoubleAnimation(); + db.To = to; + db.From = from; + db.EasingFunction = easing; + db.Duration = duration; + Storyboard.SetTarget(db, target); + Storyboard.SetTargetProperty(db, new PropertyPath(propertyPath)); + return db; + } + } +} diff --git a/sbbs-client-wp7.csproj b/sbbs-client-wp7.csproj index aaee0dd..91368bb 100644 --- a/sbbs-client-wp7.csproj +++ b/sbbs-client-wp7.csproj @@ -74,6 +74,8 @@ MainPage.xaml + + @@ -145,6 +147,7 @@ PreserveNewest +