WinPhone学习笔记(四)——磁贴
对每个Windows Phone的使用者来说,给他们的第一印象就是大大小小的磁贴——Metro,本篇介绍的是Windows Phone的磁贴,提到的有开始菜单的磁贴,也有在App里面的磁贴。
开始菜单的磁贴
首先介绍了一下每个磁贴的构造,每个磁贴分正反两面,正反两面都有图标,而正面有一个标题和统计数量(一般用作消息推送的时候用),在背面就有一个描述性的内容,下图就摘自MSDN上的图片,图中黑色字体其实就是每个磁贴数据类的属性,这个稍后会提到
对于一个磁贴来说,他的图片像素建议是173*173像素的,占用空间控制在80KB以内,它各个部分更详尽的数据大小如下图。
在开始菜单中的磁贴分两类,一类是App本身启动用的,通过在应用程序列表中创建的磁贴,叫应用程序磁贴;另一类是由App创建的,那个叫次要磁贴。
在控制开始菜单上的磁贴,主要有两个类,一个是StandandTileData,另一个是ShellTile。前者则是之前提过有一个类存储磁贴上的数据信息那个类,后者是负责管理开始菜单中的磁贴(包括了增删改查),但只局限于本App的磁贴。
下面的代码则磁贴创建的代码
StandardTileData tileData = new StandardTileData() { Title = "Test Tile", BackContent = "The " + ShellTile.ActiveTiles.Count() + " One", BackTitle = ShellTile.ActiveTiles.Count().ToString(), Count = ShellTile.ActiveTiles.Count() }; 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的效果不是很明显,而且有限制,有一些控件虽然用上了但也没有倾斜的效果。在网上查看资料时发现有个老外也写了一个倾斜效果,效果比微软提供的要明显,而且还可以调节倾斜的角度。两个类的代码如下
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Collections.Generic;
using System.Windows.Controls.Primitives; #if WINDOWS_PHONE
using Microsoft.Phone.Controls;
#endif namespace ControlTiltEffect
{
/// <summary>
/// This code provides attached properties for adding a 'tilt' effect to all controls within a container.
/// </summary>
public class TiltEffect : DependencyObject
{ #region Constructor and Static Constructor
/// <summary>
/// This is not a constructable class, but it cannot be static because it derives from DependencyObject.
/// </summary>
private TiltEffect()
{
} /// <summary>
/// Initialize the static properties
/// </summary>
static TiltEffect()
{
// The tiltable items list.
TiltableItems = new List<Type>() { typeof(ButtonBase), typeof(ListBoxItem) };
UseLogarithmicEase = false;
} #endregion #region Fields and simple properties // These constants are the same as the built-in effects
/// <summary>
/// Maximum amount of tilt, in radians
/// </summary>
const double MaxAngle = 0.3; /// <summary>
/// Maximum amount of depression, in pixels
/// </summary>
const double MaxDepression = ; /// <summary>
/// Delay between releasing an element and the tilt release animation playing
/// </summary>
static readonly TimeSpan TiltReturnAnimationDelay = TimeSpan.FromMilliseconds(); /// <summary>
/// Duration of tilt release animation
/// </summary>
static readonly TimeSpan TiltReturnAnimationDuration = TimeSpan.FromMilliseconds(); /// <summary>
/// The control that is currently being tilted
/// </summary>
static FrameworkElement currentTiltElement; /// <summary>
/// The single instance of a storyboard used for all tilts
/// </summary>
static Storyboard tiltReturnStoryboard; /// <summary>
/// The single instance of an X rotation used for all tilts
/// </summary>
static DoubleAnimation tiltReturnXAnimation; /// <summary>
/// The single instance of a Y rotation used for all tilts
/// </summary>
static DoubleAnimation tiltReturnYAnimation; /// <summary>
/// The single instance of a Z depression used for all tilts
/// </summary>
static DoubleAnimation tiltReturnZAnimation; /// <summary>
/// The center of the tilt element
/// </summary>
static Point currentTiltElementCenter; /// <summary>
/// Whether the animation just completed was for a 'pause' or not
/// </summary>
static bool wasPauseAnimation = false; /// <summary>
/// Whether to use a slightly more accurate (but slightly slower) tilt animation easing function
/// </summary>
public static bool UseLogarithmicEase { get; set; } /// <summary>
/// Default list of items that are tiltable
/// </summary>
public static List<Type> TiltableItems { get; private set; } #endregion #region Dependency properties /// <summary>
/// Whether the tilt effect is enabled on a container (and all its children)
/// </summary>
public static readonly DependencyProperty IsTiltEnabledProperty = DependencyProperty.RegisterAttached(
"IsTiltEnabled",
typeof(bool),
typeof(TiltEffect),
new PropertyMetadata(OnIsTiltEnabledChanged)
); /// <summary>
/// Gets the IsTiltEnabled dependency property from an object
/// </summary>
/// <param name="source">The object to get the property from</param>
/// <returns>The property's value</returns>
public static bool GetIsTiltEnabled(DependencyObject source) { return (bool)source.GetValue(IsTiltEnabledProperty); } /// <summary>
/// Sets the IsTiltEnabled dependency property on an object
/// </summary>
/// <param name="source">The object to set the property on</param>
/// <param name="value">The value to set</param>
public static void SetIsTiltEnabled(DependencyObject source, bool value) { source.SetValue(IsTiltEnabledProperty, value); } /// <summary>
/// Suppresses the tilt effect on a single control that would otherwise be tilted
/// </summary>
public static readonly DependencyProperty SuppressTiltProperty = DependencyProperty.RegisterAttached(
"SuppressTilt",
typeof(bool),
typeof(TiltEffect),
null
); /// <summary>
/// Gets the SuppressTilt dependency property from an object
/// </summary>
/// <param name="source">The object to get the property from</param>
/// <returns>The property's value</returns>
public static bool GetSuppressTilt(DependencyObject source) { return (bool)source.GetValue(SuppressTiltProperty); } /// <summary>
/// Sets the SuppressTilt dependency property from an object
/// </summary>
/// <param name="source">The object to get the property from</param>
/// <returns>The property's value</returns>
public static void SetSuppressTilt(DependencyObject source, bool value) { source.SetValue(SuppressTiltProperty, value); } /// <summary>
/// Property change handler for the IsTiltEnabled dependency property
/// </summary>
/// <param name="target">The element that the property is atteched to</param>
/// <param name="args">Event args</param>
/// <remarks>
/// Adds or removes event handlers from the element that has been (un)registered for tilting
/// </remarks>
static void OnIsTiltEnabledChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
{
if (target is FrameworkElement)
{
// Add / remove the event handler if necessary
if ((bool)args.NewValue == true)
{
(target as FrameworkElement).ManipulationStarted += TiltEffect_ManipulationStarted;
}
else
{
(target as FrameworkElement).ManipulationStarted -= TiltEffect_ManipulationStarted;
}
}
} #endregion #region Top-level manipulation event handlers /// <summary>
/// Event handler for ManipulationStarted
/// </summary>
/// <param name="sender">sender of the event - this will be the tilt container (eg, entire page)</param>
/// <param name="e">event args</param>
static void TiltEffect_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{ TryStartTiltEffect(sender as FrameworkElement, e);
} /// <summary>
/// Event handler for ManipulationDelta
/// </summary>
/// <param name="sender">sender of the event - this will be the tilting object (eg a button)</param>
/// <param name="e">event args</param>
static void TiltEffect_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{ ContinueTiltEffect(sender as FrameworkElement, e);
} /// <summary>
/// Event handler for ManipulationCompleted
/// </summary>
/// <param name="sender">sender of the event - this will be the tilting object (eg a button)</param>
/// <param name="e">event args</param>
static void TiltEffect_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{ EndTiltEffect(currentTiltElement);
} #endregion #region Core tilt logic /// <summary>
/// Checks if the manipulation should cause a tilt, and if so starts the tilt effect
/// </summary>
/// <param name="source">The source of the manipulation (the tilt container, eg entire page)</param>
/// <param name="e">The args from the ManipulationStarted event</param>
static void TryStartTiltEffect(FrameworkElement source, ManipulationStartedEventArgs e)
{
foreach (FrameworkElement ancestor in (e.OriginalSource as FrameworkElement).GetVisualAncestors())
{
foreach (Type t in TiltableItems)
{
if (t.IsAssignableFrom(ancestor.GetType()))
{
if ((bool)ancestor.GetValue(SuppressTiltProperty) != true)
{
// Use first child of the control, so that you can add transforms and not
// impact any transforms on the control itself
FrameworkElement element = VisualTreeHelper.GetChild(ancestor, ) as FrameworkElement;
FrameworkElement container = e.ManipulationContainer as FrameworkElement; if (element == null || container == null)
return; // Touch point relative to the element being tilted
Point tiltTouchPoint = container.TransformToVisual(element).Transform(e.ManipulationOrigin); // Center of the element being tilted
Point elementCenter = new Point(element.ActualWidth / , element.ActualHeight / ); // Camera adjustment
Point centerToCenterDelta = GetCenterToCenterDelta(element, source); BeginTiltEffect(element, tiltTouchPoint, elementCenter, centerToCenterDelta);
return;
}
}
}
}
} /// <summary>
/// Computes the delta between the centre of an element and its container
/// </summary>
/// <param name="element">The element to compare</param>
/// <param name="container">The element to compare against</param>
/// <returns>A point that represents the delta between the two centers</returns>
static Point GetCenterToCenterDelta(FrameworkElement element, FrameworkElement container)
{
Point elementCenter = new Point(element.ActualWidth / , element.ActualHeight / );
Point containerCenter; #if WINDOWS_PHONE // Need to special-case the frame to handle different orientations
if (container is PhoneApplicationFrame)
{
PhoneApplicationFrame frame = container as PhoneApplicationFrame; // Switch width and height in landscape mode
if ((frame.Orientation & PageOrientation.Landscape) == PageOrientation.Landscape)
{ containerCenter = new Point(container.ActualHeight / , container.ActualWidth / );
}
else
containerCenter = new Point(container.ActualWidth / , container.ActualHeight / );
}
else
containerCenter = new Point(container.ActualWidth / , container.ActualHeight / );
#else containerCenter = new Point(container.ActualWidth / , container.ActualHeight / ); #endif Point transformedElementCenter = element.TransformToVisual(container).Transform(elementCenter);
Point result = new Point(containerCenter.X - transformedElementCenter.X, containerCenter.Y - transformedElementCenter.Y); return result;
} /// <summary>
/// Begins the tilt effect by preparing the control and doing the initial animation
/// </summary>
/// <param name="element">The element to tilt </param>
/// <param name="touchPoint">The touch point, in element coordinates</param>
/// <param name="centerPoint">The center point of the element in element coordinates</param>
/// <param name="centerDelta">The delta between the <paramref name="element"/>'s center and
/// the container's center</param>
static void BeginTiltEffect(FrameworkElement element, Point touchPoint, Point centerPoint, Point centerDelta)
{ if (tiltReturnStoryboard != null)
StopTiltReturnStoryboardAndCleanup(); if (PrepareControlForTilt(element, centerDelta) == false)
return; currentTiltElement = element;
currentTiltElementCenter = centerPoint;
PrepareTiltReturnStoryboard(element); ApplyTiltEffect(currentTiltElement, touchPoint, currentTiltElementCenter);
} /// <summary>
/// Prepares a control to be tilted by setting up a plane projection and some event handlers
/// </summary>
/// <param name="element">The control that is to be tilted</param>
/// <param name="centerDelta">Delta between the element's center and the tilt container's</param>
/// <returns>true if successful; false otherwise</returns>
/// <remarks>
/// This method is conservative; it will fail any attempt to tilt a control that already
/// has a projection on it
/// </remarks>
static bool PrepareControlForTilt(FrameworkElement element, Point centerDelta)
{
// Prevents interference with any existing transforms
if (element.Projection != null || (element.RenderTransform != null && element.RenderTransform.GetType() != typeof(MatrixTransform)))
return false; TranslateTransform transform = new TranslateTransform();
transform.X = centerDelta.X;
transform.Y = centerDelta.Y;
element.RenderTransform = transform; PlaneProjection projection = new PlaneProjection();
projection.GlobalOffsetX = - * centerDelta.X;
projection.GlobalOffsetY = - * centerDelta.Y;
element.Projection = projection; element.ManipulationDelta += TiltEffect_ManipulationDelta;
element.ManipulationCompleted += TiltEffect_ManipulationCompleted; return true;
} /// <summary>
/// Removes modifications made by PrepareControlForTilt
/// </summary>
/// <param name="element">THe control to be un-prepared</param>
/// <remarks>
/// This method is basic; it does not do anything to detect if the control being un-prepared
/// was previously prepared
/// </remarks>
static void RevertPrepareControlForTilt(FrameworkElement element)
{
element.ManipulationDelta -= TiltEffect_ManipulationDelta;
element.ManipulationCompleted -= TiltEffect_ManipulationCompleted;
element.Projection = null;
element.RenderTransform = null;
} /// <summary>
/// Creates the tilt return storyboard (if not already created) and targets it to the projection
/// </summary>
/// <param name="projection">the projection that should be the target of the animation</param>
static void PrepareTiltReturnStoryboard(FrameworkElement element)
{ if (tiltReturnStoryboard == null)
{
tiltReturnStoryboard = new Storyboard();
tiltReturnStoryboard.Completed += TiltReturnStoryboard_Completed; tiltReturnXAnimation = new DoubleAnimation();
Storyboard.SetTargetProperty(tiltReturnXAnimation, new PropertyPath(PlaneProjection.RotationXProperty));
tiltReturnXAnimation.BeginTime = TiltReturnAnimationDelay;
tiltReturnXAnimation.To = ;
tiltReturnXAnimation.Duration = TiltReturnAnimationDuration; tiltReturnYAnimation = new DoubleAnimation();
Storyboard.SetTargetProperty(tiltReturnYAnimation, new PropertyPath(PlaneProjection.RotationYProperty));
tiltReturnYAnimation.BeginTime = TiltReturnAnimationDelay;
tiltReturnYAnimation.To = ;
tiltReturnYAnimation.Duration = TiltReturnAnimationDuration; tiltReturnZAnimation = new DoubleAnimation();
Storyboard.SetTargetProperty(tiltReturnZAnimation, new PropertyPath(PlaneProjection.GlobalOffsetZProperty));
tiltReturnZAnimation.BeginTime = TiltReturnAnimationDelay;
tiltReturnZAnimation.To = ;
tiltReturnZAnimation.Duration = TiltReturnAnimationDuration; if (UseLogarithmicEase)
{
tiltReturnXAnimation.EasingFunction = new LogarithmicEase();
tiltReturnYAnimation.EasingFunction = new LogarithmicEase();
tiltReturnZAnimation.EasingFunction = new LogarithmicEase();
} tiltReturnStoryboard.Children.Add(tiltReturnXAnimation);
tiltReturnStoryboard.Children.Add(tiltReturnYAnimation);
tiltReturnStoryboard.Children.Add(tiltReturnZAnimation);
} Storyboard.SetTarget(tiltReturnXAnimation, element.Projection);
Storyboard.SetTarget(tiltReturnYAnimation, element.Projection);
Storyboard.SetTarget(tiltReturnZAnimation, element.Projection);
} /// <summary>
/// Continues a tilt effect that is currently applied to an element, presumably because
/// the user moved their finger
/// </summary>
/// <param name="element">The element being tilted</param>
/// <param name="e">The manipulation event args</param>
static void ContinueTiltEffect(FrameworkElement element, ManipulationDeltaEventArgs e)
{
FrameworkElement container = e.ManipulationContainer as FrameworkElement;
if (container == null || element == null)
return; Point tiltTouchPoint = container.TransformToVisual(element).Transform(e.ManipulationOrigin); // If touch moved outside bounds of element, then pause the tilt (but don't cancel it)
if (new Rect(, , currentTiltElement.ActualWidth, currentTiltElement.ActualHeight).Contains(tiltTouchPoint) != true)
{ PauseTiltEffect();
return;
} // Apply the updated tilt effect
ApplyTiltEffect(currentTiltElement, e.ManipulationOrigin, currentTiltElementCenter);
} /// <summary>
/// Ends the tilt effect by playing the animation
/// </summary>
/// <param name="element">The element being tilted</param>
static void EndTiltEffect(FrameworkElement element)
{
if (element != null)
{
element.ManipulationCompleted -= TiltEffect_ManipulationCompleted;
element.ManipulationDelta -= TiltEffect_ManipulationDelta;
} if (tiltReturnStoryboard != null)
{
wasPauseAnimation = false;
if (tiltReturnStoryboard.GetCurrentState() != ClockState.Active)
tiltReturnStoryboard.Begin();
}
else
StopTiltReturnStoryboardAndCleanup();
} /// <summary>
/// Handler for the storyboard complete event
/// </summary>
/// <param name="sender">sender of the event</param>
/// <param name="e">event args</param>
static void TiltReturnStoryboard_Completed(object sender, EventArgs e)
{
if (wasPauseAnimation)
ResetTiltEffect(currentTiltElement);
else
StopTiltReturnStoryboardAndCleanup();
} /// <summary>
/// Resets the tilt effect on the control, making it appear 'normal' again
/// </summary>
/// <param name="element">The element to reset the tilt on</param>
/// <remarks>
/// This method doesn't turn off the tilt effect or cancel any current
/// manipulation; it just temporarily cancels the effect
/// </remarks>
static void ResetTiltEffect(FrameworkElement element)
{
PlaneProjection projection = element.Projection as PlaneProjection;
projection.RotationY = ;
projection.RotationX = ;
projection.GlobalOffsetZ = ;
} /// <summary>
/// Stops the tilt effect and release resources applied to the currently-tilted control
/// </summary>
static void StopTiltReturnStoryboardAndCleanup()
{
if (tiltReturnStoryboard != null)
tiltReturnStoryboard.Stop(); RevertPrepareControlForTilt(currentTiltElement);
} /// <summary>
/// Pauses the tilt effect so that the control returns to the 'at rest' position, but doesn't
/// stop the tilt effect (handlers are still attached, etc.)
/// </summary>
static void PauseTiltEffect()
{
if ((tiltReturnStoryboard != null) && !wasPauseAnimation)
{
tiltReturnStoryboard.Stop();
wasPauseAnimation = true;
tiltReturnStoryboard.Begin();
}
} /// <summary>
/// Resets the storyboard to not running
/// </summary>
private static void ResetTiltReturnStoryboard()
{
tiltReturnStoryboard.Stop();
wasPauseAnimation = false;
} /// <summary>
/// Applies the tilt effect to the control
/// </summary>
/// <param name="element">the control to tilt</param>
/// <param name="touchPoint">The touch point, in the container's coordinates</param>
/// <param name="centerPoint">The center point of the container</param>
static void ApplyTiltEffect(FrameworkElement element, Point touchPoint, Point centerPoint)
{
// Stop any active animation
ResetTiltReturnStoryboard(); // Get relative point of the touch in percentage of container size
Point normalizedPoint = new Point(
Math.Min(Math.Max(touchPoint.X / (centerPoint.X * ), ), ),
Math.Min(Math.Max(touchPoint.Y / (centerPoint.Y * ), ), )); // Shell values
double xMagnitude = Math.Abs(normalizedPoint.X - 0.5);
double yMagnitude = Math.Abs(normalizedPoint.Y - 0.5);
double xDirection = -Math.Sign(normalizedPoint.X - 0.5);
double yDirection = Math.Sign(normalizedPoint.Y - 0.5);
double angleMagnitude = xMagnitude + yMagnitude;
double xAngleContribution = xMagnitude + yMagnitude > ? xMagnitude / (xMagnitude + yMagnitude) : ; double angle = angleMagnitude * MaxAngle * / Math.PI;
double depression = ( - angleMagnitude) * MaxDepression; // RotationX and RotationY are the angles of rotations about the x- or y-*axis*;
// to achieve a rotation in the x- or y-*direction*, we need to swap the two.
// That is, a rotation to the left about the y-axis is a rotation to the left in the x-direction,
// and a rotation up about the x-axis is a rotation up in the y-direction.
PlaneProjection projection = element.Projection as PlaneProjection;
projection.RotationY = angle * xAngleContribution * xDirection;
projection.RotationX = angle * ( - xAngleContribution) * yDirection;
projection.GlobalOffsetZ = -depression;
} #endregion #region Custom easing function /// <summary>
/// Provides an easing function for the tilt return
/// </summary>
private class LogarithmicEase : EasingFunctionBase
{
/// <summary>
/// Computes the easing function
/// </summary>
/// <param name="normalizedTime">The time</param>
/// <returns>The eased value</returns>
protected override double EaseInCore(double normalizedTime)
{
return Math.Log(normalizedTime + ) / 0.693147181; // ln(t + 1) / ln(2)
}
} #endregion
} /// <summary>
/// Couple of simple helpers for walking the visual tree
/// </summary>
static class TreeHelpers
{
/// <summary>
/// Gets the ancestors of the element, up to the root
/// </summary>
/// <param name="node">The element to start from</param>
/// <returns>An enumerator of the ancestors</returns>
public static IEnumerable<FrameworkElement> GetVisualAncestors(this FrameworkElement node)
{
FrameworkElement parent = node.GetVisualParent();
while (parent != null)
{
yield return parent;
parent = parent.GetVisualParent();
}
} /// <summary>
/// Gets the visual parent of the element
/// </summary>
/// <param name="node">The element to check</param>
/// <returns>The visual parent</returns>
public static FrameworkElement GetVisualParent(this FrameworkElement node)
{
return VisualTreeHelper.GetParent(node) as FrameworkElement;
}
}
}
TiltEffect.cs
public static class MetroInMotion
{
#region AnimationLevel public static int GetAnimationLevel(DependencyObject obj)
{
return (int)obj.GetValue(AnimationLevelProperty);
} public static void SetAnimationLevel(DependencyObject obj, int value)
{
obj.SetValue(AnimationLevelProperty, value);
} public static readonly DependencyProperty AnimationLevelProperty =
DependencyProperty.RegisterAttached("AnimationLevel", typeof(int),
typeof(MetroInMotion), new PropertyMetadata(-)); #endregion #region Tilt public static double GetTilt(DependencyObject obj)
{
return (double)obj.GetValue(TiltProperty);
} public static void SetTilt(DependencyObject obj, double value)
{
obj.SetValue(TiltProperty, value);
} public static readonly DependencyProperty TiltProperty =
DependencyProperty.RegisterAttached("Tilt", typeof(double),
typeof(MetroInMotion), new PropertyMetadata(2.0, OnTiltChanged)); /// <summary>
/// The extent of the tilt action, the larger the number, the bigger the tilt
/// </summary>
private static double TiltAngleFactor = ; /// <summary>
/// The extent of the scaling action, the smaller the number, the greater the scaling.
/// </summary>
private static double ScaleFactor = ; private static void OnTiltChanged(DependencyObject d,
DependencyPropertyChangedEventArgs args)
{
FrameworkElement targetElement = d as FrameworkElement; double tiltFactor = GetTilt(d); // create the required transformations
var projection = new PlaneProjection();
var scale = new ScaleTransform();
var translate = new TranslateTransform(); var transGroup = new TransformGroup();
transGroup.Children.Add(scale);
transGroup.Children.Add(translate); // associate with the target element
targetElement.Projection = projection;
targetElement.RenderTransform = transGroup;
targetElement.RenderTransformOrigin = new Point(0.5, 0.5); targetElement.MouseLeftButtonDown += (s, e) =>
{
var clickPosition = e.GetPosition(targetElement); // find the maximum of width / height
double maxDimension = Math.Max(targetElement.ActualWidth, targetElement.ActualHeight); // compute the normalised horizontal distance from the centre
double distanceFromCenterX = targetElement.ActualWidth / - clickPosition.X;
double normalisedDistanceX = * distanceFromCenterX / maxDimension; // rotate around the Y axis
projection.RotationY = normalisedDistanceX * TiltAngleFactor * tiltFactor; // compute the normalised vertical distance from the centre
double distanceFromCenterY = targetElement.ActualHeight / - clickPosition.Y;
double normalisedDistanceY = * distanceFromCenterY / maxDimension; // rotate around the X axis,
projection.RotationX = -normalisedDistanceY * TiltAngleFactor * tiltFactor; // find the distance to centre
double distanceToCentre = Math.Sqrt(normalisedDistanceX * normalisedDistanceX
+ normalisedDistanceY * normalisedDistanceY); // scale accordingly
double scaleVal = tiltFactor * ( - distanceToCentre) / ScaleFactor;
scale.ScaleX = - scaleVal;
scale.ScaleY = - scaleVal; // offset the plane transform
var rootElement = Application.Current.RootVisual as FrameworkElement;
var relativeToCentre = (targetElement.GetRelativePosition(rootElement).Y - rootElement.ActualHeight / ) / ;
translate.Y = -relativeToCentre;
projection.LocalOffsetY = +relativeToCentre; }; targetElement.ManipulationCompleted += (s, e) =>
{
var sb = new Storyboard();
sb.Children.Add(CreateAnimation(null, , 0.1, "RotationY", projection));
sb.Children.Add(CreateAnimation(null, , 0.1, "RotationX", projection));
sb.Children.Add(CreateAnimation(null, , 0.1, "ScaleX", scale));
sb.Children.Add(CreateAnimation(null, , 0.1, "ScaleY", scale));
sb.Begin(); translate.Y = ;
projection.LocalOffsetY = ;
}; } #endregion #region IsPivotAnimated public static bool GetIsPivotAnimated(DependencyObject obj)
{
return (bool)obj.GetValue(IsPivotAnimatedProperty);
} public static void SetIsPivotAnimated(DependencyObject obj, bool value)
{
obj.SetValue(IsPivotAnimatedProperty, value);
} public static readonly DependencyProperty IsPivotAnimatedProperty =
DependencyProperty.RegisterAttached("IsPivotAnimated", typeof(bool),
typeof(MetroInMotion), new PropertyMetadata(false, OnIsPivotAnimatedChanged)); private static void OnIsPivotAnimatedChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
{
ItemsControl list = d as ItemsControl; list.Loaded += (s2, e2) =>
{
// locate the pivot control that this list is within
Pivot pivot = list.Ancestors<Pivot>().Single() as Pivot; // and its index within the pivot
int pivotIndex = pivot.Items.IndexOf(list.Ancestors<PivotItem>().Single()); bool selectionChanged = false; pivot.SelectionChanged += (s3, e3) =>
{
selectionChanged = true;
}; // handle manipulation events which occur when the user
// moves between pivot items
pivot.ManipulationCompleted += (s, e) =>
{
if (!selectionChanged)
return; selectionChanged = false; if (pivotIndex != pivot.SelectedIndex)
return; // determine which direction this tab will be scrolling in from
bool fromRight = e.TotalManipulation.Translation.X <= ; // iterate over each of the items in view
var items = list.GetItemsInView().ToList();
for (int index = ; index < items.Count; index++)
{
var lbi = items[index]; list.Dispatcher.BeginInvoke(() =>
{
var animationTargets = lbi.Descendants()
.Where(p => MetroInMotion.GetAnimationLevel(p) > -);
foreach (FrameworkElement target in animationTargets)
{
// trigger the required animation
GetSlideAnimation(target, fromRight).Begin();
}
});
}; };
};
} #endregion /// <summary>
/// Animates each element in order, creating a 'peel' effect. The supplied action
/// is invoked when the animation ends.
/// </summary>
public static void Peel(this IEnumerable<FrameworkElement> elements, Action endAction)
{
var elementList = elements.ToList();
var lastElement = elementList.Last(); // iterate over all the elements, animating each of them
double delay = ;
foreach (FrameworkElement element in elementList)
{
var sb = GetPeelAnimation(element, delay); // add a Completed event handler to the last element
if (element.Equals(lastElement))
{
sb.Completed += (s, e) =>
{
endAction();
};
} sb.Begin();
delay += ;
}
} /// <summary>
/// Enumerates all the items that are currently visible in am ItemsControl. This implementation assumes
/// that a VirtualizingStackPanel is being used as the ItemsPanel.
/// </summary>
public static IEnumerable<FrameworkElement> GetItemsInView(this ItemsControl itemsControl)
{
// locate the stack panel that hosts the items
VirtualizingStackPanel vsp = itemsControl.Descendants<VirtualizingStackPanel>().First() as VirtualizingStackPanel; // iterate over each of the items in view
int firstVisibleItem = (int)vsp.VerticalOffset;
int visibleItemCount = (int)vsp.ViewportHeight;
for (int index = firstVisibleItem; index <= firstVisibleItem + visibleItemCount + ; index++)
{
var item = itemsControl.ItemContainerGenerator.ContainerFromIndex(index) as FrameworkElement;
if (item == null)
continue; yield return item;
}
} /// <summary>
/// Creates a PlaneProjection and associates it with the given element, returning
/// a Storyboard which will animate the PlaneProjection to 'peel' the item
/// from the screen.
/// </summary>
private static Storyboard GetPeelAnimation(FrameworkElement element, double delay)
{
Storyboard sb; var projection = new PlaneProjection()
{
CenterOfRotationX = -0.1
};
element.Projection = projection; // compute the angle of rotation required to make this element appear
// at a 90 degree angle at the edge of the screen.
var width = element.ActualWidth;
var targetAngle = Math.Atan( / (width / ));
targetAngle = targetAngle * / Math.PI; // animate the projection
sb = new Storyboard();
sb.BeginTime = TimeSpan.FromMilliseconds(delay);
sb.Children.Add(CreateAnimation(, -( - targetAngle), 0.3, "RotationY", projection));
sb.Children.Add(CreateAnimation(, , 0.3, "RotationZ", projection));
sb.Children.Add(CreateAnimation(, -, 0.3, "GlobalOffsetZ", projection));
return sb;
} private static DoubleAnimation CreateAnimation(double? from, double? to, double duration,
string targetProperty, DependencyObject target)
{
var db = new DoubleAnimation();
db.To = to;
db.From = from;
db.EasingFunction = new SineEase();
db.Duration = TimeSpan.FromSeconds(duration);
Storyboard.SetTarget(db, target);
Storyboard.SetTargetProperty(db, new PropertyPath(targetProperty));
return db;
} /// <summary>
/// Creates a TranslateTransform and associates it with the given element, returning
/// a Storyboard which will animate the TranslateTransform with a SineEase function
/// </summary>
private static Storyboard GetSlideAnimation(FrameworkElement element, bool fromRight)
{
double from = fromRight ? : -; Storyboard sb;
double delay = (MetroInMotion.GetAnimationLevel(element)) * 0.1 + 0.1; TranslateTransform trans = new TranslateTransform() { X = from };
element.RenderTransform = trans; sb = new Storyboard();
sb.BeginTime = TimeSpan.FromSeconds(delay);
sb.Children.Add(CreateAnimation(from, , 0.8, "X", trans));
return sb;
} } public static class ExtensionMethods
{
public static Point GetRelativePosition(this UIElement element, UIElement other)
{
return element.TransformToVisual(other)
.Transform(new Point(, ));
}
} public class ItemFlyInAndOutAnimations
{
private Popup _popup; private Canvas _popupCanvas; private FrameworkElement _targetElement; private Point _targetElementPosition; private Image _targetElementClone; private Rectangle _backgroundMask; private static TimeSpan _flyInSpeed = TimeSpan.FromMilliseconds(); private static TimeSpan _flyOutSpeed = TimeSpan.FromMilliseconds(); public ItemFlyInAndOutAnimations()
{
// construct a popup, with a Canvas as its child
_popup = new Popup();
_popupCanvas = new Canvas();
_popup.Child = _popupCanvas;
} public static void TitleFlyIn(FrameworkElement title)
{
TranslateTransform trans = new TranslateTransform();
trans.X = ;
trans.Y = -;
title.RenderTransform = trans; var sb = new Storyboard(); // animate the X position
var db = CreateDoubleAnimation(, ,
new SineEase(), trans, TranslateTransform.XProperty, _flyInSpeed);
sb.Children.Add(db); // animate the Y position
db = CreateDoubleAnimation(-, ,
new SineEase(), trans, TranslateTransform.YProperty, _flyInSpeed);
sb.Children.Add(db); sb.Begin();
} /// <summary>
/// Animate the previously 'flown-out' element back to its original location.
/// </summary>
public void ItemFlyIn()
{
if (_popupCanvas.Children.Count != )
return; _popup.IsOpen = true;
_backgroundMask.Opacity = 0.0; Image animatedImage = _popupCanvas.Children[] as Image; var sb = new Storyboard(); // animate the X position
var db = CreateDoubleAnimation(_targetElementPosition.X - , _targetElementPosition.X,
new SineEase(),
_targetElementClone, Canvas.LeftProperty, _flyInSpeed);
sb.Children.Add(db); // animate the Y position
db = CreateDoubleAnimation(_targetElementPosition.Y - , _targetElementPosition.Y,
new SineEase(),
_targetElementClone, Canvas.TopProperty, _flyInSpeed);
sb.Children.Add(db); sb.Completed += (s, e) =>
{
// when the animation has finished, hide the popup once more
_popup.IsOpen = false; // restore the element we have animated
_targetElement.Opacity = 1.0; // and get rid of our clone
_popupCanvas.Children.Clear();
}; sb.Begin();
} /// <summary>
/// Animate the given element so that it flies off screen, fading
/// everything else that is on screen.
/// </summary>
public void ItemFlyOut(FrameworkElement element, Action action)
{
_targetElement = element;
var rootElement = Application.Current.RootVisual as FrameworkElement; _backgroundMask = new Rectangle()
{
Fill = new SolidColorBrush(Colors.Black),
Opacity = 0.0,
Width = rootElement.ActualWidth,
Height = rootElement.ActualHeight
};
_popupCanvas.Children.Add(_backgroundMask); _targetElementClone = new Image()
{
Source = new WriteableBitmap(element, null)
};
_popupCanvas.Children.Add(_targetElementClone); _targetElementPosition = element.GetRelativePosition(rootElement);
Canvas.SetTop(_targetElementClone, _targetElementPosition.Y);
Canvas.SetLeft(_targetElementClone, _targetElementPosition.X); var sb = new Storyboard(); // animate the X position
var db = CreateDoubleAnimation(_targetElementPosition.X, _targetElementPosition.X + ,
new SineEase() { EasingMode = EasingMode.EaseIn },
_targetElementClone, Canvas.LeftProperty, _flyOutSpeed);
sb.Children.Add(db); // animate the Y position
db = CreateDoubleAnimation(_targetElementPosition.Y, _targetElementPosition.Y + ,
new SineEase() { EasingMode = EasingMode.EaseOut },
_targetElementClone, Canvas.TopProperty, _flyOutSpeed);
sb.Children.Add(db); // fade out the other elements
db = CreateDoubleAnimation(, ,
null, _backgroundMask, UIElement.OpacityProperty, _flyOutSpeed);
sb.Children.Add(db); sb.Completed += (s, e2) =>
{
action(); // hide the popup, by placing a task on the dispatcher queue, this
// should be executed after the navigation has occurred
element.Dispatcher.BeginInvoke(() =>
{
_popup.IsOpen = false;
});
}; // hide the element we have 'cloned' into the popup
element.Opacity = 0.0; // open the popup
_popup.IsOpen = true; // begin the animation
sb.Begin();
} public static DoubleAnimation CreateDoubleAnimation(double from, double to, IEasingFunction easing,
DependencyObject target, object propertyPath, TimeSpan duration)
{
var db = new DoubleAnimation();
db.To = to;
db.From = from;
db.EasingFunction = easing;
db.Duration = duration;
Storyboard.SetTarget(db, target);
Storyboard.SetTargetProperty(db, new PropertyPath(propertyPath));
return db;
}
} public class VisualTreeAdapter : ILinqTree<DependencyObject>
{
private DependencyObject _item; public VisualTreeAdapter(DependencyObject item)
{
_item = item;
} public IEnumerable<DependencyObject> Children()
{
int childrenCount = VisualTreeHelper.GetChildrenCount(_item);
for (int i = ; i < childrenCount; i++)
{
yield return VisualTreeHelper.GetChild(_item, i);
}
} public DependencyObject Parent
{
get
{
return VisualTreeHelper.GetParent(_item);
}
}
} public interface ILinqTree<T>
{
IEnumerable<T> Children(); T Parent { get; }
} public static class TreeExtensions
{
/// <summary>
/// Returns a collection of descendant elements.
/// </summary>
public static IEnumerable<DependencyObject> Descendants(this DependencyObject item)
{
ILinqTree<DependencyObject> adapter = new VisualTreeAdapter(item);
foreach (var child in adapter.Children())
{
yield return child; foreach (var grandChild in child.Descendants())
{
yield return grandChild;
}
}
} /// <summary>
/// Returns a collection containing this element and all descendant elements.
/// </summary>
public static IEnumerable<DependencyObject> DescendantsAndSelf(this DependencyObject item)
{
yield return item; foreach (var child in item.Descendants())
{
yield return child;
}
} /// <summary>
/// Returns a collection of ancestor elements.
/// </summary>
public static IEnumerable<DependencyObject> Ancestors(this DependencyObject item)
{
ILinqTree<DependencyObject> adapter = new VisualTreeAdapter(item); var parent = adapter.Parent;
while (parent != null)
{
yield return parent;
adapter = new VisualTreeAdapter(parent);
parent = adapter.Parent;
}
} /// <summary>
/// Returns a collection containing this element and all ancestor elements.
/// </summary>
public static IEnumerable<DependencyObject> AncestorsAndSelf(this DependencyObject item)
{
yield return item; foreach (var ancestor in item.Ancestors())
{
yield return ancestor;
}
} /// <summary>
/// Returns a collection of child elements.
/// </summary>
public static IEnumerable<DependencyObject> Elements(this DependencyObject item)
{
ILinqTree<DependencyObject> adapter = new VisualTreeAdapter(item);
foreach (var child in adapter.Children())
{
yield return child;
}
} /// <summary>
/// Returns a collection of the sibling elements before this node, in document order.
/// </summary>
public static IEnumerable<DependencyObject> ElementsBeforeSelf(this DependencyObject item)
{
if (item.Ancestors().FirstOrDefault() == null)
yield break;
foreach (var child in item.Ancestors().First().Elements())
{
if (child.Equals(item))
break;
yield return child;
}
} /// <summary>
/// Returns a collection of the after elements after this node, in document order.
/// </summary>
public static IEnumerable<DependencyObject> ElementsAfterSelf(this DependencyObject item)
{
if (item.Ancestors().FirstOrDefault() == null)
yield break;
bool afterSelf = false;
foreach (var child in item.Ancestors().First().Elements())
{
if (afterSelf)
yield return child; if (child.Equals(item))
afterSelf = true;
}
} /// <summary>
/// Returns a collection containing this element and all child elements.
/// </summary>
public static IEnumerable<DependencyObject> ElementsAndSelf(this DependencyObject item)
{
yield return item; foreach (var child in item.Elements())
{
yield return child;
}
} /// <summary>
/// Returns a collection of descendant elements which match the given type.
/// </summary>
public static IEnumerable<DependencyObject> Descendants<T>(this DependencyObject item)
{
return item.Descendants().Where(i => i is T).Cast<DependencyObject>();
} /// <summary>
/// Returns a collection of the sibling elements before this node, in document order
/// which match the given type.
/// </summary>
public static IEnumerable<DependencyObject> ElementsBeforeSelf<T>(this DependencyObject item)
{
return item.ElementsBeforeSelf().Where(i => i is T).Cast<DependencyObject>();
} /// <summary>
/// Returns a collection of the after elements after this node, in document order
/// which match the given type.
/// </summary>
public static IEnumerable<DependencyObject> ElementsAfterSelf<T>(this DependencyObject item)
{
return item.ElementsAfterSelf().Where(i => i is T).Cast<DependencyObject>();
} /// <summary>
/// Returns a collection containing this element and all descendant elements
/// which match the given type.
/// </summary>
public static IEnumerable<DependencyObject> DescendantsAndSelf<T>(this DependencyObject item)
{
return item.DescendantsAndSelf().Where(i => i is T).Cast<DependencyObject>();
} /// <summary>
/// Returns a collection of ancestor elements which match the given type.
/// </summary>
public static IEnumerable<DependencyObject> Ancestors<T>(this DependencyObject item)
{
return item.Ancestors().Where(i => i is T).Cast<DependencyObject>();
} /// <summary>
/// Returns a collection containing this element and all ancestor elements
/// which match the given type.
/// </summary>
public static IEnumerable<DependencyObject> AncestorsAndSelf<T>(this DependencyObject item)
{
return item.AncestorsAndSelf().Where(i => i is T).Cast<DependencyObject>();
} /// <summary>
/// Returns a collection of child elements which match the given type.
/// </summary>
public static IEnumerable<DependencyObject> Elements<T>(this DependencyObject item)
{
return item.Elements().Where(i => i is T).Cast<DependencyObject>();
} /// <summary>
/// Returns a collection containing this element and all child elements.
/// which match the given type.
/// </summary>
public static IEnumerable<DependencyObject> ElementsAndSelf<T>(this DependencyObject item)
{
return item.ElementsAndSelf().Where(i => i is T).Cast<DependencyObject>();
} } public static class EnumerableTreeExtensions
{
/// <summary>
/// Applies the given function to each of the items in the supplied
/// IEnumerable.
/// </summary>
private static IEnumerable<DependencyObject> DrillDown(this IEnumerable<DependencyObject> items,
Func<DependencyObject, IEnumerable<DependencyObject>> function)
{
foreach (var item in items)
{
foreach (var itemChild in function(item))
{
yield return itemChild;
}
}
} /// <summary>
/// Applies the given function to each of the items in the supplied
/// IEnumerable, which match the given type.
/// </summary>
public static IEnumerable<DependencyObject> DrillDown<T>(this IEnumerable<DependencyObject> items,
Func<DependencyObject, IEnumerable<DependencyObject>> function)
where T : DependencyObject
{
foreach (var item in items)
{
foreach (var itemChild in function(item))
{
if (itemChild is T)
{
yield return (T)itemChild;
}
}
}
} /// <summary>
/// Returns a collection of descendant elements.
/// </summary>
public static IEnumerable<DependencyObject> Descendants(this IEnumerable<DependencyObject> items)
{
return items.DrillDown(i => i.Descendants());
} /// <summary>
/// Returns a collection containing this element and all descendant elements.
/// </summary>
public static IEnumerable<DependencyObject> DescendantsAndSelf(this IEnumerable<DependencyObject> items)
{
return items.DrillDown(i => i.DescendantsAndSelf());
} /// <summary>
/// Returns a collection of ancestor elements.
/// </summary>
public static IEnumerable<DependencyObject> Ancestors(this IEnumerable<DependencyObject> items)
{
return items.DrillDown(i => i.Ancestors());
} /// <summary>
/// Returns a collection containing this element and all ancestor elements.
/// </summary>
public static IEnumerable<DependencyObject> AncestorsAndSelf(this IEnumerable<DependencyObject> items)
{
return items.DrillDown(i => i.AncestorsAndSelf());
} /// <summary>
/// Returns a collection of child elements.
/// </summary>
public static IEnumerable<DependencyObject> Elements(this IEnumerable<DependencyObject> items)
{
return items.DrillDown(i => i.Elements());
} /// <summary>
/// Returns a collection containing this element and all child elements.
/// </summary>
public static IEnumerable<DependencyObject> ElementsAndSelf(this IEnumerable<DependencyObject> items)
{
return items.DrillDown(i => i.ElementsAndSelf());
} /// <summary>
/// Returns a collection of descendant elements which match the given type.
/// </summary>
public static IEnumerable<DependencyObject> Descendants<T>(this IEnumerable<DependencyObject> items)
where T : DependencyObject
{
return items.DrillDown<T>(i => i.Descendants());
} /// <summary>
/// Returns a collection containing this element and all descendant elements.
/// which match the given type.
/// </summary>
public static IEnumerable<DependencyObject> DescendantsAndSelf<T>(this IEnumerable<DependencyObject> items)
where T : DependencyObject
{
return items.DrillDown<T>(i => i.DescendantsAndSelf());
} /// <summary>
/// Returns a collection of ancestor elements which match the given type.
/// </summary>
public static IEnumerable<DependencyObject> Ancestors<T>(this IEnumerable<DependencyObject> items)
where T : DependencyObject
{
return items.DrillDown<T>(i => i.Ancestors());
} /// <summary>
/// Returns a collection containing this element and all ancestor elements.
/// which match the given type.
/// </summary>
public static IEnumerable<DependencyObject> AncestorsAndSelf<T>(this IEnumerable<DependencyObject> items)
where T : DependencyObject
{
return items.DrillDown<T>(i => i.AncestorsAndSelf());
} /// <summary>
/// Returns a collection of child elements which match the given type.
/// </summary>
public static IEnumerable<DependencyObject> Elements<T>(this IEnumerable<DependencyObject> items)
where T : DependencyObject
{
return items.DrillDown<T>(i => i.Elements());
} /// <summary>
/// Returns a collection containing this element and all child elements.
/// which match the given type.
/// </summary>
public static IEnumerable<DependencyObject> ElementsAndSelf<T>(this IEnumerable<DependencyObject> items)
where T : DependencyObject
{
return items.DrillDown<T>(i => i.ElementsAndSelf());
}
}
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的由于基础不好现在还没看得明白,代码全部都铺上来,效果如下图
public class PeopleHubTileData : INotifyPropertyChanged
{
private ImageSource _ImageFront;
public ImageSource ImageFront
{
get
{
return _ImageFront;
}
set
{
if (value != _ImageFront)
{
_ImageFront = value;
NotifyPropertyChanged("ImageFront");
}
}
} private ImageSource _ImageBack;
public ImageSource ImageBack
{
get
{
return _ImageBack;
}
set
{
if (value != _ImageBack)
{
_ImageBack = value;
NotifyPropertyChanged("ImageBack");
}
}
} private Stretch _ImageStretch;
public Stretch ImageStretch
{
get
{
return _ImageStretch;
}
set
{
if (value != _ImageStretch)
{
_ImageStretch = value;
NotifyPropertyChanged("ImageStretch");
}
}
} public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
PepoleHubTileData
public class Tiles : DependencyObject
{
public Tiles()
{
this.CenterOfRotationY = 0.5;
}
public Tiles(object item)
: this()
{
this.TileData = item;
}
public object TileData { get; set; } public double CenterOfRotationY { get; set; }
public double ZIndex
{
get { return (int)GetValue(ZIndexProperty); }
set { SetValue(ZIndexProperty, value); }
}
public static DependencyProperty ZIndexProperty = DependencyProperty.Register("ZIndex", typeof(int), typeof(Tiles), new PropertyMetadata());
public double RotationX
{
get { return (double)GetValue(RotationXProperty); }
set { SetValue(RotationXProperty, value); }
}
public static DependencyProperty RotationXProperty = DependencyProperty.Register("RotationX", typeof(double), typeof(Tiles), new PropertyMetadata(0.0)); }
Tiles
public class PeopleHubTile : ContentControl
{
#region Member variables
private int LastAnimatedTile = ;
/// <summary>
/// 大磁贴起始位置选择完毕,可以开始制造大磁贴
/// </summary>
private bool isBigTileAnimationStarted = false;
/// <summary>
/// 表明给大磁贴选择了图片
/// </summary>
private bool isBitImageSelected = false;
/// <summary>
/// 大磁贴图片的索引
/// </summary>
private int BitImageSelectedIndex = ;
/// <summary>
/// 累计翻动大磁贴时已经翻动了小磁贴的数目
/// </summary>
private int TileAnimateIndex = ;
private int TileAnimationCount = ;
/// <summary>
/// 所有磁贴进入就绪状态,可以开始选取大磁贴的起始位置
/// </summary>
private bool isReadyForBigTile = false;
private Random RandomTile = new Random();
private DispatcherTimer dispatcherTimer = new DispatcherTimer();
private List<String> ImageUrl = new List<string>()
{
"/Themes/Images/1.jpg",
"/Themes/Images/13.jpg",
"/Themes/Images/14.jpg",
"/Themes/Images/15.jpg",
"/Themes/Images/16.jpg",
"/Themes/Images/17.jpg",
"/Themes/Images/18.jpg",
"/Themes/Images/19.jpg",
"/Themes/Images/2.jpg",
"/Themes/Images/20.jpg",
"/Themes/Images/21.jpg",
"/Themes/Images/3.jpg", }; private ObservableCollection<Tiles> dataItems = new ObservableCollection<Tiles>()
{
new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/1.jpg", UriKind.RelativeOrAbsolute)) }),
new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/2.jpg", UriKind.RelativeOrAbsolute)) }),
new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/13.jpg", UriKind.RelativeOrAbsolute)) }),
new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/14.jpg", UriKind.RelativeOrAbsolute)) }),
new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/15.jpg", UriKind.RelativeOrAbsolute)) }),
new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/16.jpg", UriKind.RelativeOrAbsolute)) }),
new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/17.jpg", UriKind.RelativeOrAbsolute)) }),
new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/18.jpg", UriKind.RelativeOrAbsolute)) }),
new Tiles(new PeopleHubTileData(){ ImageFront = new BitmapImage(new Uri("/Themes/Images/19.jpg", UriKind.RelativeOrAbsolute)) }),
};
#endregion #region Constructor
public PeopleHubTile()
{
DefaultStyleKey = typeof(PeopleHubTile);
Loaded += PeopleHubTile_Loaded; }
#endregion #region Methods
ListBox ItemsListBox;
void PeopleHubTile_Loaded(object sender, RoutedEventArgs e)
{
///在generic中获取ListBox 附上各个Tilt在ListBox中
ItemsListBox = this.GetTemplateChild("ItemsListBox") as ListBox;
this.ItemsListBox.ItemsSource = dataItems;
//开启定时更换tile的任务
dispatcherTimer.Interval = TimeSpan.FromSeconds();
dispatcherTimer.Tick += dispatcherTimer_Tick;
dispatcherTimer.Start();
} void dispatcherTimer_Tick(object sender, EventArgs e)
{
//计数,如果是9个则尝试启动大磁贴
TileAnimationCount++;
if (TileAnimationCount > && isReadyForBigTile == false)
{
TileAnimationCount = ;
isReadyForBigTile = true; } int AnimateItem = ;
Tiles AnimateTile = null;
//未启动大磁贴的操作
if (!isBigTileAnimationStarted)
{
AnimateItem = RandomTile.Next(this.dataItems.Count);
AnimateTile = this.dataItems[AnimateItem]; ///尝试启动大磁贴 并且当前是抽到变换的磁贴是允许作为大磁贴的第一个磁贴
///它变换大磁贴时是从大磁贴的左上 右上 左下 右下来变换
if (isReadyForBigTile && (AnimateItem == || AnimateItem == || AnimateItem == || AnimateItem == ))
{
LastAnimatedTile = AnimateItem;
isBigTileAnimationStarted = true;
TileAnimateIndex = ; } ///用ZIndex来区分正面和反面
/// Animate small tiles
if (AnimateTile.ZIndex == )
{
//back tile
PeopleHubTileData ItemData = AnimateTile.TileData as PeopleHubTileData; int newImage = RandomTile.Next(ImageUrl.Count);
if (RandomTile.Next() > )
ItemData.ImageBack = new BitmapImage(new Uri(ImageUrl[newImage], UriKind.RelativeOrAbsolute));
else
ItemData.ImageBack = new BitmapImage(new Uri("", UriKind.RelativeOrAbsolute));
}
else if (AnimateTile.ZIndex != )
{
//front tile
PeopleHubTileData ItemData = AnimateTile.TileData as PeopleHubTileData; int newImage = RandomTile.Next(ImageUrl.Count);
if (RandomTile.Next() > )
ItemData.ImageFront = new BitmapImage(new Uri(ImageUrl[newImage], UriKind.RelativeOrAbsolute));
else
ItemData.ImageFront = new BitmapImage(new Uri("", UriKind.RelativeOrAbsolute)); } } ///已经启用大磁贴
else if (isBigTileAnimationStarted && TileAnimateIndex < )
{ int[] LastTiles = new int[];
//按照大磁贴其实位置来选出大磁贴所占用的小磁贴的索引
switch (LastAnimatedTile)
{
case :
LastTiles = new int[] { , , , };
break;
case :
LastTiles = new int[] { , , , };
break;
case :
LastTiles = new int[] { , , , };
break;
case :
LastTiles = new int[] { , , , };
break;
default:
break; } AnimateTile = this.dataItems[LastTiles[TileAnimateIndex]];
///还没有生成大磁贴所用的图片时
if (!isBitImageSelected)
{
isBitImageSelected = true;
BitImageSelectedIndex = RandomTile.Next(ImageUrl.Count);
}
///bmpWB是直接从资源列表中拿的图片
BitmapImage bmpWB = new BitmapImage(new Uri(ImageUrl[BitImageSelectedIndex], UriKind.RelativeOrAbsolute));
///最终写到磁贴上的部分图片
WriteableBitmap ImageWB = new WriteableBitmap(bmpWB.PixelWidth, bmpWB.PixelHeight);
bmpWB.CreateOptions = BitmapCreateOptions.None;
///整幅大磁贴的图片
WriteableBitmap imageBitMap = new WriteableBitmap(bmpWB); switch (TileAnimateIndex)
{
case : ImageWB = GetCropImage(imageBitMap, , , imageBitMap.PixelWidth / , imageBitMap.PixelHeight / ); break;
case : ImageWB = GetCropImage(imageBitMap, imageBitMap.PixelWidth / , , imageBitMap.PixelWidth / , imageBitMap.PixelHeight / );
break;
case : ImageWB = GetCropImage(imageBitMap, , imageBitMap.PixelHeight / , imageBitMap.PixelWidth / , imageBitMap.PixelHeight / ); break;
case : ImageWB = GetCropImage(imageBitMap, imageBitMap.PixelWidth / , imageBitMap.PixelHeight / , imageBitMap.PixelWidth / , imageBitMap.PixelHeight / );
break;
default:
break; }
///通过累计数目来判断大磁贴是否完成
TileAnimateIndex++;
if (TileAnimateIndex > )
{
isBigTileAnimationStarted = false;
isReadyForBigTile = false;
isBitImageSelected = false;
} //animate part of big tiles
if (AnimateTile.ZIndex == )
{
//back tile
PeopleHubTileData ItemData = AnimateTile.TileData as PeopleHubTileData; ItemData.ImageBack = ImageWB; }
else if (AnimateTile.ZIndex != )
{
//front tile
PeopleHubTileData ItemData = AnimateTile.TileData as PeopleHubTileData; ItemData.ImageFront = ImageWB;
} }
//tile animation
Storyboard MyStory = new Storyboard();
DoubleAnimation MyDouble = new DoubleAnimation();
MyDouble.From = AnimateTile.RotationX;
MyDouble.To = AnimateTile.RotationX + ;
MyDouble.Duration = TimeSpan.FromSeconds(0.5);
Storyboard.SetTarget(MyDouble, AnimateTile);
Storyboard.SetTargetProperty(MyDouble, new PropertyPath(Tiles.RotationXProperty));
MyStory.Children.Add(MyDouble); ObjectAnimationUsingKeyFrames MyObject = new ObjectAnimationUsingKeyFrames();
DiscreteObjectKeyFrame MyKeyFrame = new DiscreteObjectKeyFrame();
MyKeyFrame.KeyTime = TimeSpan.FromSeconds();
MyKeyFrame.Value = AnimateTile.ZIndex;
MyObject.KeyFrames.Add(MyKeyFrame); MyKeyFrame = new DiscreteObjectKeyFrame();
MyKeyFrame.KeyTime = TimeSpan.FromSeconds(0.3);
MyKeyFrame.Value = (AnimateTile.ZIndex == ) ? : ;
MyObject.KeyFrames.Add(MyKeyFrame);
Storyboard.SetTarget(MyObject, AnimateTile);
Storyboard.SetTargetProperty(MyObject, new PropertyPath(Tiles.ZIndexProperty));
MyStory.Children.Add(MyObject);
MyStory.Begin(); } /// <summary>
/// 利用数组copy,通过计算位图的位置来切割出部分的图片
/// </summary>
/// <param name="aBitmapSource"></param>
/// <param name="XPoint"></param>
/// <param name="YPoint"></param>
/// <param name="aWidth"></param>
/// <param name="aHeight"></param>
/// <returns></returns>
private static WriteableBitmap GetCropImage(WriteableBitmap aBitmapSource, int XPoint, int YPoint, int aWidth, int aHeight)
{
var SourceWidth = aBitmapSource.PixelWidth;
var result = new WriteableBitmap(aWidth, aHeight);
for (var x = ; x < aHeight - ; x++)
{
var Index = XPoint + (YPoint + x) * SourceWidth;
var FinalIndex = x * aWidth;
Array.Copy(aBitmapSource.Pixels, Index, result.Pixels, FinalIndex, aWidth); }
return result;
}
#endregion #region OnApplyTemplate
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
}
#endregion
}
PepoleHubTile
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
xmlns:local="clr-namespace:PeopleHubTileEx"> <DataTemplate x:Key="DataTemplatePeopleHubTile">
<Grid x:Name="TileGrid" Height="80" Width="80" >
<Grid.Projection>
<PlaneProjection RotationX="{Binding RotationX}" CenterOfRotationY="{Binding CenterOfRotationX}">
</PlaneProjection>
</Grid.Projection>
<Grid x:Name="BackGrid" Canvas.ZIndex="{Binding ZIndex}" RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<CompositeTransform ScaleY="-1"/>
</Grid.RenderTransform>
<Grid.Background>
<SolidColorBrush Color="Green"></SolidColorBrush>
</Grid.Background>
<Image Source="{Binding TileData.ImageBack}" Stretch="Fill" />
</Grid>
<Grid x:Name="FrontGrid">
<Grid.Background>
<SolidColorBrush Color="Green"></SolidColorBrush>
</Grid.Background>
<Image Source="{Binding TileData.ImageFront}" Stretch="Fill" > </Image>
</Grid>
</Grid>
</DataTemplate> <Style TargetType="local:PeopleHubTile"> <Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:PeopleHubTile">
<Grid>
<ListBox Name="ItemsListBox" Width="240" Height="240"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ItemTemplate="{StaticResource DataTemplatePeopleHubTile}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel ItemHeight="80" ItemWidth="80" Orientation="Horizontal"> </toolkit:WrapPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox> </Grid> </ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
generic.xaml
使用的时候只需要以下面的形式则可。
<PeopleHubTileControl:PeopleHubTile VerticalAlignment="Center"> </PeopleHubTileControl:PeopleHubTile>
WinPhone学习笔记(四)——磁贴的更多相关文章
- C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻
前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ...
- IOS学习笔记(四)之UITextField和UITextView控件学习
IOS学习笔记(四)之UITextField和UITextView控件学习(博客地址:http://blog.csdn.net/developer_jiangqq) Author:hmjiangqq ...
- java之jvm学习笔记四(安全管理器)
java之jvm学习笔记四(安全管理器) 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器 ...
- Learning ROS for Robotics Programming Second Edition学习笔记(四) indigo devices
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...
- Typescript 学习笔记四:回忆ES5 中的类
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- ES6学习笔记<四> default、rest、Multi-line Strings
default 参数默认值 在实际开发 有时需要给一些参数默认值. 在ES6之前一般都这么处理参数默认值 function add(val_1,val_2){ val_1 = val_1 || 10; ...
- muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制
目录 muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制 eventfd的使用 eventfd系统函数 使用示例 EventLoop对eventfd的封装 工作时序 runInLoo ...
- python3.4学习笔记(四) 3.x和2.x的区别,持续更新
python3.4学习笔记(四) 3.x和2.x的区别 在2.x中:print html,3.x中必须改成:print(html) import urllib2ImportError: No modu ...
- Go语言学习笔记四: 运算符
Go语言学习笔记四: 运算符 这章知识好无聊呀,本来想跨过去,但没准有初学者要学,还是写写吧. 运算符种类 与你预期的一样,Go的特点就是啥都有,爱用哪个用哪个,所以市面上的运算符基本都有. 算术运算 ...
- 零拷贝详解 Java NIO学习笔记四(零拷贝详解)
转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...
随机推荐
- 使用mybatis访问sql server
原创文章转载请注明出处:@协思, http://zeeman.cnblogs.com mybatis作为一种半自动化的ORM工具,可以提供更大的灵活性,逐渐受到社区的欢迎. 官方下载地址是:https ...
- [安卓] 15、用NFC解锁手机并自动打开应用
最近接到一个项目:将手机放到一个带有NFC卡的底座上手机会自动解锁,然后打开相应的应用 本人用:杭州公交通用卡做为NFC卡+Coolpad手机进行试验 效果如下: 1.手机本身带有图案锁,输对图案才能 ...
- Java基础类型总结
最近一直在总结反思自己, 趁着现在请假在学校上课的空余时间,从基础开始重新温故学习下Java,充实下自己. 一.数据类型 从下图中,我们可以很清晰的看出Java中的类型,其中红色方框中的是Java的4 ...
- 如何选择前端框架:ANGULAR VS EMBER VS REACT
最近一段时间是令前端工程师们非常兴奋的时期,因为三大Web框架陆续发布新版本,让我们见识到了更强大的Web框架.Ember2.0在2个月之前已经发布,从1.0升级到2.0非常简单.几周之前React发 ...
- Windows Live Writer 初次使用
Windows Live Writer 博客园文章的一款发布器,这篇文章就是通过其发布的,可以先在word中写好博客内容,直接粘贴到这里发布就OK,之前我都是先在Word中写好一份,然后在blogs ...
- 网络异步编程(C#)团购课
新生命开发团队大石头讲解网络异步编程(C#) 内容:网络编程基础.IOCP.APM.SAEA 时长:2~3小时 价格:20元,20人及以上成团,http://item.taobao.com/item. ...
- 合法提交Html标签 Page指令
3.2.1 提交合法的HTML标签(1) 有时候我们需要让我们提交的文本展示出来的效果非常美观,通常会对服务器提交一些HTML标签来控制文本或内容的样式. HTML标签可能包含了很多不安全的因素,所以 ...
- atitit 英文与中文与阿拉伯文的简化解决方案.docx
atitit 英文与中文与阿拉伯文的简化解决方案.docx 1. 英文与中文与阿拉伯文的简化意义1 2. 简化的主要方法分类2 2.1. 同音替代法(比如o2o)2 2.2. 符号法(比如英文i18n ...
- Atitit 微信支付 支付结果通用通知
Atitit 微信支付 支付结果通用通知 Wechat hto sh ma 返回页面return_url - 熊佳佳的博客 d ,only notyfi url-... 接口链接 该链接是通过[统一 ...
- 常用 sql
一. sql 多个字段拼接 mysql: 1. concat函数 如果连接串中存在NULL,则返回结果为NULL: 例如: select concat('1','2','3') ...