【问题标题】:wpf is it possible to block ui thread while animating?wpf是否可以在动画时阻止ui线程?
【发布时间】:2016-02-07 03:12:41
【问题描述】:

伙计们,当我打算为我的女儿炫耀 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 线程上运行。似乎很矛盾。 不知道是不是我理解有误。而且,这种情况有什么解决办法吗?

【问题讨论】:

标签: wpf animation serialization synchronization


【解决方案1】:

您可以将存储板的执行包装在一个任务中并await 该任务。

这可能看起来如this answer 中所述。基本上,您订阅了StoryboardCompleted 事件。然后您可以轻松地等待结果。 (从提供的链接略微采用的代码)。

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

在您的情况下,您需要做的就是用await storyboard.BeginAsync(disc); 替换MoveOn 方法中的storyboard.Begin(disc); 调用。

通过此更改,您可以将基于事件的方法(使用 Completed 事件)转变为可等待的任务,从而使处理变得更加容易。

【讨论】:

    猜你喜欢
    • 2012-08-02
    • 1970-01-01
    • 1970-01-01
    • 2019-01-30
    • 2019-11-23
    • 1970-01-01
    • 2010-09-26
    • 2016-11-03
    • 1970-01-01
    相关资源
    最近更新 更多