【问题标题】:How to await a Task that calls a storyboard如何等待调用故事板的任务
【发布时间】:2015-07-30 20:04:39
【问题描述】:

我想使用情节提要动画逐渐将 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;
 }

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

【问题讨论】:

  • 由于您从FadeOut 返回Task,您可以.ContinueWith 删除代码...
  • @Don't Wait() 一个任务。它将阻塞调用线程。请改用await FadeOut(item).
  • @EZI 这告诉我它不能,除非它是异步的。然后当我将其标记为异步时,它会告诉我返回类型在我的返回语句中是无效的。
  • @crclayton 然后用Task替换void
  • 淡出功能是Task。如果你的意思是里面有这些调用的函数,我不能。那将意味着改变太多。

标签: c# wpf multithreading xaml asynchronous


【解决方案1】:

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

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

【讨论】:

  • 谢谢!我不熟悉那种语法,但它看起来就像我需要的一样。
【解决方案2】:

我最近遇到了 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.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-22
    • 2019-07-22
    相关资源
    最近更新 更多