2010-11-15 37 views
2

我有一個Silverlight應用程序。有一個矩形動畫到一個新位置的基本動畫。動畫由兩個DoubleAnimation()組成 - 一個轉換X,另一個轉換Y.它工作正常。如何在動畫完成之前阻止對方法的訪問

我基本上想阻止任何其他調用這個動畫方法,直到前兩個動畫完成。我看到DoubleAnimation()類具有一個Completed事件,但我沒有成功構建任何類型的代碼,直到兩者都完成,才成功阻塞。

我試圖在輸入方法時使用私有成員上的Monitor.Enter,然後釋放一個動畫Completed事件中的鎖,但我試圖鏈接兩個事件(所以直到兩個已經完成)沒有成功。

這裏的動畫方法是什麼樣子:

public void AnimateRectangle(Rectangle rect, double newX, double newY) 
    { 
      var xIsComplete = false; 

      Duration duration = new Duration(new TimeSpan(0, 0, 0, 1, 350)); 
      var easing = new ElasticEase() { EasingMode = EasingMode.EaseOut, Oscillations = 1, Springiness = 4 }; 
      var animateX = new DoubleAnimation(); 
      var animateY = new DoubleAnimation(); 

      animateX.EasingFunction = easing; 
      animateX.Duration = duration; 
      animateY.EasingFunction = easing; 
      animateY.Duration = duration; 

      var sb = new Storyboard(); 

      sb.Duration = duration; 
      sb.Children.Add(animateX); 
      sb.Children.Add(animateY); 

      Storyboard.SetTarget(animateX, rect); 
      Storyboard.SetTargetProperty(animateX, new PropertyPath("(Canvas.Left)")); 
      Storyboard.SetTarget(animateY, rect); 
      Storyboard.SetTargetProperty(animateY, new PropertyPath("(Canvas.Top)")); 

      animateX.To = newX; 
      animateY.To = newY; 
      sb.Begin(); 

    } 

編輯(添加詳細信息)

我最初遇到了這一點,因爲我打電話從其他方法這個方法(因爲它處理它製成的物品對動畫的調用)。我注意到,這些項目沒有結束,我預期他們會。我傳入的新X/Y座標是基於當前位置的項目,因此如果在完成之前多次調用該X/Y座標,則會導致錯誤的位置。作爲一項測試,我添加了一個只運行一次動畫的按鈕。有效。但是,如果我連續點擊一次按鈕,就會看到與之前相同的行爲:項目最終位於錯誤的位置。

是的,看起來Silverlight動畫是在主UI線程上運行的。我嘗試過的一個測試是添加了兩個屬性,標記了兩個動畫是否都已完成。在AnimateRectange()方法中,我在一個while循環中調用了它們(調用Thread.Sleep)。這個循環從未完成(所以它絕對在同一個線程上)。

所以我創建了一個隊列來處理動畫順序:

private void ProcessAnimationQueue() 
    { 
     var items = this.m_animationQueue.GetEnumerator(); 
     while (items.MoveNext()) 
     { 
      while (this.m_isXanimationInProgress || this.m_isYanimationInProgress) 
      { 
       System.Threading.Thread.Sleep(100); 
      } 

      var item = items.Current; 
      Dispatcher.BeginInvoke(() => this.AnimateRectangle(item.Rect.Rect, item.X, item.Y));     
     } 
    } 

然後,我打電話給我的最初的程序(其中排隊的動畫),並調用此方法在一個新線程。我看到了相同的結果。

回答

0

據我所知,Silverlight中的所有動畫都是在UI線程上發生的。我猜測只有UI線程調用這個動畫函數,所以我不確定使用鎖定會有所幫助。你真的想要阻止整個線程或阻止另一個動畫的啓動嗎?

我會更喜歡這個建議的內容:

private bool isAnimating = false; 

public void AnimateRectangle(Rectangle rect, double newX, double newY) 
{ 
    if (isAnimating) 
     return; 

    // rest of animation code 

    sb.Completed += (sender, e) => 
    { 
     isAnimating = false; 
    }; 

    isAnimating = true; 
    sb.Begin(); 
} 

只要不斷的您是否正在與一個標誌動畫跟蹤和提前返回,如果你是。如果你不想失去潛在的動畫,你的其他選擇是保持某種動畫的隊列,當每個動畫完成時你可以檢查/開始動畫。

0

這個問題確實達到了我的興趣。事實上,我將把它列入我的下一篇博客文章。

爲了確保我們談論的是同樣的事情,您不希望阻止撥打AnimateRectangle的電話,您只是想對呼叫進行「排隊」,以便一旦未完成的呼叫完成其呼叫動畫這個「排隊」的調用得到執行。如果以前的呼叫尚未開始,您可能需要排隊幾個呼叫。

因此,我們需要兩樣東西: -

  1. 一種手段來對待什麼本質上是異步操作(sb.Begin已完成事件)的順序操作,一個操作僅在啓動時以前完成。
  2. 當一個或多個操作尚未完成時排隊附加操作的方法。

AsyncOperationService

項目1在Silverlight中是數不勝數不同的方式出現由於這麼多的事情異步特性。我用一個簡單的異步操作跑步者博客here來解決這個問題。將AsyncOperationService代碼添加到您的項目中。

AsyncOperationQueue

其第2項真的啓動了我的興趣。這裏的變化是,雖然現有的一套操作正在進行中,但仍有需求增加另一個操作。對於一般情況下的解決方案,我們需要一個包含其他操作的線程安全方法。

這裏是一個的裸機: -

public class AsyncOperationQueue 
{ 
    readonly Queue<AsyncOperation> myQueue = new Queue<AsyncOperation>(); 
    AsyncOperation myCurrentOp = null; 

    public void Enqueue(AsyncOperation op) 
    { 
     bool start = false; 

     lock (myQueue) 
     { 
      if (myCurrentOp != null) 
      { 
       myQueue.Enqueue(op); 
      } 
      else 
      { 
       myCurrentOp = op; 
       start = true; 
      } 
     } 

     if (start) 
      DequeueOps().Run(delegate { }); 
    } 

    private AsyncOperation GetNextOperation() 
    { 
     lock (myQueue) 
     { 
      myCurrentOp = (myQueue.Count > 0) ? myQueue.Dequeue() : null; 
      return myCurrentOp; 
     } 
    } 

    private IEnumerable<AsyncOperation> DequeueOps() 
    { 
     AsyncOperation nextOp = myCurrentOp; 
     while (nextOp != null) 
     { 
      yield return nextOp; 
      nextOp = GetNextOperation(); 
     } 
    } 
} 

將其投入使用

首先要做的就是將您現有AnimateRectangle方法爲GetAnimateRectangleOp返回一個AsyncOperation。像這樣的: -

public AsyncOperation GetAnimateRectangleOp(Rectangle rect, double newX, double newY) 
    { 
     return (completed) => 
     { 
      // Code identical to the body of your original AnimateRectangle method. 
      sb.Begin(); 

      sb.Completed += (s, args) => completed(null); 
     }; 

    } 

我們需要持有的一個實例: -

private AsyncOperationQueue myAnimationQueue = new AsyncOperationQueue(); 

最後,我們需要重新創建AnimateRectangle即排入操作隊列: -

public void AnimateRectangle(Rectangle rect, double newX, double newY) 
{ 
    myAnimationQueue.Enqueue(GetAnimateRectangleOp(rect, newX, newY) 
} 
相關問題