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线程上运行。这似乎是矛盾的。 我不知道我是否有错误的理解。而且,这个场合有什么解决方案吗?
答案 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);
事件)转换为等待处理的任务,从而使处理更加轻松。