【问题标题】:Storyboard.Begin() occasionally blocked or delayedStoryboard.Begin() 偶尔被阻塞或延迟
【发布时间】:2012-02-07 08:34:57
【问题描述】:

我们的应用程序在消息窗口关闭时使用动画淡出消息窗口。为此,我们重写 OnClosing,如果我们还没有完成动画,我们取消关闭并开始动画。动画完成后,我们关闭窗口。根据我在网上看到的情况,这似乎是一种相当普遍的模式,因为 Closing 和 Closed 不是可路由的事件。

问题是,我们偶尔会看到一个窗口“卡住”——动画似乎没有发生,所以“完成”标志永远不会被设置,窗口只是坐在那里,取消任何关闭事件。有时,动画最终会启动并关闭窗口,但有时它看起来像是永久卡住(但显然你无法确定......停止问题等等......)。

有没有人知道为什么即使调用 Begin() 后情节提要也不会启动?由于无法真正深入挖掘 Storyboard 代码/数据结构,感觉就像是在等待某事发生,然后才真正开始,而这从未发生过。

以下是我目前排除的一些可能性:

  • 线程不正确(所有窗口、动画、情节提要等都是在 GUI 线程上创建/处理/访问的)
  • GUI 线程被阻塞(单击 X 仍会触发 Closing 事件,并且 GUI 线程会响应其他窗口、系统托盘等中的事件)
  • GUI 线程忙(我们只在 GUI 线程上做 UI 工作,而且我们通常只打开一个窗口,一次最多运行一个“关闭”动画)

也就是说,这里有一些可能有所贡献的东西:

  • Windows 可以相互关闭(在某些情况下,如果一个已经打开,新的窗口将关闭现有的窗口)
  • 我们还通过 XAML 将“淡入”动画绑定到 Loaded 事件,但这似乎可以正确完成。
  • 我们最近删除了“主”UI 窗口,因此应用程序的主窗口现在是一个虚拟隐藏窗口。

我怀疑最后一个最强烈,因为它是最近添加的,并且在该更改之前从未报告/注意到此行为(尽管我不能明确地说它从未在该更改之前发生) .但其他与 GUI/窗口相关的事件仍然有效,并且淡出动画逻辑在大多数时间都有效。

最后,这是我们用于制作结束动画的代码:

public class MyWindow : Window {
    private bool _storyBoardCompleted;
    private Storyboard _closingStoryBoard;

    protected Storyboard GetClosingStoryBoard()
    {
        Grid mainGrid = (Grid)FindName("MainGrid");
        DoubleAnimation closingAnimation = new DoubleAnimation();
        closingAnimation.From = 1;
        closingAnimation.To = 0;
        closingAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(500));

        Storyboard.SetTarget(closingAnimation, mainGrid);
        Storyboard.SetTargetProperty(closingAnimation, new PropertyPath(OpacityProperty));

        Storyboard storyboard = new Storyboard();
        storyboard.Children.Add(closingAnimation);
        storyboard.Completed += StoryboardCompleted;

        return storyboard;
    }

    private void StoryboardCompleted(object sender, EventArgs e)
    {
        _storyBoardCompleted = true;
        Close();
    }

    protected override void OnClosing(CancelEventArgs e)
    {
        if (_closingStoryBoard == null)
        {
            _closingStoryBoard = GetClosingStoryBoard();
            _closingStoryBoard.Begin();
        }

        if (!_storyBoardCompleted)
        {
            e.Cancel = true;
        }
    }
}

编辑: 每当我们及时打开这些窗口中的两个非常接近时,问题似乎就会弹出,并在显示第二个之前关闭第一个。本质上:

window1.Show()
window1.Close()
window2.Show()

此时,如果我们尝试关闭 window2,它会像上面的代码一样取消关闭,但动画不会开始(因此出现问题)。如果我们稍后调用 window2.Close() 紧跟 window3.Show() (即显示第三个窗口),则 window2 的动画结束并且窗口关闭 - 但随后 window3 卡在相同的状态。因此,一旦我们处于这种状态,这种行为似乎会从一个窗口“传递”到另一个窗口。

编辑: 我已经排除了另一种可能性 - 我想知道第二个窗口的“淡入”故事板是否与第一个窗口的“淡出”故事板发生某种冲突,并以某种方式影响第二个窗口的淡出。但是即使在删除淡入故事板之后也会出现问题。 此外,如果我用半秒调度计时器替换淡出故事板(即只是延迟关闭,没有动画),问题就完全消失了。只是一个健全的检查,它肯定与动画有关......

【问题讨论】:

    标签: c# .net windows-7


    【解决方案1】:

    你有没有尝试向 Begin 方法添加参数:

    _closingStoryBoard.Begin(this);
    

    【讨论】:

    • 我还没试过;从我对文档的阅读来看,似乎传递“this”基本上会默认任何未定位的动画以窗口为目标。但我只有一个动画,它有一个目标。不过值得一试,谢谢!
    • 不幸的是,这并没有解决问题,但我认为我已经找到了重现它的方法。
    【解决方案2】:

    我无法完全确定为什么会发生这种情况,但这似乎与我在显示第二个窗口时第一个窗口仍在动画的事实有关。我仍然不明白为什么这会导致问题,但我能够通过等待第一个完全关闭然后再显示第二个(基本上挂钩到第一个窗口的 Closed 事件)来解决它。

    仍然很想知道为什么会这样,以防“等待第一个窗口关闭”的解决方法不再足够......

    【讨论】:

    • 我怀疑这是因为 WPF 基于 DirectX 并且每个窗口都需要自己的 DirectX 窗口。我不相信 DirectX 可以很好地处理多个窗口,但我认为 DX11 在这方面有所改进。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 2015-01-06
    • 1970-01-01
    相关资源
    最近更新 更多