WPF 讓子元素動起來!

在沒有接觸Blend以前,本身整出了一個MultiTouchHelper,這東西是作什麼的呢?就是利用附加屬性讓元素能夠多點觸控。express

而後某一天發現Blend裏面有一個Behavior的東西,我去,原來有現成的一個叫TranslateZoomRoateBehavior!canvas

第一反應,浪費了本碼農兩天時間!dom

第二反應,原來本碼農的思想已經達到了這種境界(能夠寫出和大神相似的東西了),相信要不了多久,本碼農就能夠升職加薪,當上總經理,出任CEO,迎娶白富美,走上人生巔峯,想一想還有點小激動呢,嘿嘿~~ 動畫

第三反應,TranslateZoomRoateBehavior這玩意兒的名字老長了,並且得添加2個dll,這樣的使用方法:ui

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"spa

<xxxUIElement>
<i:Interaction.Behaviors>
    <ei:TranslateZoomRotateBehavior/>
</i:Interaction.Behaviors>
</xxxUIElement>

再來看本碼農的短(chang)小(cu)~精悍的MultiTouchHelper,使用方法:code

<Grid mt:MultiTouchHelper.IsContenter="True">
  <xxxUIElement mt:MultiTouchHelper.MaxScale="4"            
mt:MultiTouchHelper.MinScale="0.5"
mt:MultiTouchHelper.ManipulationMode="All" mt:MultiTouchHelper.WaitingForRecover="500" /> </Grid>
mt:MultiTouchHelper.IsContenter:設定觸摸的容器
mt:MultiTouchHelper.MaxScale:放大的最大倍數         
mt:MultiTouchHelper.MinScale:縮小的最小倍數
mt:MultiTouchHelper.ManipulationMode:觸摸方式 mt:MultiTouchHelper.WaitingForRecover: 恢復初始狀態的等待時間

綜合使用下來,細節方面TZRB不如MTH,例如MTH支持被觸摸的元素置於最頂層,可是效率方面MTH彷佛不如TZRB?

MultiTouchHelper會在之後再寫一篇文章,下面進入我今天想說的話題(是否是前奏有點長?是否是像某種藝術片讓人忍不住跳過~~)


需求:讓ListBox中元素依次從左到右移動。


項目進行:

一、建立自定義控件,放個ListBox,遍歷ListBox的子元素,爲其添加動畫,大功告成。
哎呀呀~項目進行的還真是順利,果真動起來了,任務完成,相信要不了多久我就能夠走向人生的巔峯了,想一想還真是有點小激動呢~~

客戶:你這個框框太難看了,改改。

好吧,改改就改改。找到自定義控件=》ListBox=》ItemTemplate,嚯嚯嚯嚯!改好了,相信要不了多久就能夠迎娶白富美,想一想還真是有點小激動呢~~

客戶:你這東西作的不錯,xxx頁面也來一個。

好嘞,複製粘貼嘛,哪一個不會嘛!哦呵呵,還真是有點小激動呢~~

哎喲,數據實體不同,子元素的樣式不同哎。再來一個自定義控件?嗯,是個好辦法!想一想還真是有點小激動呢~~

等等……這樣下去也不是辦法啊,這也來一個那也來一個,啥時候升職加薪???

哎,想一想還真是有點小憂桑...

至此,CanvasItemBehavior橫空出世,拯救蒼生,造福人類……咳咳,請看:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;

namespace Near.Helper.Behaviors
{
    public class CanvasItemBehavior
    {

        #region MoveMode 子級移動方式
        public static MoveOrientaion GetMoveMode(Canvas obj)
        {
            return (MoveOrientaion)obj.GetValue(MoveModeProperty);
        }

        public static void SetMoveMode(Canvas obj, MoveOrientaion value)
        {
            obj.SetValue(MoveModeProperty, value);
        }

        // Using a DependencyProperty as the backing store for MoveMode.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MoveModeProperty =
            DependencyProperty.RegisterAttached("MoveMode", typeof(MoveOrientaion), typeof(CanvasItemBehavior), new PropertyMetadata(MoveOrientaion.None, new PropertyChangedCallback(OnMoveModeChanged)));

        #endregion

        #region 存儲動畫
        private static Storyboard GetStoryboard(DependencyObject obj)
        {
            return (Storyboard)obj.GetValue(StoryboardProperty);
        }

        private static void SetStoryboard(DependencyObject obj, Storyboard value)
        {
            obj.SetValue(StoryboardProperty, value);
        }

        // Using a DependencyProperty as the backing store for Storyboard.  This enables animation, styling, binding, etc...
        private static readonly DependencyProperty StoryboardProperty =
            DependencyProperty.RegisterAttached("Storyboard", typeof(Storyboard), typeof(CanvasItemBehavior), new PropertyMetadata(null));
        #endregion

        #region Duration 動畫持續時間
        public static Duration GetDuration(DependencyObject obj)
        {
            return (Duration)obj.GetValue(DurationProperty);
        }

        public static void SetDuration(DependencyObject obj, Duration value)
        {
            obj.SetValue(DurationProperty, value);
        }

        // Using a DependencyProperty as the backing store for Duration.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DurationProperty =
            DependencyProperty.RegisterAttached("Duration", typeof(Duration), typeof(CanvasItemBehavior), new PropertyMetadata(Duration.Automatic));
        #endregion

        #region 元素出來的時間間隔
        //public static TimeSpan GetInterval(DependencyObject obj)
        //{
        //    return (TimeSpan)obj.GetValue(IntervalProperty);
        //}

        //public static void SetInterval(DependencyObject obj, TimeSpan value)
        //{
        //    obj.SetValue(IntervalProperty, value);
        //}

        //// Using a DependencyProperty as the backing store for Interval.  This enables animation, styling, binding, etc...
        //public static readonly DependencyProperty IntervalProperty =
        //    DependencyProperty.RegisterAttached("Interval", typeof(TimeSpan), typeof(CanvasItemBehavior), new PropertyMetadata(TimeSpan.Zero));

        #endregion

        #region 泡泡模式時指定半徑
        public static double GetBubbleR(DependencyObject obj)
        {
            return (double)obj.GetValue(BubbleRProperty);
        }

        public static void SetBubbleR(DependencyObject obj, double value)
        {
            obj.SetValue(BubbleRProperty, value);
        }

        // Using a DependencyProperty as the backing store for BubbleR.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BubbleRProperty =
            DependencyProperty.RegisterAttached("BubbleR", typeof(double), typeof(CanvasItemBehavior), new PropertyMetadata(double.NaN));
        #endregion

        private static void OnMoveModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is Canvas)
            {
                var canvas = d as Canvas;
                if ((MoveOrientaion)e.NewValue != MoveOrientaion.None)
                {
                    canvas.Loaded += canvas_Loaded;
                }
                //else
                //{
                //    canvas.Loaded -= canvas_Loaded;
                //}
            }


        }

        static void canvas_Loaded(object sender, RoutedEventArgs e)
        {
            var canvas = sender as Canvas;
            var mode = GetMoveMode(canvas);

            if (mode > 0)
            {
var itemSource = VisualHelper.FindVisualParent<ListBox>(canvas).ItemsSource; if (itemSource is System.Collections.Specialized.INotifyCollectionChanged) { (itemSource as System.Collections.Specialized.INotifyCollectionChanged).CollectionChanged += (ss, ee) => { if (ee.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) { foreach (var item in ee.NewItems) { var lt = VisualHelper.FindVisualParent<ListBox>(canvas); var itemBox = lt.ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem; itemBox.Visibility = Visibility.Hidden; itemBox.PreviewMouseDown += item_MouseDown; itemBox.IsVisibleChanged += item_IsVisibleChanged; } } }; } foreach (UIElement item in canvas.Children) { item.Visibility = Visibility.Hidden; item.PreviewMouseDown += item_MouseDown; item.IsVisibleChanged += item_IsVisibleChanged; } if (mode == MoveOrientaion.RightToLeft) { canvas.FlowDirection = FlowDirection.RightToLeft; } var timer = new DispatcherTimer(); int index = 0; timer.Interval = TimeSpan.FromMilliseconds(50); timer.Tick += (ss, ee) => { if (canvas.Children.Count > 1) { var item = canvas.Children[index++]; if (!item.IsVisible) { var t = TimeSpan.Zero; if ((int)mode < 3) { var speed = GetDuration(canvas) != Duration.Automatic ? canvas.ActualWidth / GetDuration(canvas).TimeSpan.Seconds : 100; t = TimeSpan.FromSeconds(canvas.Children[0].RenderSize.Width / speed); } else { var speed = GetDuration(canvas) != Duration.Automatic ? canvas.ActualHeight / GetDuration(canvas).TimeSpan.Seconds : 100; t = TimeSpan.FromSeconds(canvas.Children[0].RenderSize.Height / speed); } if (timer.Interval != t) timer.Interval = t; if ((int)mode < 3) { Canvas.SetTop(item, GetRandom(canvas.ActualHeight - item.RenderSize.Height)); } else { Canvas.SetLeft(item, GetRandom(canvas.ActualWidth - item.RenderSize.Width)); } item.Visibility = Visibility.Visible; } if (index >= canvas.Children.Count) index = 0; //Console.WriteLine(item); } }; timer.Start(); } } static void item_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { var s = GetStoryboard(sender as UIElement); if (s != null) { if (s.GetIsPaused()) s.Resume(); else s.Pause(); } } static void item_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { if ((bool)e.NewValue) { var item = sender as UIElement; if (GetStoryboard(item) == null) { var canvas = VisualTreeHelper.GetParent(item) as Canvas; var s = new Storyboard(); var ani = new DoubleAnimation(); Storyboard.SetTarget(ani, item); var mode = GetMoveMode(canvas); if ((int)mode < 3) { ani.From = 0 - item.RenderSize.Width; ani.To = canvas.ActualWidth; ani.Duration = GetDuration(canvas) > TimeSpan.Zero ? GetDuration(canvas) : TimeSpan.FromSeconds(canvas.ActualWidth / 100); Storyboard.SetTargetProperty(ani, new PropertyPath(Canvas.LeftProperty)); } else if (mode == MoveOrientaion.TopToBottom) { ani.From = 0 - item.RenderSize.Height; ani.To = canvas.ActualHeight; ani.Duration = GetDuration(canvas) > TimeSpan.Zero ? GetDuration(canvas) : TimeSpan.FromSeconds(canvas.ActualHeight / 100); Storyboard.SetTargetProperty(ani, new PropertyPath(Canvas.TopProperty)); } else if (mode == MoveOrientaion.BottomToTop) { ani.From = canvas.ActualHeight; ani.To = 0 - item.RenderSize.Height; ani.Duration = GetDuration(canvas) > TimeSpan.Zero ? GetDuration(canvas) : TimeSpan.FromSeconds(canvas.ActualHeight / 100); Storyboard.SetTargetProperty(ani, new PropertyPath(Canvas.TopProperty)); } s.Children.Add(ani); s.Completed += (ss, ee) => { item.Visibility = Visibility.Hidden; SetStoryboard(item, null); }; SetStoryboard(item, s); s.Begin(); } } } static int GetRandom(int maxValue) { Random rd = new Random(Guid.NewGuid().GetHashCode()); return rd.Next(maxValue); } static int GetRandom(double maxValue) { return GetRandom((int)maxValue); } } public enum MoveOrientaion { None = -1, /// <summary> /// 從左到右 /// </summary> LeftToRight = 1, /// <summary> /// 從右到左 /// </summary> RightToLeft = 2, /// <summary> /// 從上往下 /// </summary> TopToBottom = 3, /// <summary> /// 從下往上 /// </summary> BottomToTop = 4, /// <summary> /// 泡泡模式 /// </summary> Bubble = 0 } }

用法:xml

  <ListBox Name="listBox">
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas bh:CanvasItemBehavior.MoveMode="LeftToRight" bh:CanvasItemBehavior.Duration="0:0:6" />
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Image Source="{Binding}" Width="400" Height="300"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
  </ListBox>

有了CanvasItemBehavior,媽媽不再用擔憂我複製粘貼修改啦~~blog

CanvasItemBehavior目前只實現了水平、垂直方向上的依次移動,至於裏面的Bubble(泡泡模式,也就是win系統的泡泡屏保效果)尚未實現,由於一通代碼寫下來,發現頭都大了,什麼數學啊物理啊,期待有志之士有趣之人實現這一效果的可以不吝賜教。ip

 

 

終於又寫了一篇博客,相信要不了多久我就會被某位獵頭看中,而後跳槽加薪,當上總經理,出任CEO,迎娶白富美,走上人生的巔峯,想一想還真是有點小激動呢~~

 

 

注:有點小激動的想拍磚的童鞋,請輕拍。

相關文章
相關標籤/搜索