如何等待调用故事板的任务

时间:2015-07-30 20:04:40

标签: c# wpf multithreading xaml asynchronous

我想使用故事板动画逐渐将WPF元素的可见性转换为隐藏,并在继续执行代码之前等待它完成。但是,我不想使用Storyboard.Completed属性转到不同的函数,因为它需要传递给它的参数,而且会增加复杂性。

我想它的工作原理如下:

ListBoxItem itemToRemove = sender as ListBoxItem;    
FadeOut(itemToRemove);      // this needs to wait until animation is completed
ListBox.Items.Remove(item); // because this line will remove it immediately

这是我的故事板:

    <Storyboard x:Key="fadeOut">
        <ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0">
                <DiscreteObjectKeyFrame.Value>
                    <Visibility>Visible</Visibility>
                </DiscreteObjectKeyFrame.Value>
            </DiscreteObjectKeyFrame>
        </ObjectAnimationUsingKeyFrames>
        <DoubleAnimation BeginTime="0:0:0.0" Duration="0:0:0.5" From="1" Storyboard.TargetProperty="Opacity" To="0" />
    </Storyboard>

我称之为:

((Storyboard)FindResource("fadeOut")).Begin(item);

但是,我想等到故事板完成后再继续。

我试过了:

((Storyboard)FindResource("fadeOut")).Completed += FadeOut_Completed;
((Storyboard)FindResource("fadeOut")).Begin(item);

但是,我无法将参数item传递给FadeOut_Completed以将其从ListBox中删除。

所以我尝试了这个:

 FadeOut(item).Wait();

其中

 public Task FadeOut(ListBoxItem item)
 {
      var tcs = new TaskCompletionSource<bool>();
      ((Storyboard)FindResource("fadeOut")).Begin(item);
      ((Storyboard)FindResource("fadeOut")).Completed += (s, e) => tcs.SetResult(true);
      return tcs.Task;
 }

但是那个卡住了。我是线程和异步的新手。有人可以指出我出错的地方或提出替代方案吗?

2 个答案:

答案 0 :(得分:2)

Completed事件处理程序中获取项目的一种简单方法是使用闭包。

var storyborard = (Storyboard)FindResource("fadeOut");
storyborard.Completed += (s,e) => ListBox.Items.Remove(item);
storyborard.Begin(item);

答案 1 :(得分:1)

我最近遇到了一个情况,即Completed事件没有按时触发,或者有时根本没有触发,即使我在调用Begin()之前设置事件处理程序(这是其他大多数人的罪魁祸首)我找到的相关帖子)。所以我一直在寻找一种等待动画结束的方法,然后继续使用相同的方法。我最终创建了下面的静态方法,该方法将您想要的故事板作为参数(加上您的方案的FrameworkElement)。

public async static Task RunStoryboard(Storyboard Story, FrameworkElement item)
{
    Story.Begin(item);
    while (Story.GetCurrentState() == ClockState.Active && Story.GetCurrentTime() < Story.Duration)
    {
        await Task.Delay(100);
    }
}

我添加了第二个条件,因为在完成事件永远不会触发的情况下,即使在动画结束后,ClockState也始终显示为活动状态。因此,这需要明确设置故事板的持续时间,但到目前为止它一直很适合我。

所以在你的情况下,你只需要调用静态方法。

var storyboard = (Storyboard)FindResource("fadeOut");
await RunStoryboard(storyboard, item);
// ... continue code here.