• WinPhone学习笔记(四)——磁贴


    对每个Windows Phone的使用者来说,给他们的第一印象就是大大小小的磁贴——Metro,本篇介绍的是Windows Phone的磁贴,提到的有开始菜单的磁贴,也有在App里面的磁贴。

    开始菜单的磁贴

    首先介绍了一下每个磁贴的构造,每个磁贴分正反两面,正反两面都有图标,而正面有一个标题和统计数量(一般用作消息推送的时候用),在背面就有一个描述性的内容,下图就摘自MSDN上的图片,图中黑色字体其实就是每个磁贴数据类的属性,这个稍后会提到

    对于一个磁贴来说,他的图片像素建议是173*173像素的,占用空间控制在80KB以内,它各个部分更详尽的数据大小如下图。

    在开始菜单中的磁贴分两类,一类是App本身启动用的,通过在应用程序列表中创建的磁贴,叫应用程序磁贴;另一类是由App创建的,那个叫次要磁贴。

    在控制开始菜单上的磁贴,主要有两个类,一个是StandandTileData,另一个是ShellTile。前者则是之前提过有一个类存储磁贴上的数据信息那个类,后者是负责管理开始菜单中的磁贴(包括了增删改查),但只局限于本App的磁贴。

    下面的代码则磁贴创建的代码

     1 StandardTileData tileData = new StandardTileData()
     2 
     3 {
     4 
     5 Title = "Test Tile",
     6 
     7 BackContent = "The " + ShellTile.ActiveTiles.Count() + " One",
     8 
     9 BackTitle = ShellTile.ActiveTiles.Count().ToString(),
    10 
    11 Count = ShellTile.ActiveTiles.Count()
    12 
    13 };
    14 
    15 ShellTile.Create(new Uri(NavigationService.Source.ToString()+"?key="+Guid.NewGuid().ToString(), UriKind.Relative), tileData);

    添加磁贴就是ShellTile的静态方法Create,传入的是点击磁贴后要跳转到的页面的URI还有这个磁贴的数据类,对于上面的代码,如果创建了一次磁贴之后再次执行则会抛出异常,原因在于对一个App的次要磁贴来说,它们的URI不允许重复,那遇到创建多个磁贴都是跳转到相同的页面时,可以给URI上面加上不同是QueryString来使得各个URI不一样。

    ShellTile的ActiveTiles静态属性是获取这个App所有开始菜单上磁贴的枚举,是一个ShellTile的泛型集合。要获取这个App中的某一个磁贴只能遍历这个集合。有个特别之处就是不管这个App有没有放应用程序磁贴到开始菜单中,第一个元素绝对是应用程序磁贴,次要磁贴则是从第二个元素开始。

    更新磁贴只是从ActiveTiles获取了相应的磁贴类之后,然后用一个StandandTileData赋上新的值,通过该磁贴的ShellTile实例的Update方法把StandandTileData传过去就可以了。

    删除磁贴也是通过ActiveTiles获取了相应的磁贴类ShellTile实例,再调用它的Delete方法,但注意的一点是这里的删除只能删除次要磁贴,应用程序磁贴是不允许删除的。

    应用程序内部的磁贴

    类似开始菜单中的磁贴也可以添加到App内部。但它就不是ShellTile了,是HubTile,这个控件并非单纯从工具箱可以拖到页面中去,这个需要引用Toolkit,在以前WP7时使用Toolkit相对简单,但是WP8的话则需要联机获取dll了。

    在vs中打开"扩展与更新"窗口,搜索"Nuget";

    搜索出来了"Nuget Package Manager"便安装,安装完毕后就记得重启VS;在"扩展与更新"窗口中重启"Nuget Package Manager"。

    现在就可以在引用文件夹中添加dll了。选的是"管理NuGet程序包"。

    搜索"windows phone toolkit"进行安装,

    最后在包管理器控制台中输入命令"Install-Package WPToolkit"就可以完成dll的添加了。包管理控制台打开方式如下图。

    在需要使用该dll的xaml页面肯要添加对应的xml命名空间

    Xmlns:toolkit="clr-namespace;Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit" 

    在xaml中添加下面语句则可以往页面成功添加一个磁贴

    <toolkit:HubTile Grid.Row="1" Grid.Column="1" Background="Red" Source="Assets/Tiles/FlipCycleTileSmall.png" Title="Metro" Message="This is Metro in App"/>

    一个HubTile一共有下面五种状态,这个这个磁贴用到的属性其实在上面一条语句中都可以看出来,Background是磁贴的背景色;Source是磁贴中图片,这个图片就只有一面才有,反面就没有了,Title则是那个很醒目的磁贴的标题,在磁贴的背面也有;Message是在磁贴背面

    运行的时候会发现磁贴是贴在了页面上了,但是手点击上去就没有了开始菜单中的那种倾斜效果,这个磁贴的倾斜效果是这个Toolkit的另外一个附加属性 TiltEffect.IsEnable。它是一个布尔类型,True表示使用倾斜效果。还需要在隐藏文件的构造函数中加入这个控件的类型

    <toolkit:HubTile toolkit:TiltEffect.IsTiltEnabled="True" Grid.Row="1" Grid.Column="1" Background="Red" Source="Assets/Tiles/FlipCycleTileSmall.png" Title="Metro" Message="This is Metro in App"/>
    public TileTestPage()
    
    {
    
    InitializeComponent();
    
    ControlTiltEffect.TiltEffect.TiltableItems.Add(typeof(HubTile));
    
    }

    但是用Toolkit的效果不是很明显,而且有限制,有一些控件虽然用上了但也没有倾斜的效果。在网上查看资料时发现有个老外也写了一个倾斜效果,效果比微软提供的要明显,而且还可以调节倾斜的角度。两个类的代码如下

      1 using System;
      2 using System.Windows;
      3 using System.Windows.Controls;
      4 using System.Windows.Input;
      5 using System.Windows.Media;
      6 using System.Windows.Media.Animation;
      7 using System.Collections.Generic;
      8 using System.Windows.Controls.Primitives;
      9 
     10 
     11 #if WINDOWS_PHONE
     12 using Microsoft.Phone.Controls;
     13 #endif
     14 
     15 namespace ControlTiltEffect
     16 {
     17     /// <summary>
     18     /// This code provides attached properties for adding a 'tilt' effect to all controls within a container.
     19     /// </summary>
     20     public class TiltEffect : DependencyObject
     21     {
     22 
     23         #region Constructor and Static Constructor
     24         /// <summary>
     25         /// This is not a constructable class, but it cannot be static because it derives from DependencyObject.
     26         /// </summary>
     27         private TiltEffect()
     28         {
     29         }
     30 
     31         /// <summary>
     32         /// Initialize the static properties
     33         /// </summary>
     34         static TiltEffect()
     35         {
     36             // The tiltable items list.
     37             TiltableItems = new List<Type>() { typeof(ButtonBase), typeof(ListBoxItem) };
     38             UseLogarithmicEase = false;
     39         }
     40 
     41         #endregion
     42 
     43 
     44         #region Fields and simple properties
     45 
     46         // These constants are the same as the built-in effects
     47         /// <summary>
     48         /// Maximum amount of tilt, in radians
     49         /// </summary>
     50         const double MaxAngle = 0.3;
     51 
     52         /// <summary>
     53         /// Maximum amount of depression, in pixels
     54         /// </summary>
     55         const double MaxDepression = 25;
     56 
     57         /// <summary>
     58         /// Delay between releasing an element and the tilt release animation playing
     59         /// </summary>
     60         static readonly TimeSpan TiltReturnAnimationDelay = TimeSpan.FromMilliseconds(200);
     61 
     62         /// <summary>
     63         /// Duration of tilt release animation
     64         /// </summary>
     65         static readonly TimeSpan TiltReturnAnimationDuration = TimeSpan.FromMilliseconds(100);
     66 
     67         /// <summary>
     68         /// The control that is currently being tilted
     69         /// </summary>
     70         static FrameworkElement currentTiltElement;
     71 
     72         /// <summary>
     73         /// The single instance of a storyboard used for all tilts
     74         /// </summary>
     75         static Storyboard tiltReturnStoryboard;
     76 
     77         /// <summary>
     78         /// The single instance of an X rotation used for all tilts
     79         /// </summary>
     80         static DoubleAnimation tiltReturnXAnimation;
     81 
     82         /// <summary>
     83         /// The single instance of a Y rotation used for all tilts
     84         /// </summary>
     85         static DoubleAnimation tiltReturnYAnimation;
     86 
     87         /// <summary>
     88         /// The single instance of a Z depression used for all tilts
     89         /// </summary>
     90         static DoubleAnimation tiltReturnZAnimation;
     91 
     92         /// <summary>
     93         /// The center of the tilt element
     94         /// </summary>
     95         static Point currentTiltElementCenter;
     96 
     97         /// <summary>
     98         /// Whether the animation just completed was for a 'pause' or not
     99         /// </summary>
    100         static bool wasPauseAnimation = false;
    101 
    102         /// <summary>
    103         /// Whether to use a slightly more accurate (but slightly slower) tilt animation easing function
    104         /// </summary>
    105         public static bool UseLogarithmicEase { get; set; }
    106 
    107         /// <summary>
    108         /// Default list of items that are tiltable
    109         /// </summary>
    110         public static List<Type> TiltableItems { get; private set; }
    111 
    112         #endregion
    113 
    114 
    115         #region Dependency properties
    116 
    117         /// <summary>
    118         /// Whether the tilt effect is enabled on a container (and all its children)
    119         /// </summary>
    120         public static readonly DependencyProperty IsTiltEnabledProperty = DependencyProperty.RegisterAttached(
    121           "IsTiltEnabled",
    122           typeof(bool),
    123           typeof(TiltEffect),
    124           new PropertyMetadata(OnIsTiltEnabledChanged)
    125           );
    126 
    127         /// <summary>
    128         /// Gets the IsTiltEnabled dependency property from an object
    129         /// </summary>
    130         /// <param name="source">The object to get the property from</param>
    131         /// <returns>The property's value</returns>
    132         public static bool GetIsTiltEnabled(DependencyObject source) { return (bool)source.GetValue(IsTiltEnabledProperty); }
    133 
    134         /// <summary>
    135         /// Sets the IsTiltEnabled dependency property on an object
    136         /// </summary>
    137         /// <param name="source">The object to set the property on</param>
    138         /// <param name="value">The value to set</param>
    139         public static void SetIsTiltEnabled(DependencyObject source, bool value) { source.SetValue(IsTiltEnabledProperty, value); }
    140 
    141         /// <summary>
    142         /// Suppresses the tilt effect on a single control that would otherwise be tilted
    143         /// </summary>
    144         public static readonly DependencyProperty SuppressTiltProperty = DependencyProperty.RegisterAttached(
    145           "SuppressTilt",
    146           typeof(bool),
    147           typeof(TiltEffect),
    148           null
    149           );
    150 
    151         /// <summary>
    152         /// Gets the SuppressTilt dependency property from an object
    153         /// </summary>
    154         /// <param name="source">The object to get the property from</param>
    155         /// <returns>The property's value</returns>
    156         public static bool GetSuppressTilt(DependencyObject source) { return (bool)source.GetValue(SuppressTiltProperty); }
    157 
    158         /// <summary>
    159         /// Sets the SuppressTilt dependency property from an object
    160         /// </summary>
    161         /// <param name="source">The object to get the property from</param>
    162         /// <returns>The property's value</returns>
    163         public static void SetSuppressTilt(DependencyObject source, bool value) { source.SetValue(SuppressTiltProperty, value); }
    164 
    165 
    166         /// <summary>
    167         /// Property change handler for the IsTiltEnabled dependency property
    168         /// </summary>
    169         /// <param name="target">The element that the property is atteched to</param>
    170         /// <param name="args">Event args</param>
    171         /// <remarks>
    172         /// Adds or removes event handlers from the element that has been (un)registered for tilting
    173         /// </remarks>
    174         static void OnIsTiltEnabledChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
    175         {
    176             if (target is FrameworkElement)
    177             {
    178                 // Add / remove the event handler if necessary
    179                 if ((bool)args.NewValue == true)
    180                 {
    181                     (target as FrameworkElement).ManipulationStarted += TiltEffect_ManipulationStarted;
    182                 }
    183                 else
    184                 {
    185                     (target as FrameworkElement).ManipulationStarted -= TiltEffect_ManipulationStarted;
    186                 }
    187             }
    188         }
    189 
    190         #endregion
    191 
    192 
    193         #region Top-level manipulation event handlers
    194 
    195         /// <summary>
    196         /// Event handler for ManipulationStarted
    197         /// </summary>
    198         /// <param name="sender">sender of the event - this will be the tilt container (eg, entire page)</param>
    199         /// <param name="e">event args</param>
    200         static void TiltEffect_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
    201         {
    202 
    203             TryStartTiltEffect(sender as FrameworkElement, e);
    204         }
    205 
    206         /// <summary>
    207         /// Event handler for ManipulationDelta
    208         /// </summary>
    209         /// <param name="sender">sender of the event - this will be the tilting object (eg a button)</param>
    210         /// <param name="e">event args</param>
    211         static void TiltEffect_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
    212         {
    213 
    214             ContinueTiltEffect(sender as FrameworkElement, e);
    215         }
    216 
    217         /// <summary>
    218         /// Event handler for ManipulationCompleted
    219         /// </summary>
    220         /// <param name="sender">sender of the event - this will be the tilting object (eg a button)</param>
    221         /// <param name="e">event args</param>
    222         static void TiltEffect_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
    223         {
    224 
    225             EndTiltEffect(currentTiltElement);
    226         }
    227 
    228         #endregion
    229 
    230 
    231         #region Core tilt logic
    232 
    233         /// <summary>
    234         /// Checks if the manipulation should cause a tilt, and if so starts the tilt effect
    235         /// </summary>
    236         /// <param name="source">The source of the manipulation (the tilt container, eg entire page)</param>
    237         /// <param name="e">The args from the ManipulationStarted event</param>
    238         static void TryStartTiltEffect(FrameworkElement source, ManipulationStartedEventArgs e)
    239         {
    240             foreach (FrameworkElement ancestor in (e.OriginalSource as FrameworkElement).GetVisualAncestors())
    241             {
    242                 foreach (Type t in TiltableItems)
    243                 {
    244                     if (t.IsAssignableFrom(ancestor.GetType()))
    245                     {
    246                         if ((bool)ancestor.GetValue(SuppressTiltProperty) != true)
    247                         {
    248                             // Use first child of the control, so that you can add transforms and not
    249                             // impact any transforms on the control itself
    250                             FrameworkElement element = VisualTreeHelper.GetChild(ancestor, 0) as FrameworkElement;
    251                             FrameworkElement container = e.ManipulationContainer as FrameworkElement;
    252 
    253                             if (element == null || container == null)
    254                                 return;
    255 
    256                             // Touch point relative to the element being tilted
    257                             Point tiltTouchPoint = container.TransformToVisual(element).Transform(e.ManipulationOrigin);
    258 
    259                             // Center of the element being tilted
    260                             Point elementCenter = new Point(element.ActualWidth / 2, element.ActualHeight / 2);
    261 
    262                             // Camera adjustment
    263                             Point centerToCenterDelta = GetCenterToCenterDelta(element, source);
    264 
    265                             BeginTiltEffect(element, tiltTouchPoint, elementCenter, centerToCenterDelta);
    266                             return;
    267                         }
    268                     }
    269                 }
    270             }
    271         }
    272 
    273         /// <summary>
    274         /// Computes the delta between the centre of an element and its container
    275         /// </summary>
    276         /// <param name="element">The element to compare</param>
    277         /// <param name="container">The element to compare against</param>
    278         /// <returns>A point that represents the delta between the two centers</returns>
    279         static Point GetCenterToCenterDelta(FrameworkElement element, FrameworkElement container)
    280         {
    281             Point elementCenter = new Point(element.ActualWidth / 2, element.ActualHeight / 2);
    282             Point containerCenter;
    283 
    284 #if WINDOWS_PHONE
    285 
    286             // Need to special-case the frame to handle different orientations
    287             if (container is PhoneApplicationFrame)
    288             {
    289                 PhoneApplicationFrame frame = container as PhoneApplicationFrame;
    290 
    291                 // Switch width and height in landscape mode
    292                 if ((frame.Orientation & PageOrientation.Landscape) == PageOrientation.Landscape)
    293                 {
    294 
    295                     containerCenter = new Point(container.ActualHeight / 2, container.ActualWidth / 2);
    296                 }
    297                 else
    298                     containerCenter = new Point(container.ActualWidth / 2, container.ActualHeight / 2);
    299             }
    300             else
    301                 containerCenter = new Point(container.ActualWidth / 2, container.ActualHeight / 2);
    302 #else
    303 
    304             containerCenter = new Point(container.ActualWidth / 2, container.ActualHeight / 2);
    305 
    306 #endif
    307 
    308             Point transformedElementCenter = element.TransformToVisual(container).Transform(elementCenter);
    309             Point result = new Point(containerCenter.X - transformedElementCenter.X, containerCenter.Y - transformedElementCenter.Y);
    310 
    311             return result;
    312         }
    313 
    314         /// <summary>
    315         /// Begins the tilt effect by preparing the control and doing the initial animation
    316         /// </summary>
    317         /// <param name="element">The element to tilt </param>
    318         /// <param name="touchPoint">The touch point, in element coordinates</param>
    319         /// <param name="centerPoint">The center point of the element in element coordinates</param>
    320         /// <param name="centerDelta">The delta between the <paramref name="element"/>'s center and 
    321         /// the container's center</param>
    322         static void BeginTiltEffect(FrameworkElement element, Point touchPoint, Point centerPoint, Point centerDelta)
    323         {
    324 
    325 
    326             if (tiltReturnStoryboard != null)
    327                 StopTiltReturnStoryboardAndCleanup();
    328 
    329             if (PrepareControlForTilt(element, centerDelta) == false)
    330                 return;
    331 
    332             currentTiltElement = element;
    333             currentTiltElementCenter = centerPoint;
    334             PrepareTiltReturnStoryboard(element);
    335 
    336             ApplyTiltEffect(currentTiltElement, touchPoint, currentTiltElementCenter);
    337         }
    338 
    339         /// <summary>
    340         /// Prepares a control to be tilted by setting up a plane projection and some event handlers
    341         /// </summary>
    342         /// <param name="element">The control that is to be tilted</param>
    343         /// <param name="centerDelta">Delta between the element's center and the tilt container's</param>
    344         /// <returns>true if successful; false otherwise</returns>
    345         /// <remarks>
    346         /// This method is conservative; it will fail any attempt to tilt a control that already
    347         /// has a projection on it
    348         /// </remarks>
    349         static bool PrepareControlForTilt(FrameworkElement element, Point centerDelta)
    350         {
    351             // Prevents interference with any existing transforms
    352             if (element.Projection != null || (element.RenderTransform != null && element.RenderTransform.GetType() != typeof(MatrixTransform)))
    353                 return false;
    354 
    355             TranslateTransform transform = new TranslateTransform();
    356             transform.X = centerDelta.X;
    357             transform.Y = centerDelta.Y;
    358             element.RenderTransform = transform;
    359 
    360             PlaneProjection projection = new PlaneProjection();
    361             projection.GlobalOffsetX = -1 * centerDelta.X;
    362             projection.GlobalOffsetY = -1 * centerDelta.Y;
    363             element.Projection = projection;
    364 
    365             element.ManipulationDelta += TiltEffect_ManipulationDelta;
    366             element.ManipulationCompleted += TiltEffect_ManipulationCompleted;
    367 
    368             return true;
    369         }
    370 
    371         /// <summary>
    372         /// Removes modifications made by PrepareControlForTilt
    373         /// </summary>
    374         /// <param name="element">THe control to be un-prepared</param>
    375         /// <remarks>
    376         /// This method is basic; it does not do anything to detect if the control being un-prepared
    377         /// was previously prepared
    378         /// </remarks>
    379         static void RevertPrepareControlForTilt(FrameworkElement element)
    380         {
    381             element.ManipulationDelta -= TiltEffect_ManipulationDelta;
    382             element.ManipulationCompleted -= TiltEffect_ManipulationCompleted;
    383             element.Projection = null;
    384             element.RenderTransform = null;
    385         }
    386 
    387         /// <summary>
    388         /// Creates the tilt return storyboard (if not already created) and targets it to the projection
    389         /// </summary>
    390         /// <param name="projection">the projection that should be the target of the animation</param>
    391         static void PrepareTiltReturnStoryboard(FrameworkElement element)
    392         {
    393 
    394             if (tiltReturnStoryboard == null)
    395             {
    396                 tiltReturnStoryboard = new Storyboard();
    397                 tiltReturnStoryboard.Completed += TiltReturnStoryboard_Completed;
    398 
    399                 tiltReturnXAnimation = new DoubleAnimation();
    400                 Storyboard.SetTargetProperty(tiltReturnXAnimation, new PropertyPath(PlaneProjection.RotationXProperty));
    401                 tiltReturnXAnimation.BeginTime = TiltReturnAnimationDelay;
    402                 tiltReturnXAnimation.To = 0;
    403                 tiltReturnXAnimation.Duration = TiltReturnAnimationDuration;
    404 
    405                 tiltReturnYAnimation = new DoubleAnimation();
    406                 Storyboard.SetTargetProperty(tiltReturnYAnimation, new PropertyPath(PlaneProjection.RotationYProperty));
    407                 tiltReturnYAnimation.BeginTime = TiltReturnAnimationDelay;
    408                 tiltReturnYAnimation.To = 0;
    409                 tiltReturnYAnimation.Duration = TiltReturnAnimationDuration;
    410 
    411                 tiltReturnZAnimation = new DoubleAnimation();
    412                 Storyboard.SetTargetProperty(tiltReturnZAnimation, new PropertyPath(PlaneProjection.GlobalOffsetZProperty));
    413                 tiltReturnZAnimation.BeginTime = TiltReturnAnimationDelay;
    414                 tiltReturnZAnimation.To = 0;
    415                 tiltReturnZAnimation.Duration = TiltReturnAnimationDuration;
    416 
    417                 if (UseLogarithmicEase)
    418                 {
    419                     tiltReturnXAnimation.EasingFunction = new LogarithmicEase();
    420                     tiltReturnYAnimation.EasingFunction = new LogarithmicEase();
    421                     tiltReturnZAnimation.EasingFunction = new LogarithmicEase();
    422                 }
    423 
    424                 tiltReturnStoryboard.Children.Add(tiltReturnXAnimation);
    425                 tiltReturnStoryboard.Children.Add(tiltReturnYAnimation);
    426                 tiltReturnStoryboard.Children.Add(tiltReturnZAnimation);
    427             }
    428 
    429             Storyboard.SetTarget(tiltReturnXAnimation, element.Projection);
    430             Storyboard.SetTarget(tiltReturnYAnimation, element.Projection);
    431             Storyboard.SetTarget(tiltReturnZAnimation, element.Projection);
    432         }
    433 
    434 
    435         /// <summary>
    436         /// Continues a tilt effect that is currently applied to an element, presumably because
    437         /// the user moved their finger
    438         /// </summary>
    439         /// <param name="element">The element being tilted</param>
    440         /// <param name="e">The manipulation event args</param>
    441         static void ContinueTiltEffect(FrameworkElement element, ManipulationDeltaEventArgs e)
    442         {
    443             FrameworkElement container = e.ManipulationContainer as FrameworkElement;
    444             if (container == null || element == null)
    445                 return;
    446 
    447             Point tiltTouchPoint = container.TransformToVisual(element).Transform(e.ManipulationOrigin);
    448 
    449             // If touch moved outside bounds of element, then pause the tilt (but don't cancel it)
    450             if (new Rect(0, 0, currentTiltElement.ActualWidth, currentTiltElement.ActualHeight).Contains(tiltTouchPoint) != true)
    451             {
    452 
    453                 PauseTiltEffect();
    454                 return;
    455             }
    456 
    457             // Apply the updated tilt effect
    458             ApplyTiltEffect(currentTiltElement, e.ManipulationOrigin, currentTiltElementCenter);
    459         }
    460 
    461         /// <summary>
    462         /// Ends the tilt effect by playing the animation  
    463         /// </summary>
    464         /// <param name="element">The element being tilted</param>
    465         static void EndTiltEffect(FrameworkElement element)
    466         {
    467             if (element != null)
    468             {
    469                 element.ManipulationCompleted -= TiltEffect_ManipulationCompleted;
    470                 element.ManipulationDelta -= TiltEffect_ManipulationDelta;
    471             }
    472 
    473             if (tiltReturnStoryboard != null)
    474             {
    475                 wasPauseAnimation = false;
    476                 if (tiltReturnStoryboard.GetCurrentState() != ClockState.Active)
    477                     tiltReturnStoryboard.Begin();
    478             }
    479             else
    480                 StopTiltReturnStoryboardAndCleanup();
    481         }
    482 
    483         /// <summary>
    484         /// Handler for the storyboard complete event
    485         /// </summary>
    486         /// <param name="sender">sender of the event</param>
    487         /// <param name="e">event args</param>
    488         static void TiltReturnStoryboard_Completed(object sender, EventArgs e)
    489         {
    490             if (wasPauseAnimation)
    491                 ResetTiltEffect(currentTiltElement);
    492             else
    493                 StopTiltReturnStoryboardAndCleanup();
    494         }
    495 
    496         /// <summary>
    497         /// Resets the tilt effect on the control, making it appear 'normal' again 
    498         /// </summary>
    499         /// <param name="element">The element to reset the tilt on</param>
    500         /// <remarks>
    501         /// This method doesn't turn off the tilt effect or cancel any current
    502         /// manipulation; it just temporarily cancels the effect
    503         /// </remarks>
    504         static void ResetTiltEffect(FrameworkElement element)
    505         {
    506             PlaneProjection projection = element.Projection as PlaneProjection;
    507             projection.RotationY = 0;
    508             projection.RotationX = 0;
    509             projection.GlobalOffsetZ = 0;
    510         }
    511 
    512         /// <summary>
    513         /// Stops the tilt effect and release resources applied to the currently-tilted control
    514         /// </summary>
    515         static void StopTiltReturnStoryboardAndCleanup()
    516         {
    517             if (tiltReturnStoryboard != null)
    518                 tiltReturnStoryboard.Stop();
    519 
    520             RevertPrepareControlForTilt(currentTiltElement);
    521         }
    522 
    523         /// <summary>
    524         /// Pauses the tilt effect so that the control returns to the 'at rest' position, but doesn't
    525         /// stop the tilt effect (handlers are still attached, etc.)
    526         /// </summary>
    527         static void PauseTiltEffect()
    528         {
    529             if ((tiltReturnStoryboard != null) && !wasPauseAnimation)
    530             {
    531                 tiltReturnStoryboard.Stop();
    532                 wasPauseAnimation = true;
    533                 tiltReturnStoryboard.Begin();
    534             }
    535         }
    536 
    537         /// <summary>
    538         /// Resets the storyboard to not running
    539         /// </summary>
    540         private static void ResetTiltReturnStoryboard()
    541         {
    542             tiltReturnStoryboard.Stop();
    543             wasPauseAnimation = false;
    544         }
    545 
    546         /// <summary>
    547         /// Applies the tilt effect to the control
    548         /// </summary>
    549         /// <param name="element">the control to tilt</param>
    550         /// <param name="touchPoint">The touch point, in the container's coordinates</param>
    551         /// <param name="centerPoint">The center point of the container</param>
    552         static void ApplyTiltEffect(FrameworkElement element, Point touchPoint, Point centerPoint)
    553         {
    554             // Stop any active animation
    555             ResetTiltReturnStoryboard();
    556 
    557             // Get relative point of the touch in percentage of container size
    558             Point normalizedPoint = new Point(
    559                 Math.Min(Math.Max(touchPoint.X / (centerPoint.X * 2), 0), 1),
    560                 Math.Min(Math.Max(touchPoint.Y / (centerPoint.Y * 2), 0), 1));
    561 
    562             // Shell values
    563             double xMagnitude = Math.Abs(normalizedPoint.X - 0.5);
    564             double yMagnitude = Math.Abs(normalizedPoint.Y - 0.5);
    565             double xDirection = -Math.Sign(normalizedPoint.X - 0.5);
    566             double yDirection = Math.Sign(normalizedPoint.Y - 0.5);
    567             double angleMagnitude = xMagnitude + yMagnitude;
    568             double xAngleContribution = xMagnitude + yMagnitude > 0 ? xMagnitude / (xMagnitude + yMagnitude) : 0;
    569 
    570             double angle = angleMagnitude * MaxAngle * 180 / Math.PI;
    571             double depression = (1 - angleMagnitude) * MaxDepression;
    572 
    573             // RotationX and RotationY are the angles of rotations about the x- or y-*axis*;
    574             // to achieve a rotation in the x- or y-*direction*, we need to swap the two.
    575             // That is, a rotation to the left about the y-axis is a rotation to the left in the x-direction,
    576             // and a rotation up about the x-axis is a rotation up in the y-direction.
    577             PlaneProjection projection = element.Projection as PlaneProjection;
    578             projection.RotationY = angle * xAngleContribution * xDirection;
    579             projection.RotationX = angle * (1 - xAngleContribution) * yDirection;
    580             projection.GlobalOffsetZ = -depression;
    581         }
    582 
    583         #endregion
    584 
    585 
    586         #region Custom easing function
    587 
    588         /// <summary>
    589         /// Provides an easing function for the tilt return
    590         /// </summary>
    591         private class LogarithmicEase : EasingFunctionBase
    592         {
    593             /// <summary>
    594             /// Computes the easing function
    595             /// </summary>
    596             /// <param name="normalizedTime">The time</param>
    597             /// <returns>The eased value</returns>
    598             protected override double EaseInCore(double normalizedTime)
    599             {
    600                 return Math.Log(normalizedTime + 1) / 0.693147181; // ln(t + 1) / ln(2)
    601             }
    602         }
    603 
    604         #endregion
    605     }
    606 
    607     /// <summary>
    608     /// Couple of simple helpers for walking the visual tree
    609     /// </summary>
    610     static class TreeHelpers
    611     {
    612         /// <summary>
    613         /// Gets the ancestors of the element, up to the root
    614         /// </summary>
    615         /// <param name="node">The element to start from</param>
    616         /// <returns>An enumerator of the ancestors</returns>
    617         public static IEnumerable<FrameworkElement> GetVisualAncestors(this FrameworkElement node)
    618         {
    619             FrameworkElement parent = node.GetVisualParent();
    620             while (parent != null)
    621             {
    622                 yield return parent;
    623                 parent = parent.GetVisualParent();
    624             }
    625         }
    626 
    627         /// <summary>
    628         /// Gets the visual parent of the element
    629         /// </summary>
    630         /// <param name="node">The element to check</param>
    631         /// <returns>The visual parent</returns>
    632         public static FrameworkElement GetVisualParent(this FrameworkElement node)
    633         {
    634             return VisualTreeHelper.GetParent(node) as FrameworkElement;
    635         }
    636     }
    637 }
    TiltEffect.cs
      1     public static class MetroInMotion
      2     {
      3         #region AnimationLevel
      4 
      5         public static int GetAnimationLevel(DependencyObject obj)
      6         {
      7             return (int)obj.GetValue(AnimationLevelProperty);
      8         }
      9 
     10         public static void SetAnimationLevel(DependencyObject obj, int value)
     11         {
     12             obj.SetValue(AnimationLevelProperty, value);
     13         }
     14 
     15 
     16         public static readonly DependencyProperty AnimationLevelProperty =
     17             DependencyProperty.RegisterAttached("AnimationLevel", typeof(int),
     18             typeof(MetroInMotion), new PropertyMetadata(-1));
     19 
     20         #endregion
     21 
     22         #region Tilt
     23 
     24         public static double GetTilt(DependencyObject obj)
     25         {
     26             return (double)obj.GetValue(TiltProperty);
     27         }
     28 
     29         public static void SetTilt(DependencyObject obj, double value)
     30         {
     31             obj.SetValue(TiltProperty, value);
     32         }
     33 
     34 
     35         public static readonly DependencyProperty TiltProperty =
     36             DependencyProperty.RegisterAttached("Tilt", typeof(double),
     37             typeof(MetroInMotion), new PropertyMetadata(2.0, OnTiltChanged));
     38 
     39         /// <summary>
     40         /// The extent of the tilt action, the larger the number, the bigger the tilt
     41         /// </summary>
     42         private static double TiltAngleFactor = 4;
     43 
     44         /// <summary>
     45         /// The extent of the scaling action, the smaller the number, the greater the scaling.
     46         /// </summary>
     47         private static double ScaleFactor = 100;
     48 
     49         private static void OnTiltChanged(DependencyObject d,
     50           DependencyPropertyChangedEventArgs args)
     51         {
     52             FrameworkElement targetElement = d as FrameworkElement;
     53 
     54             double tiltFactor = GetTilt(d);
     55 
     56             // create the required transformations
     57             var projection = new PlaneProjection();
     58             var scale = new ScaleTransform();
     59             var translate = new TranslateTransform();
     60 
     61             var transGroup = new TransformGroup();
     62             transGroup.Children.Add(scale);
     63             transGroup.Children.Add(translate);
     64 
     65             // associate with the target element
     66             targetElement.Projection = projection;
     67             targetElement.RenderTransform = transGroup;
     68             targetElement.RenderTransformOrigin = new Point(0.5, 0.5);
     69 
     70             targetElement.MouseLeftButtonDown += (s, e) =>
     71             {
     72                 var clickPosition = e.GetPosition(targetElement);
     73 
     74                 // find the maximum of width / height
     75                 double maxDimension = Math.Max(targetElement.ActualWidth, targetElement.ActualHeight);
     76 
     77                 // compute the normalised horizontal distance from the centre
     78                 double distanceFromCenterX = targetElement.ActualWidth / 2 - clickPosition.X;
     79                 double normalisedDistanceX = 2 * distanceFromCenterX / maxDimension;
     80 
     81                 // rotate around the Y axis 
     82                 projection.RotationY = normalisedDistanceX * TiltAngleFactor * tiltFactor;
     83 
     84                 // compute the normalised vertical distance from the centre
     85                 double distanceFromCenterY = targetElement.ActualHeight / 2 - clickPosition.Y;
     86                 double normalisedDistanceY = 2 * distanceFromCenterY / maxDimension;
     87 
     88                 // rotate around the X axis, 
     89                 projection.RotationX = -normalisedDistanceY * TiltAngleFactor * tiltFactor;
     90 
     91                 // find the distance to centre
     92                 double distanceToCentre = Math.Sqrt(normalisedDistanceX * normalisedDistanceX
     93                   + normalisedDistanceY * normalisedDistanceY);
     94 
     95                 // scale accordingly
     96                 double scaleVal = tiltFactor * (1 - distanceToCentre) / ScaleFactor;
     97                 scale.ScaleX = 1 - scaleVal;
     98                 scale.ScaleY = 1 - scaleVal;
     99 
    100                 // offset the plane transform
    101                 var rootElement = Application.Current.RootVisual as FrameworkElement;
    102                 var relativeToCentre = (targetElement.GetRelativePosition(rootElement).Y - rootElement.ActualHeight / 2) / 2;
    103                 translate.Y = -relativeToCentre;
    104                 projection.LocalOffsetY = +relativeToCentre;
    105 
    106             };
    107 
    108             targetElement.ManipulationCompleted += (s, e) =>
    109             {
    110                 var sb = new Storyboard();
    111                 sb.Children.Add(CreateAnimation(null, 0, 0.1, "RotationY", projection));
    112                 sb.Children.Add(CreateAnimation(null, 0, 0.1, "RotationX", projection));
    113                 sb.Children.Add(CreateAnimation(null, 1, 0.1, "ScaleX", scale));
    114                 sb.Children.Add(CreateAnimation(null, 1, 0.1, "ScaleY", scale));
    115                 sb.Begin();
    116 
    117                 translate.Y = 0;
    118                 projection.LocalOffsetY = 0;
    119             };
    120 
    121         }
    122 
    123 
    124         #endregion
    125 
    126         #region IsPivotAnimated
    127 
    128         public static bool GetIsPivotAnimated(DependencyObject obj)
    129         {
    130             return (bool)obj.GetValue(IsPivotAnimatedProperty);
    131         }
    132 
    133         public static void SetIsPivotAnimated(DependencyObject obj, bool value)
    134         {
    135             obj.SetValue(IsPivotAnimatedProperty, value);
    136         }
    137 
    138         public static readonly DependencyProperty IsPivotAnimatedProperty =
    139             DependencyProperty.RegisterAttached("IsPivotAnimated", typeof(bool),
    140             typeof(MetroInMotion), new PropertyMetadata(false, OnIsPivotAnimatedChanged));
    141 
    142         private static void OnIsPivotAnimatedChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
    143         {
    144             ItemsControl list = d as ItemsControl;
    145 
    146             list.Loaded += (s2, e2) =>
    147             {
    148                 // locate the pivot control that this list is within
    149                 Pivot pivot = list.Ancestors<Pivot>().Single() as Pivot;
    150 
    151                 // and its index within the pivot
    152                 int pivotIndex = pivot.Items.IndexOf(list.Ancestors<PivotItem>().Single());
    153 
    154                 bool selectionChanged = false;
    155 
    156                 pivot.SelectionChanged += (s3, e3) =>
    157                 {
    158                     selectionChanged = true;
    159                 };
    160 
    161                 // handle manipulation events which occur when the user
    162                 // moves between pivot items
    163                 pivot.ManipulationCompleted += (s, e) =>
    164                 {
    165                     if (!selectionChanged)
    166                         return;
    167 
    168                     selectionChanged = false;
    169 
    170                     if (pivotIndex != pivot.SelectedIndex)
    171                         return;
    172 
    173                     // determine which direction this tab will be scrolling in from
    174                     bool fromRight = e.TotalManipulation.Translation.X <= 0;
    175 
    176 
    177                     // iterate over each of the items in view
    178                     var items = list.GetItemsInView().ToList();
    179                     for (int index = 0; index < items.Count; index++)
    180                     {
    181                         var lbi = items[index];
    182 
    183                         list.Dispatcher.BeginInvoke(() =>
    184                         {
    185                             var animationTargets = lbi.Descendants()
    186                                                    .Where(p => MetroInMotion.GetAnimationLevel(p) > -1);
    187                             foreach (FrameworkElement target in animationTargets)
    188                             {
    189                                 // trigger the required animation
    190                                 GetSlideAnimation(target, fromRight).Begin();
    191                             }
    192                         });
    193                     };
    194 
    195                 };
    196             };
    197         }
    198 
    199 
    200         #endregion
    201 
    202         /// <summary>
    203         /// Animates each element in order, creating a 'peel' effect. The supplied action
    204         /// is invoked when the animation ends.
    205         /// </summary>
    206         public static void Peel(this IEnumerable<FrameworkElement> elements, Action endAction)
    207         {
    208             var elementList = elements.ToList();
    209             var lastElement = elementList.Last();
    210 
    211             // iterate over all the elements, animating each of them
    212             double delay = 0;
    213             foreach (FrameworkElement element in elementList)
    214             {
    215                 var sb = GetPeelAnimation(element, delay);
    216 
    217                 // add a Completed event handler to the last element
    218                 if (element.Equals(lastElement))
    219                 {
    220                     sb.Completed += (s, e) =>
    221                     {
    222                         endAction();
    223                     };
    224                 }
    225 
    226                 sb.Begin();
    227                 delay += 50;
    228             }
    229         }
    230 
    231 
    232         /// <summary>
    233         /// Enumerates all the items that are currently visible in am ItemsControl. This implementation assumes
    234         /// that a VirtualizingStackPanel is being used as the ItemsPanel.
    235         /// </summary>
    236         public static IEnumerable<FrameworkElement> GetItemsInView(this ItemsControl itemsControl)
    237         {
    238             // locate the stack panel that hosts the items
    239             VirtualizingStackPanel vsp = itemsControl.Descendants<VirtualizingStackPanel>().First() as VirtualizingStackPanel;
    240 
    241             // iterate over each of the items in view
    242             int firstVisibleItem = (int)vsp.VerticalOffset;
    243             int visibleItemCount = (int)vsp.ViewportHeight;
    244             for (int index = firstVisibleItem; index <= firstVisibleItem + visibleItemCount + 1; index++)
    245             {
    246                 var item = itemsControl.ItemContainerGenerator.ContainerFromIndex(index) as FrameworkElement;
    247                 if (item == null)
    248                     continue;
    249 
    250                 yield return item;
    251             }
    252         }
    253 
    254         /// <summary>
    255         /// Creates a PlaneProjection and associates it with the given element, returning
    256         /// a Storyboard which will animate the PlaneProjection to 'peel' the item
    257         /// from the screen.
    258         /// </summary>
    259         private static Storyboard GetPeelAnimation(FrameworkElement element, double delay)
    260         {
    261             Storyboard sb;
    262 
    263             var projection = new PlaneProjection()
    264             {
    265                 CenterOfRotationX = -0.1
    266             };
    267             element.Projection = projection;
    268 
    269             // compute the angle of rotation required to make this element appear
    270             // at a 90 degree angle at the edge of the screen.
    271             var width = element.ActualWidth;
    272             var targetAngle = Math.Atan(1000 / (width / 2));
    273             targetAngle = targetAngle * 180 / Math.PI;
    274 
    275             // animate the projection
    276             sb = new Storyboard();
    277             sb.BeginTime = TimeSpan.FromMilliseconds(delay);
    278             sb.Children.Add(CreateAnimation(0, -(180 - targetAngle), 0.3, "RotationY", projection));
    279             sb.Children.Add(CreateAnimation(0, 23, 0.3, "RotationZ", projection));
    280             sb.Children.Add(CreateAnimation(0, -23, 0.3, "GlobalOffsetZ", projection));
    281             return sb;
    282         }
    283 
    284         private static DoubleAnimation CreateAnimation(double? from, double? to, double duration,
    285           string targetProperty, DependencyObject target)
    286         {
    287             var db = new DoubleAnimation();
    288             db.To = to;
    289             db.From = from;
    290             db.EasingFunction = new SineEase();
    291             db.Duration = TimeSpan.FromSeconds(duration);
    292             Storyboard.SetTarget(db, target);
    293             Storyboard.SetTargetProperty(db, new PropertyPath(targetProperty));
    294             return db;
    295         }
    296 
    297         /// <summary>
    298         /// Creates a TranslateTransform and associates it with the given element, returning
    299         /// a Storyboard which will animate the TranslateTransform with a SineEase function
    300         /// </summary>
    301         private static Storyboard GetSlideAnimation(FrameworkElement element, bool fromRight)
    302         {
    303             double from = fromRight ? 80 : -80;
    304 
    305             Storyboard sb;
    306             double delay = (MetroInMotion.GetAnimationLevel(element)) * 0.1 + 0.1;
    307 
    308             TranslateTransform trans = new TranslateTransform() { X = from };
    309             element.RenderTransform = trans;
    310 
    311             sb = new Storyboard();
    312             sb.BeginTime = TimeSpan.FromSeconds(delay);
    313             sb.Children.Add(CreateAnimation(from, 0, 0.8, "X", trans));
    314             return sb;
    315         }
    316 
    317     }
    318 
    319     public static class ExtensionMethods
    320     {
    321         public static Point GetRelativePosition(this UIElement element, UIElement other)
    322         {
    323             return element.TransformToVisual(other)
    324               .Transform(new Point(0, 0));
    325         }
    326     }
    327 
    328     public class ItemFlyInAndOutAnimations
    329     {
    330         private Popup _popup;
    331 
    332         private Canvas _popupCanvas;
    333 
    334         private FrameworkElement _targetElement;
    335 
    336         private Point _targetElementPosition;
    337 
    338         private Image _targetElementClone;
    339 
    340         private Rectangle _backgroundMask;
    341 
    342         private static TimeSpan _flyInSpeed = TimeSpan.FromMilliseconds(200);
    343 
    344         private static TimeSpan _flyOutSpeed = TimeSpan.FromMilliseconds(300);
    345 
    346         public ItemFlyInAndOutAnimations()
    347         {
    348             // construct a popup, with a Canvas as its child
    349             _popup = new Popup();
    350             _popupCanvas = new Canvas();
    351             _popup.Child = _popupCanvas;
    352         }
    353 
    354         public static void TitleFlyIn(FrameworkElement title)
    355         {
    356             TranslateTransform trans = new TranslateTransform();
    357             trans.X = 300;
    358             trans.Y = -50;
    359             title.RenderTransform = trans;
    360 
    361             var sb = new Storyboard();
    362 
    363             // animate the X position
    364             var db = CreateDoubleAnimation(300, 0,
    365                 new SineEase(), trans, TranslateTransform.XProperty, _flyInSpeed);
    366             sb.Children.Add(db);
    367 
    368             // animate the Y position
    369             db = CreateDoubleAnimation(-100, 0,
    370                 new SineEase(), trans, TranslateTransform.YProperty, _flyInSpeed);
    371             sb.Children.Add(db);
    372 
    373             sb.Begin();
    374         }
    375 
    376         /// <summary>
    377         /// Animate the previously 'flown-out' element back to its original location.
    378         /// </summary>
    379         public void ItemFlyIn()
    380         {
    381             if (_popupCanvas.Children.Count != 2)
    382                 return;
    383 
    384             _popup.IsOpen = true;
    385             _backgroundMask.Opacity = 0.0;
    386 
    387             Image animatedImage = _popupCanvas.Children[1] as Image;
    388 
    389             var sb = new Storyboard();
    390 
    391             // animate the X position
    392             var db = CreateDoubleAnimation(_targetElementPosition.X - 100, _targetElementPosition.X,
    393                 new SineEase(),
    394                 _targetElementClone, Canvas.LeftProperty, _flyInSpeed);
    395             sb.Children.Add(db);
    396 
    397             // animate the Y position
    398             db = CreateDoubleAnimation(_targetElementPosition.Y - 50, _targetElementPosition.Y,
    399                 new SineEase(),
    400                 _targetElementClone, Canvas.TopProperty, _flyInSpeed);
    401             sb.Children.Add(db);
    402 
    403             sb.Completed += (s, e) =>
    404             {
    405                 // when the animation has finished, hide the popup once more
    406                 _popup.IsOpen = false;
    407 
    408                 // restore the element we have animated
    409                 _targetElement.Opacity = 1.0;
    410 
    411                 // and get rid of our clone
    412                 _popupCanvas.Children.Clear();
    413             };
    414 
    415             sb.Begin();
    416         }
    417 
    418 
    419         /// <summary>
    420         /// Animate the given element so that it flies off screen, fading 
    421         /// everything else that is on screen.
    422         /// </summary>
    423         public void ItemFlyOut(FrameworkElement element, Action action)
    424         {
    425             _targetElement = element;
    426             var rootElement = Application.Current.RootVisual as FrameworkElement;
    427 
    428             _backgroundMask = new Rectangle()
    429             {
    430                 Fill = new SolidColorBrush(Colors.Black),
    431                 Opacity = 0.0,
    432                 Width = rootElement.ActualWidth,
    433                 Height = rootElement.ActualHeight
    434             };
    435             _popupCanvas.Children.Add(_backgroundMask);
    436 
    437             _targetElementClone = new Image()
    438             {
    439                 Source = new WriteableBitmap(element, null)
    440             };
    441             _popupCanvas.Children.Add(_targetElementClone);
    442 
    443             _targetElementPosition = element.GetRelativePosition(rootElement);
    444             Canvas.SetTop(_targetElementClone, _targetElementPosition.Y);
    445             Canvas.SetLeft(_targetElementClone, _targetElementPosition.X);
    446 
    447             var sb = new Storyboard();
    448 
    449             // animate the X position
    450             var db = CreateDoubleAnimation(_targetElementPosition.X, _targetElementPosition.X + 500,
    451                 new SineEase() { EasingMode = EasingMode.EaseIn },
    452                 _targetElementClone, Canvas.LeftProperty, _flyOutSpeed);
    453             sb.Children.Add(db);
    454 
    455             // animate the Y position
    456             db = CreateDoubleAnimation(_targetElementPosition.Y, _targetElementPosition.Y + 50,
    457                 new SineEase() { EasingMode = EasingMode.EaseOut },
    458                 _targetElementClone, Canvas.TopProperty, _flyOutSpeed);
    459             sb.Children.Add(db);
    460 
    461             // fade out the other elements
    462             db = CreateDoubleAnimation(0, 1,
    463                 null, _backgroundMask, UIElement.OpacityProperty, _flyOutSpeed);
    464             sb.Children.Add(db);
    465 
    466             sb.Completed += (s, e2) =>
    467             {
    468                 action();
    469 
    470                 // hide the popup, by placing a task on the dispatcher queue, this
    471                 // should be executed after the navigation has occurred
    472                 element.Dispatcher.BeginInvoke(() =>
    473                 {
    474                     _popup.IsOpen = false;
    475                 });
    476             };
    477 
    478             // hide the element we have 'cloned' into the popup
    479             element.Opacity = 0.0;
    480 
    481             // open the popup
    482             _popup.IsOpen = true;
    483 
    484             // begin the animation
    485             sb.Begin();
    486         }
    487 
    488         public static DoubleAnimation CreateDoubleAnimation(double from, double to, IEasingFunction easing,
    489           DependencyObject target, object propertyPath, TimeSpan duration)
    490         {
    491             var db = new DoubleAnimation();
    492             db.To = to;
    493             db.From = from;
    494             db.EasingFunction = easing;
    495             db.Duration = duration;
    496             Storyboard.SetTarget(db, target);
    497             Storyboard.SetTargetProperty(db, new PropertyPath(propertyPath));
    498             return db;
    499         }
    500     }
    501 
    502 
    503     public class VisualTreeAdapter : ILinqTree<DependencyObject>
    504     {
    505         private DependencyObject _item;
    506 
    507         public VisualTreeAdapter(DependencyObject item)
    508         {
    509             _item = item;
    510         }
    511 
    512         public IEnumerable<DependencyObject> Children()
    513         {
    514             int childrenCount = VisualTreeHelper.GetChildrenCount(_item);
    515             for (int i = 0; i < childrenCount; i++)
    516             {
    517                 yield return VisualTreeHelper.GetChild(_item, i);
    518             }
    519         }
    520 
    521         public DependencyObject Parent
    522         {
    523             get
    524             {
    525                 return VisualTreeHelper.GetParent(_item);
    526             }
    527         }
    528     }
    529 
    530     public interface ILinqTree<T>
    531     {
    532         IEnumerable<T> Children();
    533 
    534         T Parent { get; }
    535     }
    536 
    537     public static class TreeExtensions
    538     {
    539         /// <summary>
    540         /// Returns a collection of descendant elements.
    541         /// </summary>
    542         public static IEnumerable<DependencyObject> Descendants(this DependencyObject item)
    543         {
    544             ILinqTree<DependencyObject> adapter = new VisualTreeAdapter(item);
    545             foreach (var child in adapter.Children())
    546             {
    547                 yield return child;
    548 
    549                 foreach (var grandChild in child.Descendants())
    550                 {
    551                     yield return grandChild;
    552                 }
    553             }
    554         }
    555 
    556         /// <summary>
    557         /// Returns a collection containing this element and all descendant elements.
    558         /// </summary>
    559         public static IEnumerable<DependencyObject> DescendantsAndSelf(this DependencyObject item)
    560         {
    561             yield return item;
    562 
    563             foreach (var child in item.Descendants())
    564             {
    565                 yield return child;
    566             }
    567         }
    568 
    569         /// <summary>
    570         /// Returns a collection of ancestor elements.
    571         /// </summary>
    572         public static IEnumerable<DependencyObject> Ancestors(this DependencyObject item)
    573         {
    574             ILinqTree<DependencyObject> adapter = new VisualTreeAdapter(item);
    575 
    576             var parent = adapter.Parent;
    577             while (parent != null)
    578             {
    579                 yield return parent;
    580                 adapter = new VisualTreeAdapter(parent);
    581                 parent = adapter.Parent;
    582             }
    583         }
    584 
    585         /// <summary>
    586         /// Returns a collection containing this element and all ancestor elements.
    587         /// </summary>
    588         public static IEnumerable<DependencyObject> AncestorsAndSelf(this DependencyObject item)
    589         {
    590             yield return item;
    591 
    592             foreach (var ancestor in item.Ancestors())
    593             {
    594                 yield return ancestor;
    595             }
    596         }
    597 
    598         /// <summary>
    599         /// Returns a collection of child elements.
    600         /// </summary>
    601         public static IEnumerable<DependencyObject> Elements(this DependencyObject item)
    602         {
    603             ILinqTree<DependencyObject> adapter = new VisualTreeAdapter(item);
    604             foreach (var child in adapter.Children())
    605             {
    606                 yield return child;
    607             }
    608         }
    609 
    610         /// <summary>
    611         /// Returns a collection of the sibling elements before this node, in document order.
    612         /// </summary>
    613         public static IEnumerable<DependencyObject> ElementsBeforeSelf(this DependencyObject item)
    614         {
    615             if (item.Ancestors().FirstOrDefault() == null)
    616                 yield break;
    617             foreach (var child in item.Ancestors().First().Elements())
    618             {
    619                 if (child.Equals(item))
    620                     break;
    621                 yield return child;
    622             }
    623         }
    624 
    625         /// <summary>
    626         /// Returns a collection of the after elements after this node, in document order.
    627         /// </summary>
    628         public static IEnumerable<DependencyObject> ElementsAfterSelf(this DependencyObject item)
    629         {
    630             if (item.Ancestors().FirstOrDefault() == null)
    631                 yield break;
    632             bool afterSelf = false;
    633             foreach (var child in item.Ancestors().First().Elements())
    634             {
    635                 if (afterSelf)
    636                     yield return child;
    637 
    638                 if (child.Equals(item))
    639                     afterSelf = true;
    640             }
    641         }
    642 
    643         /// <summary>
    644         /// Returns a collection containing this element and all child elements.
    645         /// </summary>
    646         public static IEnumerable<DependencyObject> ElementsAndSelf(this DependencyObject item)
    647         {
    648             yield return item;
    649 
    650             foreach (var child in item.Elements())
    651             {
    652                 yield return child;
    653             }
    654         }
    655 
    656         /// <summary>
    657         /// Returns a collection of descendant elements which match the given type.
    658         /// </summary>
    659         public static IEnumerable<DependencyObject> Descendants<T>(this DependencyObject item)
    660         {
    661             return item.Descendants().Where(i => i is T).Cast<DependencyObject>();
    662         }
    663 
    664         /// <summary>
    665         /// Returns a collection of the sibling elements before this node, in document order
    666         /// which match the given type.
    667         /// </summary>
    668         public static IEnumerable<DependencyObject> ElementsBeforeSelf<T>(this DependencyObject item)
    669         {
    670             return item.ElementsBeforeSelf().Where(i => i is T).Cast<DependencyObject>();
    671         }
    672 
    673         /// <summary>
    674         /// Returns a collection of the after elements after this node, in document order
    675         /// which match the given type.
    676         /// </summary>
    677         public static IEnumerable<DependencyObject> ElementsAfterSelf<T>(this DependencyObject item)
    678         {
    679             return item.ElementsAfterSelf().Where(i => i is T).Cast<DependencyObject>();
    680         }
    681 
    682         /// <summary>
    683         /// Returns a collection containing this element and all descendant elements
    684         /// which match the given type.
    685         /// </summary>
    686         public static IEnumerable<DependencyObject> DescendantsAndSelf<T>(this DependencyObject item)
    687         {
    688             return item.DescendantsAndSelf().Where(i => i is T).Cast<DependencyObject>();
    689         }
    690 
    691         /// <summary>
    692         /// Returns a collection of ancestor elements which match the given type.
    693         /// </summary>
    694         public static IEnumerable<DependencyObject> Ancestors<T>(this DependencyObject item)
    695         {
    696             return item.Ancestors().Where(i => i is T).Cast<DependencyObject>();
    697         }
    698 
    699         /// <summary>
    700         /// Returns a collection containing this element and all ancestor elements
    701         /// which match the given type.
    702         /// </summary>
    703         public static IEnumerable<DependencyObject> AncestorsAndSelf<T>(this DependencyObject item)
    704         {
    705             return item.AncestorsAndSelf().Where(i => i is T).Cast<DependencyObject>();
    706         }
    707 
    708         /// <summary>
    709         /// Returns a collection of child elements which match the given type.
    710         /// </summary>
    711         public static IEnumerable<DependencyObject> Elements<T>(this DependencyObject item)
    712         {
    713             return item.Elements().Where(i => i is T).Cast<DependencyObject>();
    714         }
    715 
    716         /// <summary>
    717         /// Returns a collection containing this element and all child elements.
    718         /// which match the given type.
    719         /// </summary>
    720         public static IEnumerable<DependencyObject> ElementsAndSelf<T>(this DependencyObject item)
    721         {
    722             return item.ElementsAndSelf().Where(i => i is T).Cast<DependencyObject>();
    723         }
    724 
    725     }
    726 
    727     public static class EnumerableTreeExtensions
    728     {
    729         /// <summary>
    730         /// Applies the given function to each of the items in the supplied
    731         /// IEnumerable.
    732         /// </summary>
    733         private static IEnumerable<DependencyObject> DrillDown(this IEnumerable<DependencyObject> items,
    734             Func<DependencyObject, IEnumerable<DependencyObject>> function)
    735         {
    736             foreach (var item in items)
    737             {
    738                 foreach (var itemChild in function(item))
    739                 {
    740                     yield return itemChild;
    741                 }
    742             }
    743         }
    744 
    745         /// <summary>
    746         /// Applies the given function to each of the items in the supplied
    747         /// IEnumerable, which match the given type.
    748         /// </summary>
    749         public static IEnumerable<DependencyObject> DrillDown<T>(this IEnumerable<DependencyObject> items,
    750             Func<DependencyObject, IEnumerable<DependencyObject>> function)
    751             where T : DependencyObject
    752         {
    753             foreach (var item in items)
    754             {
    755                 foreach (var itemChild in function(item))
    756                 {
    757                     if (itemChild is T)
    758                     {
    759                         yield return (T)itemChild;
    760                     }
    761                 }
    762             }
    763         }
    764 
    765         /// <summary>
    766         /// Returns a collection of descendant elements.
    767         /// </summary>
    768         public static IEnumerable<DependencyObject> Descendants(this IEnumerable<DependencyObject> items)
    769         {
    770             return items.DrillDown(i => i.Descendants());
    771         }
    772 
    773         /// <summary>
    774         /// Returns a collection containing this element and all descendant elements.
    775         /// </summary>
    776         public static IEnumerable<DependencyObject> DescendantsAndSelf(this IEnumerable<DependencyObject> items)
    777         {
    778             return items.DrillDown(i => i.DescendantsAndSelf());
    779         }
    780 
    781         /// <summary>
    782         /// Returns a collection of ancestor elements.
    783         /// </summary>
    784         public static IEnumerable<DependencyObject> Ancestors(this IEnumerable<DependencyObject> items)
    785         {
    786             return items.DrillDown(i => i.Ancestors());
    787         }
    788 
    789         /// <summary>
    790         /// Returns a collection containing this element and all ancestor elements.
    791         /// </summary>
    792         public static IEnumerable<DependencyObject> AncestorsAndSelf(this IEnumerable<DependencyObject> items)
    793         {
    794             return items.DrillDown(i => i.AncestorsAndSelf());
    795         }
    796 
    797         /// <summary>
    798         /// Returns a collection of child elements.
    799         /// </summary>
    800         public static IEnumerable<DependencyObject> Elements(this IEnumerable<DependencyObject> items)
    801         {
    802             return items.DrillDown(i => i.Elements());
    803         }
    804 
    805         /// <summary>
    806         /// Returns a collection containing this element and all child elements.
    807         /// </summary>
    808         public static IEnumerable<DependencyObject> ElementsAndSelf(this IEnumerable<DependencyObject> items)
    809         {
    810             return items.DrillDown(i => i.ElementsAndSelf());
    811         }
    812 
    813         /// <summary>
    814         /// Returns a collection of descendant elements which match the given type.
    815         /// </summary>
    816         public static IEnumerable<DependencyObject> Descendants<T>(this IEnumerable<DependencyObject> items)
    817             where T : DependencyObject
    818         {
    819             return items.DrillDown<T>(i => i.Descendants());
    820         }
    821 
    822         /// <summary>
    823         /// Returns a collection containing this element and all descendant elements.
    824         /// which match the given type.
    825         /// </summary>
    826         public static IEnumerable<DependencyObject> DescendantsAndSelf<T>(this IEnumerable<DependencyObject> items)
    827             where T : DependencyObject
    828         {
    829             return items.DrillDown<T>(i => i.DescendantsAndSelf());
    830         }
    831 
    832         /// <summary>
    833         /// Returns a collection of ancestor elements which match the given type.
    834         /// </summary>
    835         public static IEnumerable<DependencyObject> Ancestors<T>(this IEnumerable<DependencyObject> items)
    836             where T : DependencyObject
    837         {
    838             return items.DrillDown<T>(i => i.Ancestors());
    839         }
    840 
    841         /// <summary>
    842         /// Returns a collection containing this element and all ancestor elements.
    843         /// which match the given type.
    844         /// </summary>
    845         public static IEnumerable<DependencyObject> AncestorsAndSelf<T>(this IEnumerable<DependencyObject> items)
    846             where T : DependencyObject
    847         {
    848             return items.DrillDown<T>(i => i.AncestorsAndSelf());
    849         }
    850 
    851         /// <summary>
    852         /// Returns a collection of child elements which match the given type.
    853         /// </summary>
    854         public static IEnumerable<DependencyObject> Elements<T>(this IEnumerable<DependencyObject> items)
    855             where T : DependencyObject
    856         {
    857             return items.DrillDown<T>(i => i.Elements());
    858         }
    859 
    860         /// <summary>
    861         /// Returns a collection containing this element and all child elements.
    862         /// which match the given type.
    863         /// </summary>
    864         public static IEnumerable<DependencyObject> ElementsAndSelf<T>(this IEnumerable<DependencyObject> items)
    865             where T : DependencyObject
    866         {
    867             return items.DrillDown<T>(i => i.ElementsAndSelf());
    868         }
    869     }
    MetroInMotion.cs

    使用的时候只需要这样

    <toolkit:HubTile local:MetroInMotion.Tilt="1" Grid.Row="1" Grid.Column="1" Background="Red" Source="Assets/Tiles/FlipCycleTileSmall.png" Title="Metro" Message="This is Metro in App"/>

    高级的磁贴

    现在又说回开始菜单中的磁贴,在StandardTileData的属性中有一个Count专门表达这个App存在的通知数量,但是这个属性在后来的动态磁贴出现后而变得很少用(如下图左1),这部分内容并不是取代Count属性的动态磁贴的实现,而是关注另外个内置应用的磁贴——人脉(下图中间和右2)

    这里的人脉磁贴需要用到之前提过的动画效果,源码不是我编写的,我只是在网上找到老外写了那么的一个控件,cs部分的注释已经加了,xmal的由于基础不好现在还没看得明白,代码全部都铺上来,效果如下图

     1     public class PeopleHubTileData : INotifyPropertyChanged  
     2     {
     3         private ImageSource _ImageFront;
     4         public ImageSource ImageFront
     5         {
     6             get
     7             {
     8                 return _ImageFront;
     9             }
    10             set
    11             {
    12                 if (value != _ImageFront)
    13                 {
    14                     _ImageFront = value;
    15                     NotifyPropertyChanged("ImageFront");
    16                 }
    17             }
    18         }
    19 
    20         private ImageSource _ImageBack;
    21         public ImageSource ImageBack
    22         {
    23             get
    24             {
    25                 return _ImageBack;
    26             }
    27             set
    28             {
    29                 if (value != _ImageBack)
    30                 {
    31                     _ImageBack = value;
    32                     NotifyPropertyChanged("ImageBack");
    33                 }
    34             }
    35         }
    36 
    37         private Stretch _ImageStretch;
    38         public Stretch ImageStretch
    39         {
    40             get
    41             {
    42                 return _ImageStretch;
    43             }
    44             set
    45             {
    46                 if (value != _ImageStretch)
    47                 {
    48                     _ImageStretch = value;
    49                     NotifyPropertyChanged("ImageStretch");
    50                 }
    51             }
    52         }
    53 
    54 
    55       
    56 
    57         public event PropertyChangedEventHandler PropertyChanged;
    58         private void NotifyPropertyChanged(String propertyName)
    59         {
    60             PropertyChangedEventHandler handler = PropertyChanged;
    61             if (null != handler)
    62             {
    63                 handler(this, new PropertyChangedEventArgs(propertyName));
    64             }
    65         }
    66     }
    PepoleHubTileData
     1     public class Tiles : DependencyObject
     2     {
     3         public Tiles()
     4         {
     5             this.CenterOfRotationY = 0.5;
     6         }
     7         public Tiles(object item)
     8             : this()
     9         {
    10             this.TileData = item;
    11         }
    12         public object TileData { get; set; }
    13 
    14         public double CenterOfRotationY { get; set; }
    15         public double ZIndex
    16         {
    17             get { return (int)GetValue(ZIndexProperty); }
    18             set { SetValue(ZIndexProperty, value); }
    19         }
    20         public static DependencyProperty ZIndexProperty = DependencyProperty.Register("ZIndex", typeof(int), typeof(Tiles), new PropertyMetadata(0));
    21         public double RotationX
    22         {
    23             get { return (double)GetValue(RotationXProperty); }
    24             set { SetValue(RotationXProperty, value); }
    25         }
    26         public static DependencyProperty RotationXProperty = DependencyProperty.Register("RotationX", typeof(double), typeof(Tiles), new PropertyMetadata(0.0));
    27        
    28 
    29       
    30     }
    Tiles
      1     public class PeopleHubTile : ContentControl
      2     {
      3         #region Member variables
      4         private int LastAnimatedTile = 0;
      5         /// <summary>
      6         /// 大磁贴起始位置选择完毕,可以开始制造大磁贴
      7         /// </summary>
      8         private bool isBigTileAnimationStarted = false;
      9         /// <summary>
     10         /// 表明给大磁贴选择了图片
     11         /// </summary>
     12         private bool isBitImageSelected = false;
     13         /// <summary>
     14         /// 大磁贴图片的索引
     15         /// </summary>
     16         private int BitImageSelectedIndex = 0;
     17         /// <summary>
     18         /// 累计翻动大磁贴时已经翻动了小磁贴的数目
     19         /// </summary>
     20         private int TileAnimateIndex = 0;
     21         private int TileAnimationCount = 0;
     22         /// <summary>
     23         /// 所有磁贴进入就绪状态,可以开始选取大磁贴的起始位置
     24         /// </summary>
     25         private bool isReadyForBigTile = false;
     26         private Random RandomTile = new Random();
     27         private DispatcherTimer dispatcherTimer = new DispatcherTimer();
     28         private List<String> ImageUrl = new List<string>() 
     29         {
     30             "/Themes/Images/1.jpg",
     31             "/Themes/Images/13.jpg",
     32             "/Themes/Images/14.jpg",
     33             "/Themes/Images/15.jpg",
     34             "/Themes/Images/16.jpg",
     35             "/Themes/Images/17.jpg",
     36             "/Themes/Images/18.jpg",
     37             "/Themes/Images/19.jpg",
     38             "/Themes/Images/2.jpg",
     39             "/Themes/Images/20.jpg",
     40             "/Themes/Images/21.jpg",
     41             "/Themes/Images/3.jpg",
     42              
     43         };
     44 
     45 
     46 
     47         private ObservableCollection<Tiles> dataItems = new ObservableCollection<Tiles>()
     48         {
     49           new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/1.jpg", UriKind.RelativeOrAbsolute)) }),
     50           new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/2.jpg", UriKind.RelativeOrAbsolute)) }),
     51           new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/13.jpg", UriKind.RelativeOrAbsolute)) }),
     52           new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/14.jpg", UriKind.RelativeOrAbsolute)) }),
     53           new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/15.jpg", UriKind.RelativeOrAbsolute)) }),
     54           new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/16.jpg", UriKind.RelativeOrAbsolute)) }),
     55           new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/17.jpg", UriKind.RelativeOrAbsolute)) }),
     56           new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/18.jpg", UriKind.RelativeOrAbsolute)) }),
     57           new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/19.jpg", UriKind.RelativeOrAbsolute)) }),
     58         };
     59         #endregion
     60 
     61         #region Constructor
     62         public PeopleHubTile()
     63         {
     64             DefaultStyleKey = typeof(PeopleHubTile);
     65             Loaded += PeopleHubTile_Loaded;
     66 
     67         }
     68         #endregion
     69 
     70         #region Methods
     71         ListBox ItemsListBox;
     72         void PeopleHubTile_Loaded(object sender, RoutedEventArgs e)
     73         {
     74             ///在generic中获取ListBox 附上各个Tilt在ListBox中
     75             ItemsListBox = this.GetTemplateChild("ItemsListBox") as ListBox;
     76             this.ItemsListBox.ItemsSource = dataItems;
     77             //开启定时更换tile的任务
     78             dispatcherTimer.Interval = TimeSpan.FromSeconds(2);
     79             dispatcherTimer.Tick += dispatcherTimer_Tick;
     80             dispatcherTimer.Start();
     81         }
     82 
     83         void dispatcherTimer_Tick(object sender, EventArgs e)
     84         {
     85             //计数,如果是9个则尝试启动大磁贴
     86             TileAnimationCount++;
     87             if (TileAnimationCount > 9 && isReadyForBigTile == false)
     88             {
     89                 TileAnimationCount = 0;
     90                 isReadyForBigTile = true;
     91 
     92             }
     93 
     94 
     95             int AnimateItem = 0;
     96             Tiles AnimateTile = null;
     97             //未启动大磁贴的操作
     98             if (!isBigTileAnimationStarted)
     99             {
    100                 AnimateItem = RandomTile.Next(this.dataItems.Count);
    101                 AnimateTile = this.dataItems[AnimateItem];
    102 
    103                 ///尝试启动大磁贴 并且当前是抽到变换的磁贴是允许作为大磁贴的第一个磁贴
    104                 ///它变换大磁贴时是从大磁贴的左上 右上 左下 右下来变换
    105                 if (isReadyForBigTile && (AnimateItem == 0 || AnimateItem == 1 || AnimateItem == 3 || AnimateItem == 4))
    106                 {
    107                     LastAnimatedTile = AnimateItem;
    108                     isBigTileAnimationStarted = true;
    109                     TileAnimateIndex = 0;
    110                    
    111                 }
    112 
    113                 ///用ZIndex来区分正面和反面
    114                 /// Animate small tiles
    115                 if (AnimateTile.ZIndex == 0)
    116                 {
    117                     //back tile
    118                     PeopleHubTileData ItemData = AnimateTile.TileData as PeopleHubTileData;
    119 
    120                     int newImage = RandomTile.Next(ImageUrl.Count);
    121                     if (RandomTile.Next(20) > 5)
    122                         ItemData.ImageBack = new BitmapImage(new Uri(ImageUrl[newImage], UriKind.RelativeOrAbsolute));
    123                     else
    124                         ItemData.ImageBack = new BitmapImage(new Uri("", UriKind.RelativeOrAbsolute));
    125                 }
    126                 else if (AnimateTile.ZIndex != 0)
    127                 {
    128                     //front tile
    129                     PeopleHubTileData ItemData = AnimateTile.TileData as PeopleHubTileData;
    130 
    131                     int newImage = RandomTile.Next(ImageUrl.Count);
    132                     if (RandomTile.Next(20) > 5)
    133                         ItemData.ImageFront = new BitmapImage(new Uri(ImageUrl[newImage], UriKind.RelativeOrAbsolute));
    134                     else
    135                         ItemData.ImageFront = new BitmapImage(new Uri("", UriKind.RelativeOrAbsolute));
    136 
    137 
    138                 }
    139 
    140 
    141             }
    142 
    143 
    144             ///已经启用大磁贴 
    145             else if (isBigTileAnimationStarted && TileAnimateIndex < 4)
    146             {
    147 
    148                 int[] LastTiles = new int[4];
    149                 //按照大磁贴其实位置来选出大磁贴所占用的小磁贴的索引
    150                 switch (LastAnimatedTile)
    151                 {
    152                     case 0:
    153                         LastTiles = new int[4] { 0, 1, 3, 4 };
    154                         break;
    155                     case 1:
    156                         LastTiles = new int[4] { 1, 2, 4, 5 };
    157                         break;
    158                     case 3:
    159                         LastTiles = new int[4] { 3, 4, 6, 7 };
    160                         break;
    161                     case 4:
    162                         LastTiles = new int[4] { 4, 5, 7, 8 };
    163                         break;
    164                     default:
    165                         break;
    166 
    167                 }
    168 
    169 
    170               
    171                 AnimateTile = this.dataItems[LastTiles[TileAnimateIndex]];
    172                 ///还没有生成大磁贴所用的图片时
    173                 if (!isBitImageSelected)
    174                 {
    175                     isBitImageSelected = true;
    176                     BitImageSelectedIndex = RandomTile.Next(ImageUrl.Count);
    177                 }
    178                 ///bmpWB是直接从资源列表中拿的图片
    179                 BitmapImage bmpWB = new BitmapImage(new Uri(ImageUrl[BitImageSelectedIndex], UriKind.RelativeOrAbsolute));
    180                 ///最终写到磁贴上的部分图片
    181                 WriteableBitmap ImageWB = new WriteableBitmap(bmpWB.PixelWidth, bmpWB.PixelHeight);
    182                 bmpWB.CreateOptions = BitmapCreateOptions.None;
    183                 ///整幅大磁贴的图片
    184                 WriteableBitmap imageBitMap = new WriteableBitmap(bmpWB);
    185 
    186                 switch (TileAnimateIndex)
    187                 {
    188                     case 0:
    189 
    190                         ImageWB = GetCropImage(imageBitMap, 0, 0, imageBitMap.PixelWidth / 2, imageBitMap.PixelHeight / 2);
    191 
    192                         break;
    193                     case 1:
    194 
    195                         ImageWB = GetCropImage(imageBitMap, imageBitMap.PixelWidth / 2, 0, imageBitMap.PixelWidth / 2, imageBitMap.PixelHeight / 2);
    196                         break;
    197                     case 2:
    198 
    199                         ImageWB = GetCropImage(imageBitMap, 0, imageBitMap.PixelHeight / 2, imageBitMap.PixelWidth / 2, imageBitMap.PixelHeight / 2);
    200 
    201 
    202 
    203                         break;
    204                     case 3:
    205 
    206                         ImageWB = GetCropImage(imageBitMap, imageBitMap.PixelWidth / 2, imageBitMap.PixelHeight / 2, imageBitMap.PixelWidth / 2, imageBitMap.PixelHeight / 2);
    207                         break;
    208                     default:
    209                         break;
    210 
    211                 }
    212                 ///通过累计数目来判断大磁贴是否完成
    213                 TileAnimateIndex++;
    214                 if (TileAnimateIndex > 3)
    215                 {
    216                     isBigTileAnimationStarted = false;
    217                     isReadyForBigTile = false;
    218                     isBitImageSelected = false;
    219                 }
    220 
    221 
    222                 //animate part of big tiles
    223                 if (AnimateTile.ZIndex == 0)
    224                 {
    225                     //back tile
    226                     PeopleHubTileData ItemData = AnimateTile.TileData as PeopleHubTileData;
    227 
    228                     ItemData.ImageBack = ImageWB;
    229 
    230                 }
    231                 else if (AnimateTile.ZIndex != 0)
    232                 {
    233                     //front tile
    234                     PeopleHubTileData ItemData = AnimateTile.TileData as PeopleHubTileData;
    235 
    236                     ItemData.ImageFront = ImageWB;
    237                 }
    238 
    239 
    240             }
    241             //tile animation 
    242             Storyboard MyStory = new Storyboard();
    243             DoubleAnimation MyDouble = new DoubleAnimation();
    244             MyDouble.From = AnimateTile.RotationX;
    245             MyDouble.To = AnimateTile.RotationX + 180;
    246             MyDouble.Duration = TimeSpan.FromSeconds(0.5);
    247             Storyboard.SetTarget(MyDouble, AnimateTile);
    248             Storyboard.SetTargetProperty(MyDouble, new PropertyPath(Tiles.RotationXProperty));
    249             MyStory.Children.Add(MyDouble);
    250 
    251             ObjectAnimationUsingKeyFrames MyObject = new ObjectAnimationUsingKeyFrames();
    252             DiscreteObjectKeyFrame MyKeyFrame = new DiscreteObjectKeyFrame();
    253             MyKeyFrame.KeyTime = TimeSpan.FromSeconds(0);
    254             MyKeyFrame.Value = AnimateTile.ZIndex;
    255             MyObject.KeyFrames.Add(MyKeyFrame);
    256            
    257             MyKeyFrame = new DiscreteObjectKeyFrame();
    258             MyKeyFrame.KeyTime = TimeSpan.FromSeconds(0.3);
    259             MyKeyFrame.Value = (AnimateTile.ZIndex == 0) ? 1 : 0;
    260             MyObject.KeyFrames.Add(MyKeyFrame);
    261             Storyboard.SetTarget(MyObject, AnimateTile);
    262             Storyboard.SetTargetProperty(MyObject, new PropertyPath(Tiles.ZIndexProperty));
    263             MyStory.Children.Add(MyObject);
    264             MyStory.Begin();
    265 
    266 
    267 
    268         }
    269 
    270         /// <summary>
    271         /// 利用数组copy,通过计算位图的位置来切割出部分的图片
    272         /// </summary>
    273         /// <param name="aBitmapSource"></param>
    274         /// <param name="XPoint"></param>
    275         /// <param name="YPoint"></param>
    276         /// <param name="aWidth"></param>
    277         /// <param name="aHeight"></param>
    278         /// <returns></returns>
    279         private static WriteableBitmap GetCropImage(WriteableBitmap aBitmapSource, int XPoint, int YPoint, int aWidth, int aHeight)
    280         {
    281             var SourceWidth = aBitmapSource.PixelWidth;
    282             var result = new WriteableBitmap(aWidth, aHeight);
    283             for (var x = 0; x < aHeight - 1; x++)
    284             {
    285                 var Index = XPoint + (YPoint + x) * SourceWidth;
    286                 var FinalIndex = x * aWidth;
    287                 Array.Copy(aBitmapSource.Pixels, Index, result.Pixels, FinalIndex, aWidth);
    288 
    289 
    290             }
    291             return result;
    292         }
    293         #endregion
    294 
    295         #region OnApplyTemplate
    296         public override void OnApplyTemplate()
    297         {
    298             base.OnApplyTemplate();
    299         }
    300         #endregion
    301     }
    PepoleHubTile
     1 <ResourceDictionary
     2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     4     xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
     5     xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
     6     xmlns:local="clr-namespace:PeopleHubTileEx">
     7     
     8     <DataTemplate x:Key="DataTemplatePeopleHubTile">
     9         <Grid x:Name="TileGrid" Height="80" Width="80" >
    10             <Grid.Projection>
    11                 <PlaneProjection RotationX="{Binding RotationX}" CenterOfRotationY="{Binding CenterOfRotationX}">
    12                 </PlaneProjection>
    13             </Grid.Projection>
    14             <Grid x:Name="BackGrid" Canvas.ZIndex="{Binding ZIndex}" RenderTransformOrigin="0.5,0.5">
    15                 <Grid.RenderTransform>
    16                     <CompositeTransform ScaleY="-1"/>
    17                 </Grid.RenderTransform>
    18                 <Grid.Background>
    19                     <SolidColorBrush Color="Green"></SolidColorBrush>
    20                 </Grid.Background>
    21                 <Image Source="{Binding TileData.ImageBack}"  Stretch="Fill" />
    22             </Grid>
    23             <Grid x:Name="FrontGrid">
    24                 <Grid.Background>
    25                     <SolidColorBrush Color="Green"></SolidColorBrush>
    26                 </Grid.Background>
    27                 <Image  Source="{Binding TileData.ImageFront}"  Stretch="Fill" >
    28 
    29                 </Image>
    30             </Grid>
    31         </Grid>
    32     </DataTemplate>
    33   
    34     <Style TargetType="local:PeopleHubTile">
    35 
    36         <Setter Property="Template">
    37             <Setter.Value>
    38                 <ControlTemplate TargetType="local:PeopleHubTile">
    39                     <Grid>
    40                         <ListBox Name="ItemsListBox" Width="240" Height="240"
    41                  ScrollViewer.HorizontalScrollBarVisibility="Disabled"
    42                  ScrollViewer.VerticalScrollBarVisibility="Disabled"
    43                  ItemTemplate="{StaticResource DataTemplatePeopleHubTile}">
    44                             <ListBox.ItemsPanel>
    45                                 <ItemsPanelTemplate>
    46                                     <toolkit:WrapPanel ItemHeight="80" ItemWidth="80" Orientation="Horizontal">
    47 
    48                                     </toolkit:WrapPanel>
    49                                 </ItemsPanelTemplate>
    50                             </ListBox.ItemsPanel>
    51                         </ListBox>
    52                        
    53                     </Grid>
    54 
    55                 </ControlTemplate>
    56             </Setter.Value>
    57         </Setter>
    58     </Style>
    59 </ResourceDictionary>
    generic.xaml

    使用的时候只需要以下面的形式则可。

                <PeopleHubTileControl:PeopleHubTile  VerticalAlignment="Center">
                    
                </PeopleHubTileControl:PeopleHubTile>     
  • 相关阅读:
    vs2013配置opencv2.4.13(txt中复制粘贴即可,一次配置永久使用)
    描述性统计量
    Ubuntu创建、删除文件与目录
    Linux下服务器端开发流程及相关工具介绍(C++)
    TCP 协议如何保证可靠传输
    真实记录疑似Linux病毒导致服务器 带宽跑满的解决过程
    Windbg程序调试--转载
    原来问题在这里-我的memory leak诊断历程
    用WinDbg分析Debug Diagnostic Tool生成的Userdump文件
    一个内存增长问题的分析和处理(三)
  • 原文地址:https://www.cnblogs.com/HopeGi/p/4272462.html
Copyright © 2020-2023  润新知