如何在动画完成之前阻止对方法的访问 [英] How do I block access to a method until animations are complete

查看:46
本文介绍了如何在动画完成之前阻止对方法的访问的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 Silverlight 应用程序.它有一个基本动画,其中一个矩形被动画到一个新的位置.动画由两个 DoubleAnimation() 组成 - 一个转换 X,另一个转换 Y.它工作正常.

I have a Silverlight app. that has a basic animation where a rectangle is animated to a new position. The animation consists of two DoubleAnimation() - one transforms the X, the other transforms the Y. It works OK.

我基本上想阻止对这个 animate 方法的任何其他调用,直到前两个动画完成.我看到 DoubleAnimation() 类有一个 Completed 事件它会触发,但我没有成功构建任何类型的代码,在两者都完成之前成功阻止.

I basically want to block any other calls to this animate method until the first two animations have completed. I see that the DoubleAnimation() class has a Completed event it fires but I haven't been successful in constructing any kind of code that successfully blocks until both have completed.

我尝试在进入方法时对私有成员使用 Monitor.Enter,然后从动画 Completed 事件之一中释放锁定,但我尝试链接两个事件(因此锁定不会释放,直到两者都已完成)完成)尚未成功.

I attempted to use Monitor.Enter on a private member when entering the method, then releasing the lock from one of the animations Completed event, but my attempts at chaining the two events (so the lock isn't released until both have completed) haven't been successful.

动画方法如下所示:

    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 坐标基于项目当前位置,因此如果在完成之前多次调用它,它最终会出现在错误的位置.作为测试,我添加了一个仅运行一次动画的按钮.有效.但是,如果我连续多次单击该按钮,我会看到与以前相同的行为:项目最终位于错误的位置.

I ran into this initially because I was calling this method from another method (as it processed items it made a call to the animation). I noticed that the items didn't end up where I expected them to. The new X/Y coordinates I pass in are based on the items current location, so if it was called multiple times before it finished, it ended up in the wrong location. As a test I added a button that only ran the animation once. It worked. However, if I click on the button a bunch of times in a row I see the same behavior as before: items end up in the wrong location.

是的,Silverlight 动画似乎在主 UI 线程上运行.我尝试的其中一项测试添加了两个属性,用于标记两个动画是否都已完成.在 AnimateRectange() 方法中,我在 while 循环(调用 Thread.Sleep)内检查了它们.这个循环从未完成(所以它肯定在同一个线程上).

Yes, it appears Silverlight animations are run on the main UI thread. One of the tests I tried I added two properties that flagged whether both animations had completed yet. In the AnimateRectange() method I checked them inside of a while loop (calling Thread.Sleep). This loop never completed (so it's definitely on the same thread).

所以我创建了一个队列来按顺序处理动画:

So I created a queue to process the animations in order:

    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));                
        }
    }

然后我调用我的初始例程(将动画排队)并在新线程上调用此方法.我看到了相同的结果.

Then I call my initial routine (which queues up the animations) and call this method on a new thread. I see the same results.

推荐答案

这个问题真的引起了我的兴趣.事实上,我将在下一篇博文中包含它.

This question really peaked my interest. In fact I'm going to include it in my next blog post.

归根结底,只是为了确保我们在谈论同一件事,从根本上说,您不想阻止对 AnimateRectangle 的调用,您只想将调用排队"以便一次任何未完成的调用都已完成其动画,此排队"调用将被执行.通过分机,如果前一个呼叫尚未开始,您可能需要将多个呼叫排队.

Boiling it down, just to be sure we are talking about the same thing, fundementally you don't want to block the call to AnimateRectangle you just want to "queue" the call so that once any outstanding call has completed its animation this "queued" call gets executed. By extension you may need to queue several calls if a previous call hasn't even started yet.

所以我们需要两件事:-

So we need two things:-

  1. 一种将本质上是异步的操作(sb.Begin 到 Completed 事件)视为顺序操作的一种方法,一个操作仅在前一个操作完成时开始.
  2. 一种在一个或多个操作尚未完成时将其他操作排队的方法.
  1. A means to treat what are essentially asynchronous operations (sb.Begin to Completed event) as a sequential operation, one operation only starting when the previous has completed.
  2. A means to queue additional operations when one or more operations are yet to complete.

异步操作服务

由于许多事物的异步性质,项目 1 在 Silverlight 中以无数种不同的方式出现.我用一个简单的异步操作运行器解决了这个问题,博客 这里.将 AsyncOperationService 代码添加到您的项目中.

Item 1 comes up in a zillion different ways in Silverlight due to the asynchronous nature of so many things. I solve this issue with a simple asynchronous operation runner blogged here. Add the AsyncOperationService code to your project.

异步操作队列

它的第 2 项真正引起了我的兴趣.这里的变化是,当一组现有的操作正在进行时,需要添加另一个.对于一般情况下的解决方案,我们需要一种线程安全的方法来包含另一个操作.

Its item 2 that really took my interest. The variation here is that whilst an existing set of operations are in progress there is demand to add another. For a general case solution we'd need a thread-safe means of including another operation.

这是 AsyncOperationQueue 的基本结构:-

Here is the bare-bones of a AsyncOperationQueue:-

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 方法转换为返回 AsyncOperationGetAnimateRectangleOp.像这样:-

First thing to do is convert your existing AnimateRectangle method into a GetAnimateRectangleOp that returns a AsyncOperation. Like this:-

    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);
        };

    }

我们需要持有一个 AsyncOperationQueue 的实例:-

We need to hold an instance of the AsyncOperationQueue:-

 private AsyncOperationQueue myAnimationQueue = new AsyncOperationQueue();

最后我们需要重新创建AnimateRectangle,将操作排入队列:-

Finally we need to re-create AnimateRectangle that enqueues the operation to the queue:-

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

这篇关于如何在动画完成之前阻止对方法的访问的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆