iT邦幫忙

DAY 5
2

Windows Phone 程式開發系列 第 5

[WP 開發] 好的 App 特點:資料載入時有載入中畫面和 Tilt Effect

在微軟員工撰寫的文章 [好的 Windows Phone App 通常具備的特點] 文中提到
http://blogs.msdn.com/b/hermanwu/archive/2012/11/24/windows-phone-app.aspx
1. 資料載入時要有載入中畫面

2. Tilt Effect

本文針對這兩個好的特點說明如何實作。
本文刊載於
http://www.dotblogs.com.tw/chou/archive/2013/10/07/123394.aspx
http://www.dotblogs.com.tw/chou/archive/2013/09/20/119096.aspx

在這篇文章您可以學到

  1. 資料載入時要有載入中畫面
  2. Tilt Effect

資料載入時要有載入中畫面

想要達成這樣的功能,可以操作 SystemTray 搭配 ProgressIndicator 達成。
http://msdn.microsoft.com/en-us/library/windowsphone/develop/ff626537%28v=vs.105%29.aspx

新增專案,在 MainPage.xaml 中,加入 CheckBox 控制項。

切換至程式碼,我們搭配 ProgressIndicator 提供用於與應用程式的 SystemTray 做進度指示的方法和屬性。
http://msdn.microsoft.com/en-US/library/windowsphone/develop/microsoft.phone.shell.progressindicator%28v=vs.105%29.aspx

先引用 Microsoft.Phone.Shell

using Microsoft.Phone.Shell;

建立 ProgressIndicator 並設定給 SystemTray 程式碼

            // 建立 ProgressIndicator 並設定給 SystemTray
            ProgressIndicator pi = new ProgressIndicator();
            pi.Text = "Loading...";
            pi.IsIndeterminate = true;
            pi.IsVisible = true;
            SystemTray.SetProgressIndicator(this, pi);

以下是程式完整程式碼,其中 CheckBox 包含兩個事件 Checked 和 Unchecked,當 Checked 觸發時,顯示 System Tray,當 Unchecked 事件觸發時,隱藏 System Tray。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;

namespace SystemTrayApp
{
    public partial class MainPage : PhoneApplicationPage
    {
        // 建構函式
        public MainPage()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainPage_Loaded);
        }

        private void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            // 建立 ProgressIndicator 並設定給 SystemTray
            ProgressIndicator pi = new ProgressIndicator();
            pi.Text = "Loading...";
            pi.IsIndeterminate = true;
            pi.IsVisible = true;
            SystemTray.SetProgressIndicator(this, pi);
            
            // Checkbox 屬性與事件
            this.cbShowSystemTray.IsChecked = true;
            this.cbShowSystemTray.Checked += cbShowSystemTray_Checked;
            this.cbShowSystemTray.Unchecked += cbShowSystemTray_Unchecked;
        }

        void cbShowSystemTray_Checked(object sender, RoutedEventArgs e)
        {
            // SystemTray 顯示
            SystemTray.IsVisible = true;
        }

        void cbShowSystemTray_Unchecked(object sender, RoutedEventArgs e)
        {
            // SystemTray 隱藏
            SystemTray.IsVisible = false;
        }
    }
}

執行程式,一開始就會顯示 Loading...

取消勾選 CheckBox 後 Loading ... 消失

Tilt Effect

應用程式中需要讓所有可按的連結實作 Tilt Effect,Tilt Effect 主要是讓控制項按下時,會有下凹傾斜的效果,讓使用者使用上更加明顯。
接著進行實作,新增專案,新增類別 TiltEffect.cs,程式碼如下,檔案來源來自微軟網站 Tilt Effect Sample。
http://code.msdn.microsoft.com/wpapps/Tilt-Effect-Sample-fab3b035

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 = 25;

        /// <summary>
        /// Delay between releasing an element and the tilt release animation playing
        /// </summary>
        static readonly TimeSpan TiltReturnAnimationDelay = TimeSpan.FromMilliseconds(200);

        /// <summary>
        /// Duration of tilt release animation
        /// </summary>
        static readonly TimeSpan TiltReturnAnimationDuration = TimeSpan.FromMilliseconds(100);

        /// <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, 0) 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 / 2, element.ActualHeight / 2);

                            // 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 / 2, element.ActualHeight / 2);
            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 / 2, container.ActualWidth / 2);
                }
                else
                    containerCenter = new Point(container.ActualWidth / 2, container.ActualHeight / 2);
            }
            else
                containerCenter = new Point(container.ActualWidth / 2, container.ActualHeight / 2);
#else

            containerCenter = new Point(container.ActualWidth / 2, container.ActualHeight / 2);

#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 = -1 * centerDelta.X;
            projection.GlobalOffsetY = -1 * 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 = 0;
                tiltReturnXAnimation.Duration = TiltReturnAnimationDuration;

                tiltReturnYAnimation = new DoubleAnimation();
                Storyboard.SetTargetProperty(tiltReturnYAnimation, new PropertyPath(PlaneProjection.RotationYProperty));
                tiltReturnYAnimation.BeginTime = TiltReturnAnimationDelay;
                tiltReturnYAnimation.To = 0;
                tiltReturnYAnimation.Duration = TiltReturnAnimationDuration;

                tiltReturnZAnimation = new DoubleAnimation();
                Storyboard.SetTargetProperty(tiltReturnZAnimation, new PropertyPath(PlaneProjection.GlobalOffsetZProperty));
                tiltReturnZAnimation.BeginTime = TiltReturnAnimationDelay;
                tiltReturnZAnimation.To = 0;
                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(0, 0, 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 = 0;
            projection.RotationX = 0;
            projection.GlobalOffsetZ = 0;
        }

        /// <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 * 2), 0), 1),
                Math.Min(Math.Max(touchPoint.Y / (centerPoint.Y * 2), 0), 1));

            // 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 > 0 ? xMagnitude / (xMagnitude + yMagnitude) : 0;

            double angle = angleMagnitude * MaxAngle * 180 / Math.PI;
            double depression = (1 - 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 * (1 - 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 + 1) / 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;
        }
    }
}

切換至 MainPage.xaml 設計模式,引用並啟用 IsTiltEnabled 效果。

如果有特定控制項不想要有 Tilt Effect,可加上屬性 local:TiltEffect.SuppressTilt="True"

<Button Content="Button (無效果)" Height="150" HorizontalAlignment="Left" Margin="30,0,0,62" VerticalAlignment="Bottom" Width="380" local:TiltEffect.SuppressTilt="True"/>

執行專案,按下控制項測試 Tilt Effect。


上一篇
[WP 開發] 使用 Html Agility Pack 取得統一發票資訊
下一篇
[WP 開發] 使用 ZXing.Net 產生 QR Code
系列文
Windows Phone 程式開發27

1 則留言

我要留言

立即登入留言