Silverlight 2 - 程式設計師與版面設計師的分工

從網頁應用程式時代開始,程式設計師與版面設計師間如何分工,便是一個難解的習題,程式設計師的專長是寫程式,而版面設計師的專長是做出美美的網頁,

 

Silverlight 2 - 程式設計師與版面設計師的分工
 
 
/黃忠成
 
 
談真實Silverlight 2的應用程式開發
 
   從網頁應用程式時代開始,程式設計師與版面設計師間如何分工,便是一個難解的習題,程式設計師的專長是寫程式,而版面設計師的專長是做出美美的網頁,
程式設計師多半缺乏美感,而版面設計師多半不會寫程式,專案主導者總是嘗試在兩者間找出一個合作流程,但結局多半不如預期。
 
   我們都知道,最好的情況應該是,版面設計師先將版面設計好,再交給程式設計師補上程式流程,但問題是,版面設計師由於對程式不熟悉,因此對於頁面的切換,
按下按紐後的行為等動作難以著墨,最後這些事依然回到程式設計師身上,專案在兩者間流動是常見的事,既然流動,必然也會發生程式流程影響原來版面,或是版面
無法在程式流程中維持不變。結局多半是,版面設計師被迫學會JavaScript+部份的ASP.NET程式,而程式設計師被迫學會小幅的版面配置美學。
 
   同樣的情況在Silverlight 2應用程式開發時也發生,先天上的架構,讓Silverlight 2應用程式開發可以分成兩個部份,一是單純XAML的畫面設計,二是事件觸發、
資料繫結的程式撰寫,就分工上而言,單純XAML的畫面設計工作應該交給版面設計師來處理,而事件觸發、資料繫結部份則交給程式設計師。
 
但事情並非如此順利,以一個簡單的ListBox+Item動畫來說,這原本應該是版面設計師的工作,因為Item被選取時的動畫,應該是由版面設計師所設計的,
ListBoxItem來自資料繫結,如何讓版面設計師於Blend 2上就可以繫結資料,成了一大難題。
另一方面,有時版面設計師會希望,當使用者將滑鼠移往某控件上時撥放一段動畫,而這段動畫自然是由版面設計師來做,但滑鼠移往某控件上然後撥放動畫,
這卻會落到程式設計師頭上,至少!在Silverlight 2Blend 2上是如此。
 
 
關於設計時期資料繫結
 
 Blend 2在設計Silverlight 2專案時,並不如WPF專案般允許XML資料繫結,因此要達到設計時期資料繫結動作的唯一途徑是使用CLR物件,如圖123所示。
1
2
3
當擁有資料繫結能力後,版面設計師就能在不寫半行程式碼的情況下,做出類似當滑鼠移往某個Item項目後的動畫展現,且可以立即以F5來執行看成果。
如果搭配上程式設計師開發由ListBox繼承而來的自訂控件,那麼版面設計師能做的事就更多了。
 
另外,值得一提的是,CLR物件取得的資料可以來自很多來源,XML或是Web Services皆可,這可讓版面設計師能於設計時期得到真實的資料,以做出趨近需求的版面。
(PS: Blend 3 Preview提供了XML Binding機制,可讓設計師於設計時期由XML進行Binding)
 
下面是SLDataProvider的原始碼,這是一個Silverlight Class Library專案,裡面只有一個.cs檔案(程式1),當使用Blend 2時,只須將此專案加入參考即可透過CLR物件來使用Data Binding機制。
程式1
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
using System.Collections.Generic;
 
namespace SLDataProvider
{
    public class Class1
    {
        private static List<string> _data = new List<string>();
 
        public List<string> Data
        {
            get
            {
                if (_data.Count == 0)
                {
                    try
                    {
                        System.Windows.Application.Current.Host.Source.ToString();
                        ReadServer();
                    }
                    catch (Exception)
                    {
                        ReadLocal();
                    }
                }
                return _data;
            }
        }
 
        private static void ReadLocal()
        {
            using (FileStream fs = new FileStream(@"C:\ttt2.txt",
                     FileMode.Open, FileAccess.Read))
            {
                using (StreamReader sr = new StreamReader(fs))
                {
                    while (!sr.EndOfStream)
                    {
                        _data.Add(sr.ReadLine());
                    }
                }
            }
        }
 
        public static void ReadServer()
        {
            WebClient wc = new WebClient();
            wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
            wc.OpenReadAsync(new Uri("http://localhost:82/ttt2.txt", UriKind.Absolute));
        }
 
        static void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            _data.Clear();
            using (StreamReader sr = new StreamReader(e.Result))
            {
                while (!sr.EndOfStream)
                {
                    _data.Add(sr.ReadLine());
                }
            }
        }
    }
}
特別注意一點,在Data的屬性存取子上,我們利用了Host.Source.ToString();來判斷目前是處於設計時期或執行時期,當這段程式碼可運行,沒有產生例外時,就是處於執行時期,
反之則是處於設計時期,在設計時期我們直接讀取本地端的XML,反之則讀取Server端的XML。
 
另外,由於Silverlight的網路動作皆為非同步,所以在.cs中,必須意識到此點,運用DispatcherTimer來重新設置ItemsSource屬性,如下所示:
using System;
using System.Collections;
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;
 
namespace SilverlightApplication6
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
        }
 
        private void Storyboard_Completed(object sender, EventArgs e)
        {
 
        }
 
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            System.Windows.Threading.DispatcherTimer timer =
                     new System.Windows.Threading.DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(1200);
            timer.Tick += new EventHandler(timer_Tick);
            timer.Start();
        }
 
        void timer_Tick(object sender, EventArgs e)
        {
            object o = lst2.ItemsSource;
            lst2.ItemsSource = null;
            lst2.ItemsSource = o as IEnumerable;
            timer.Stop();
        }
    }
}
 
 
PS: OK,我知道用DispatcherTimer來做很偷懶,不過我相信你會找到更好的解法,例如透過Tag或是下面的Custom Attached Properties協助,
讓重置ItemsSource的動作變的更優雅。
 
 
事件觸發
 
   相對於資料繫結問題,事件觸發後的動畫撥放問題就較為麻煩,就實務情況而言,能不能讓版面設計師在不寫半行程式情況下,做出當滑鼠移往控件上時撥放一段動畫,
是能否將版面設計與程式設計完全切離的關鍵。
 
   不幸的是,Blend 2對於Silverlight 2專案的設計,並不支援Loaded外的Event Trigger,這使得版面設計師無法在不寫程式的情況下,做出前面所提到的效果,
結局多半是版面設計師得寫上一小段程式才能完成,要達到不寫程式而完成需求,直到Silverlight 3Blend 3推出之前,都很難做到。
 
   Blend 2上,對此我的回答是Custom Attached Properties,也就是附加屬性機制,透過這個機制,我們能撰寫一個Silverlight Class Library專案,於內加入一個類別,
然後將屬性附加到所有控件上,在使用者設定屬性值後自動掛載事件來撥放指定動畫,搭配上網路上可得到的自動XAML完成機制,可以做到未來Blend 3會提供的
MouseOverClickEvent Trigger功能,下面是此類別的原始碼。
 
程式3
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;
using System.Collections.Generic;
 
namespace SLTrigger
{
    public class Trigger
    {
        public static readonly DependencyProperty Trigger_ClickProperty =
           DependencyProperty.RegisterAttached(
            "Click",
            typeof(string),
            typeof(Trigger),
            new PropertyMetadata(
                new PropertyChangedCallback(OnClickPropertyChanged)));
 
 
        public static readonly DependencyProperty Trigger_MouseOverProperty =
            DependencyProperty.RegisterAttached(
            "MouseOver",
            typeof(string),
            typeof(Trigger),
            new PropertyMetadata(
                new PropertyChangedCallback(OnClickPropertyChanged)));
 
 
        public static readonly DependencyProperty Trigger_MouseLeaveProperty =
            DependencyProperty.RegisterAttached(
            "MouseLeave",
            typeof(string),
            typeof(Trigger),
            new PropertyMetadata(
                new PropertyChangedCallback(OnClickPropertyChanged)));
 
        public static readonly DependencyProperty Trigger_LostFocusProperty =
             DependencyProperty.RegisterAttached(
            "LostFocus",
            typeof(string),
            typeof(Trigger),
            new PropertyMetadata(
                new PropertyChangedCallback(OnClickPropertyChanged)));
 
        public static readonly DependencyProperty Trigger_GotFocusProperty =
            DependencyProperty.RegisterAttached(
            "GotFocus",
            typeof(string),
            typeof(Trigger),
            new PropertyMetadata(
                new PropertyChangedCallback(OnClickPropertyChanged)));
 
        public static readonly DependencyProperty Trigger_SelectionChangedProperty =
            DependencyProperty.RegisterAttached(
           "SelectionChanged",
           typeof(string),
           typeof(Trigger),
           new PropertyMetadata(
               new PropertyChangedCallback(OnClickPropertyChanged)));
 
 
        public static readonly DependencyProperty Trigger_TargetProperty =
            DependencyProperty.RegisterAttached(
           "Target",
           typeof(string),
           typeof(Trigger),
           new PropertyMetadata(
               new PropertyChangedCallback(OnClickPropertyChanged)));
 
 
        public static readonly DependencyProperty Trigger_ZIndexProperty =
             DependencyProperty.RegisterAttached(
           "ZIndex",
           typeof(string),
           typeof(Trigger),
           new PropertyMetadata(
               new PropertyChangedCallback(OnClickPropertyChanged)));
 
        public static readonly DependencyProperty Trigger_SelectedProperty =
             DependencyProperty.RegisterAttached(
           "Selected",
           typeof(string),
           typeof(Trigger),
           new PropertyMetadata(
               new PropertyChangedCallback(OnClickPropertyChanged)));
 
 
        private static Dictionary<object, List<object>> tempZIndex =
                new Dictionary<object, List<object>>();
 
        private static void OnClickPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }
 
        private static UserControl GetUserControl(FrameworkElement start)
        {
            if (start.Parent is UserControl)
                return start.Parent as UserControl;
            return GetUserControl((FrameworkElement)start.Parent);
        }
 
        public static void SetClick(UIElement element, string value)
        {
            if (element is ButtonBase)
            {
                ButtonBase bb = (ButtonBase)element;
                bb.Click -= new RoutedEventHandler(ButtonClickEvent);
                bb.SetValue(Trigger_ClickProperty, value);
                if (value != string.Empty && value != null)
                    bb.Click += new RoutedEventHandler(ButtonClickEvent);
                return;
            }
            element.MouseLeftButtonUp -= new MouseButtonEventHandler(ClickEvent);
            element.SetValue(Trigger_ClickProperty, value);
            if (value != string.Empty && value != null)
                element.MouseLeftButtonUp += new MouseButtonEventHandler(ClickEvent);
        }
 
        public static string GetClick(UIElement element)
        {
            object value = element.GetValue(Trigger_ClickProperty);
            if (value == null)
                return string.Empty;
            return (string)value;
        }
 
        public static void SetMouseOver(UIElement element, string value)
        {
            element.MouseEnter -= new MouseEventHandler(MouseOverEvent);
            element.SetValue(Trigger_MouseOverProperty, value);
            if (value != string.Empty && value != null)
                element.MouseEnter += new MouseEventHandler(MouseOverEvent);
        }
 
        public static string GetMouseOver(UIElement element)
        {
            object value = element.GetValue(Trigger_MouseOverProperty);
            if (value == null)
                return string.Empty;
            return (string)value;
        }
 
        public static void SetMouseLeave(UIElement element, string value)
        {
            element.MouseEnter -= new MouseEventHandler(MouseLeaveEvent);
            element.SetValue(Trigger_MouseLeaveProperty, value);
            if (value != string.Empty && value != null)
                element.MouseEnter += new MouseEventHandler(MouseLeaveEvent);
        }
 
        public static string GetMouseLeave(UIElement element)
        {
            object value = element.GetValue(Trigger_MouseLeaveProperty);
            if (value == null)
                return string.Empty;
            return (string)value;
        }
 
 
        public static void SetGotFocus(UIElement element, string value)
        {
            element.GotFocus -= new RoutedEventHandler(GotFocusEvent);
            element.SetValue(Trigger_GotFocusProperty, value);
            if (value != string.Empty && value != null)
                element.GotFocus += new RoutedEventHandler(GotFocusEvent);
        }
 
        public static string GetGotFocus(UIElement element)
        {
            object value = element.GetValue(Trigger_GotFocusProperty);
            if (value == null)
                return string.Empty;
            return (string)value;
        }
 
        public static void SetLostFocus(UIElement element, string value)
        {
            element.LostFocus -= new RoutedEventHandler(LostFocusEvent);
            element.SetValue(Trigger_LostFocusProperty, value);
            if (value != string.Empty && value != null)
                element.LostFocus += new RoutedEventHandler(LostFocusEvent);
        }
 
        public static string GetLostFocus(UIElement element)
        {
            object value = element.GetValue(Trigger_LostFocusProperty);
            if (value == null)
                return string.Empty;
            return (string)value;
        }
 
 
        public static void SetZIndex(UIElement element, string value)
        {
            element.SetValue(Trigger_ZIndexProperty, value);
        }
 
        public static string GetZIndex(UIElement element)
        {
            object value = element.GetValue(Trigger_ZIndexProperty);
            if (value == null)
                return string.Empty;
            return (string)value;
        }
 
 
        public static void SetSelected(UIElement element, string value)
        {
            element.SetValue(Trigger_SelectedProperty, value);
        }
 
        public static string GetSelected(UIElement element)
        {
            object value = element.GetValue(Trigger_SelectedProperty);
            if (value == null)
                return string.Empty;
            return (string)value;
        }
 
        public static void SetTarget(UIElement element, string value)
        {
            element.SetValue(Trigger_TargetProperty, value);
        }
 
        public static string GetTarget(UIElement element)
        {
            object value = element.GetValue(Trigger_TargetProperty);
            if (value == null)
                return string.Empty;
            return (string)value;
        }
 
        public static void SetSelectionChanged(UIElement element, string value)
        {
            if(element is Selector)
            {
                Selector sel = (Selector)element;
                sel.SelectionChanged -=
                  new SelectionChangedEventHandler(SelectionChangedEvent);
                element.SetValue(Trigger_SelectionChangedProperty, value);
                if (value != string.Empty && value != null)
                    sel.SelectionChanged +=
                    new SelectionChangedEventHandler(SelectionChangedEvent);
            }
            else if (element is TabControl)
            {
                TabControl sel = (TabControl)element;
                sel.SelectionChanged -=
                  new SelectionChangedEventHandler(SelectionChangedEvent);
                element.SetValue(Trigger_SelectionChangedProperty, value);
                if (value != string.Empty && value != null)
                    sel.SelectionChanged +=
                 new SelectionChangedEventHandler(SelectionChangedEvent);
            }
        }
 
        public static string GetSelectionChanged(UIElement element)
        {
            object value = element.GetValue(Trigger_SelectionChangedProperty);
            if (value == null)
                return string.Empty;
            return (string)value;
        }
 
        public static void ButtonClickEvent(object sender, EventArgs e)
        {
            ClickEvent(sender, null);
        }
 
        public static void ClickEvent(object sender, MouseButtonEventArgs e)
        {
            string ani = GetClick(sender as UIElement);
            if (ani != string.Empty)
            {
                UserControl uc = GetUserControl(sender as FrameworkElement);
                Storyboard sb = uc.FindName(ani) as Storyboard;
                Begin_Storyboard((UIElement)sender, sb);
            }
        }
 
        public static void MouseOverEvent(Object sender, MouseEventArgs e)
        {
            string ani = GetMouseOver(sender as UIElement);
            if (ani != string.Empty)
            {
                UserControl uc = GetUserControl(sender as FrameworkElement);
                Storyboard sb = uc.FindName(ani) as Storyboard;
                Begin_Storyboard((UIElement)sender, sb);
            }
        }
 
        public static void MouseLeaveEvent(Object sender, MouseEventArgs e)
        {
            string ani = GetMouseLeave(sender as UIElement);
            if (ani != string.Empty)
            {
                UserControl uc = GetUserControl(sender as FrameworkElement);
                Storyboard sb = uc.FindName(ani) as Storyboard;
                Begin_Storyboard((UIElement)sender, sb);
            }
        }
 
 
        public static void GotFocusEvent(Object sender, RoutedEventArgs e)
        {
            string ani = GetGotFocus(sender as UIElement);
            if (ani != string.Empty)
            {
                UserControl uc = GetUserControl(sender as FrameworkElement);
                Storyboard sb = uc.FindName(ani) as Storyboard;
                Begin_Storyboard((UIElement)sender, sb);
            }
        }
 
        public static void LostFocusEvent(Object sender, RoutedEventArgs e)
        {
            string ani = GetLostFocus(sender as UIElement);
            if (ani != string.Empty)
            {
                UserControl uc = GetUserControl(sender as FrameworkElement);
                Storyboard sb = uc.FindName(ani) as Storyboard;
                Begin_Storyboard((UIElement)sender, sb);
            }
        }
 
        private static void Begin_Storyboard(UIElement elem, Storyboard sb)
        {
            UserControl uc = GetUserControl(elem as FrameworkElement);
            if (sb != null)
            {
                sb.Stop();
                string aniTarget = GetTarget((UIElement)elem);
                if (aniTarget != string.Empty)
                {
                    if (aniTarget == "page")
                    {
                        UIElement aniTargetObj = uc;
                        Storyboard.SetTarget(sb, aniTargetObj);
                        string zIndex = GetZIndex(elem);
                        if (zIndex != string.Empty)
                        {
                            int oldZIndex = Canvas.GetZIndex(aniTargetObj);
                            if (!tempZIndex.ContainsKey(elem))
                                tempZIndex.Add(sb, new List<object>()
                                 { aniTargetObj, oldZIndex });
                            Canvas.SetZIndex(aniTargetObj, int.Parse(zIndex));
                            sb.Completed -= new EventHandler(sb_Completed);
                            sb.Completed += new EventHandler(sb_Completed);
                        }
                    }
                    else
                    {
                        UIElement aniTargetObj = uc.FindName(aniTarget) as UIElement;
                        Storyboard.SetTarget(sb, aniTargetObj);
                        string zIndex = GetZIndex(elem);
                       if (zIndex != string.Empty)
                        {
                            int oldZIndex = Canvas.GetZIndex(aniTargetObj);
                            if (!tempZIndex.ContainsKey(elem))
                                tempZIndex.Add(sb, new List<object>()
                                  { aniTargetObj, oldZIndex });
                            Canvas.SetZIndex(aniTargetObj, int.Parse(zIndex));
                            sb.Completed -= new EventHandler(sb_Completed);
                            sb.Completed += new EventHandler(sb_Completed);
                        }
                    }
                }               
                sb.Begin();
            }
        }
 
        static void sb_Completed(object sender, EventArgs e)
        {
            if(tempZIndex.ContainsKey(sender))
            {
                List<object> state = tempZIndex[sender];
                Canvas.SetZIndex((UIElement)state[0], (int)state[1]);
                tempZIndex.Remove(sender);
            }
           
        }
 
        public static void SelectionChangedEvent(Object sender, SelectionChangedEventArgs e)
        {
            string ani = GetSelectionChanged(sender as UIElement);
            UserControl uc = GetUserControl(sender as FrameworkElement);
            if (ani != string.Empty)
            {
                if (ani.ToLower() == "item")
                {
                    object currentItem = null;
                    if (sender is Selector && ((Selector)sender).SelectedItem != null)
                        currentItem = ((Selector)sender).SelectedItem;
                    else if (sender is TabControl &&
                              ((TabControl)sender).SelectedItem != null)
                        currentItem = ((TabControl)sender).SelectedItem;
 
                    string selAni = GetSelected(currentItem as UIElement);
                    if (selAni != string.Empty)
                    {
                        Storyboard sb = uc.FindName(selAni) as Storyboard;
                        string selTarget = GetTarget(currentItem as UIElement);
                        sb.Stop();
                        if (selTarget != string.Empty)
                        {
                            UIElement aniTargetObj = uc.FindName(selTarget) as UIElement;
                            Storyboard.SetTarget(sb, aniTargetObj);
                            string zIndex = GetZIndex(aniTargetObj);
                            if (zIndex != string.Empty)
                            {
                                int oldZIndex = Canvas.GetZIndex(aniTargetObj);
                                if (!tempZIndex.ContainsKey(aniTargetObj))
                                    tempZIndex.Add(sb, new List<object>()
                                    { aniTargetObj, oldZIndex });
                                Canvas.SetZIndex(aniTargetObj, int.Parse(zIndex));
                                sb.Completed -= new EventHandler(sb_Completed);
                                sb.Completed += new EventHandler(sb_Completed);
                            }
                        }
                        else
                        {
                            Storyboard.SetTarget(sb, currentItem as UIElement);
                            string zIndex = GetZIndex(currentItem as UIElement);
                            if (zIndex != string.Empty)
                            {
                                int oldZIndex = Canvas.GetZIndex(currentItem as UIElement);
                                if (!tempZIndex.ContainsKey(currentItem as UIElement))
                                    tempZIndex.Add(sb, new List<object>()
                                      { currentItem as UIElement, oldZIndex });
                                Canvas.SetZIndex(currentItem as UIElement,
                                   int.Parse(zIndex));
                                sb.Completed -= new EventHandler(sb_Completed);
                                sb.Completed += new EventHandler(sb_Completed);
                            }
                        }
                        sb.Begin();
                    }
                }
                else
                {                   
                    Storyboard sb = uc.FindName(ani) as Storyboard;
                    if (sb != null)
                    {
                        sb.Stop();
                        string aniTarget = GetTarget((UIElement)sender);
                        if (aniTarget != string.Empty)
                        {
                            UIElement aniTargetObj = uc.FindName(aniTarget) as UIElement;
                            Storyboard.SetTarget(sb, aniTargetObj);
                            string zIndex = GetZIndex(sender as UIElement);
                            if (zIndex != string.Empty)
                            {
                                int oldZIndex = Canvas.GetZIndex(sender as UIElement);
                                if (!tempZIndex.ContainsKey(sender))
                                    tempZIndex.Add(sb, new List<object>()
                                    { aniTargetObj, oldZIndex });
                                Canvas.SetZIndex(aniTargetObj, int.Parse(zIndex));
                                sb.Completed -= new EventHandler(sb_Completed);
                                sb.Completed += new EventHandler(sb_Completed);
                            }
                        }
                        else
                        {
                            if (sender is Selector && ((Selector)sender).SelectedItem != null)
                            {
                                Storyboard.SetTarget(sb,
                                   ((Selector)sender).SelectedItem as DependencyObject);
                                string zIndex = GetZIndex(sender as UIElement);
                                if (zIndex != string.Empty)
                                {
                                    int oldZIndex =
                          Canvas.GetZIndex(((Selector)sender).SelectedItem as UIElement);
                                    if (!tempZIndex.ContainsKey(sender))
                                        tempZIndex.Add(sb, new List<object>()
                      { ((Selector)sender).SelectedItem, oldZIndex });
                                    Canvas.SetZIndex(
                  ((Selector)sender).SelectedItem as UIElement, int.Parse(zIndex));
                                    sb.Completed -= new EventHandler(sb_Completed);
                                    sb.Completed += new EventHandler(sb_Completed);
                                }
                            }
                            else if (sender is TabControl &&
                                   ((TabControl)sender).SelectedItem != null)
                            {
                                Storyboard.SetTarget(sb,
                                 ((TabControl)sender).SelectedItem as DependencyObject);
                                string zIndex = GetZIndex(sender as UIElement);
                                int oldZIndex =
                      Canvas.GetZIndex(((Selector)sender).SelectedItem as UIElement);
                                if (!tempZIndex.ContainsKey(sender))
                                    tempZIndex.Add(sb, new List<object>()
                             { ((TabControl)sender).SelectedItem, oldZIndex });
                                Canvas.SetZIndex(
                          ((Selector)sender).SelectedItem as UIElement, int.Parse(zIndex));
                                sb.Completed -= new EventHandler(sb_Completed);
                                sb.Completed += new EventHandler(sb_Completed);
                            }
                        }
                        sb.Begin();
                    }
                }
            }
        }
    }
}
藉助此Class Library的協助,下面的XAML碼得以順利執行,不需要寫任何程式碼。
<UserControl
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           x:Class="SilverlightApplication19.Page"
           xmlns:trigger="clr-namespace:SLTrigger;assembly=SLTrigger"
           Width="640" Height="480">
           <UserControl.Resources>
                     <Storyboard x:Name="Storyboard1">
                                <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)">
                                           <SplineDoubleKeyFrame KeyTime="00:00:00.9000000" Value="264"/>
                                           <SplineDoubleKeyFrame KeyTime="00:00:01.2000000" Value="195"/>
                                </DoubleAnimationUsingKeyFrames>
                                <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">
                                           <SplineDoubleKeyFrame KeyTime="00:00:00.9000000" Value="12"/>
                                           <SplineDoubleKeyFrame KeyTime="00:00:01.2000000" Value="218"/>
                                </DoubleAnimationUsingKeyFrames>
                     </Storyboard>
           </UserControl.Resources>
 
           <Grid x:Name="LayoutRoot" Background="White">
                     <Button Height="36" trigger:Trigger.Click="Storyboard1" HorizontalAlignment="Left" Margin="180,58,0,0" VerticalAlignment="Top" Width="87" Content="Button"/>
                     <Ellipse Height="88" Margin="267,151,269,0" VerticalAlignment="Top" Stroke="#FF000000" RenderTransformOrigin="0.5,0.5" x:Name="ellipse">
                                <Ellipse.RenderTransform>
                                           <TransformGroup>
                                                     <ScaleTransform/>
                                                     <SkewTransform/>
                                                     <RotateTransform/>
                                                     <TranslateTransform/>
                                           </TransformGroup>
                                </Ellipse.RenderTransform>
                                <Ellipse.Fill>
                                           <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                                     <GradientStop Color="#FF000000"/>
                                                     <GradientStop Color="#FFE2C6C6" Offset="1"/>
                                           </LinearGradientBrush>
                                </Ellipse.Fill>
                     </Ellipse>
           </Grid>
</UserControl>
 
 
範例下載:
 
SLTrigger(設計時期Event Trigger支援)