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

本文关键字:故事 调用 任务 一个 何等待 等待 | 更新日期: 2023-09-27 18:05:33

我想使用故事板动画逐渐将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;
 }

但是卡住了。我是新的线程和异步的东西。谁能指出我哪里错了,或者建议一个替代方案?

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

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

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

我最近遇到了一种情况,即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);
    }
}

我添加第二个条件是因为,在Completed事件从未触发的情况下,ClockState将始终显示为活动状态,即使在动画完成后也是如此。因此,这需要显式地设置故事板的持续时间,但到目前为止,它对我来说工作得很好。

在你的例子中,你可以像这样调用静态方法

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