wpf是否可以在制作动画时阻止ui线程?

时间:2016-02-06 15:22:29

标签: wpf animation serialization synchronization

伙计们,当我为女儿炫耀WPF动画解决河内问题的力量时,我遇到了一个问题。问题相关的代码如下:

   void MoveSet(Disk[] disks, int maxIndex, char from, char via, char to)
   {
        if (maxIndex > 0) MoveSet(disks, maxIndex - 1, from, to, via);
        MoveOne(disks[maxIndex], from, to);
        if (maxIndex > 0) MoveSet(disks, maxIndex - 1, via, from, to);
   }

以上代码递归移动磁盘。我在MoveOne方法中添加了动画,如下所示:

void MoveOne(Disk disk, char from, char to)
{
    // set animation parameters
    ... upAnimation...
    ... levelShiftAnimation...
    ... downAnimation...

    Storyboard.SetTarget(upAnimation, disk);
    Storyboard.SetTarget(levelShiftAnimation, disk);
    Storyboard.SetTarget(downAnimation, disk);
    storyboard.Begin(disk);
}

以上代码运作良好。但是所有的动画几乎都在同一时间运行。在几个无序动画之后,所有磁盘都改变了位置。看起来不酷。所以我想逐个显示每个磁盘的动画。我修改了MoveOne方法并进行了这样的更改:

void MoveOne(Disk disk, char from, char to)
{
    ...
    Storyboard.SetTarget(upAnimation, disk);
    Storyboard.SetTarget(levelShiftAnimation, disk);
    Storyboard.SetTarget(downAnimation, disk);

    AutoResetEvent signaler=new AutoResetEvent(false);
    EventHandler eh = null;
    eh = (s, e) => 
    {
        storyboard.Completed -= eh;
        signaler.Set();
    };
    storyboard.Completed+=eh;
    storyboard.Begin(disk);
    signaler.WaitOne();
}

上述修改使整个程序陷入困境。我认为原因是动画和MoveOne方法都只在一个UI线程中运行,阻塞MoveOne方法块也是动画。所以我尝试在另一个新创建的任务中创建并启动(使用UI调度程序调用)动画,但它还没有工作。 最后,我理顺了我的真实要求。我想运行一个动画并阻止其他动画,所有动画都在同一个唯一的UI线程上运行。这似乎是矛盾的。 我不知道我是否有错误的理解。而且,这个场合有什么解决方案吗?

1 个答案:

答案 0 :(得分:1)

您可以将storboard的执行包装在Task和await那个任务中。

这可能如this answer中所述。基本上,您订阅Completed的{​​{1}}事件。然后你可以轻松等待结果。 (稍微从提供的链接中采用的代码)。

Storyboard

在您的情况下,您需要做的就是将public static class StoryboardExtensions { public static Task BeginAsync(this Storyboard storyboard, FrameworkContentElement element) { System.Threading.Tasks.TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); if (storyboard == null) tcs.SetException(new ArgumentNullException()); else { EventHandler onComplete = null; onComplete = (s, e) => { storyboard.Completed -= onComplete; tcs.SetResult(true); }; storyboard.Completed += onComplete; storyboard.Begin(element); } return tcs.Task; } } 方法中storyboard.Begin(disc);的来电替换为MoveOn

通过此更改,您可以将基于事件的方法(使用await storyboard.BeginAsync(disc);事件)转换为等待处理的任务,从而使处理更加轻松。