【问题标题】:How do I show an animation while creating user control如何在创建用户控件时显示动画
【发布时间】:2011-01-26 13:20:25
【问题描述】:

在 WPF 4 应用程序中,我有一个非常大的用户控件,其中充满了控件,需要 4 秒才能在快速机器上进行初始化。在此期间,应用程序当然完全没有响应。
有没有办法在初始化此控件时在主窗口中显示动画?

我知道我无法在另一个线程上创建它。但是有没有办法从 Dispatcher 以较低的优先级创建它,以便我可以在主窗口上显示一个仍会旋转的旋转轮等?

(我现在能想到的唯一解决方案是将用户控件分解为更多部分并仅在需要时加载它们。但这需要大量的开发时间来改变。)

更新1
更清楚地说:这是一个使用标签页的简单 WPF 窗口。当打开一个新标签页时,我正在初始化包含此标签页控件的用户控件。其中一个用户控件的控件如此之多,以至于需要 4 秒才能显示新标签页。
所以我认为展示一个旋转的轮子比阻止一个应用程序更好。

【问题讨论】:

  • 您能否至少包含一部分有问题的代码,以了解它正在执行哪种操作?
  • @Eamon Nerbonne:嗯,这只是控制。想象一个有 9 个数据网格的用户控件,所有数据网格都至少有 12 列并使用重蒙皮。即使禁用数据加载以测试此用户控件也需要很长时间才能初始化。
  • 是的,这是 WPF 不太擅长的东西。具有讽刺意味的是,与样式化的 WPF UI 相比,普通的 javascript+CSS+浏览器 UI 通常加载速度更快,样式更通用。

标签: c# .net wpf performance animation


【解决方案1】:

我认为您将不得不将此用户控件分解为多个部分。您可以做的是使用 BackgroundWorker 来协调此用户控件的“构建”。每次触发 DoWork 事件时,使用 Dispatcher.BeginInvoke 创建一个将下一个控件添加到您的 UI。此技术在以下博客文章中进行了描述:

http://loekvandenouweland.com/index.php/2010/12/wp7-add-user-controls-graphics-in-background-thread/

这将允许您在加载过程中显示动画。

【讨论】:

  • 我刚刚查看了您提到的博客文章,这看起来确实很有趣。也许我会走那条路。
  • 根据@ColinE 的建议,看看 AdornerLayer 作为展示加载动画的地方:msdn.microsoft.com/en-us/library/ms743737.aspx
  • 我尝试了您的建议,确实效果很好。现在唯一的问题是我没有任何开发时间,但下次我会记得从一开始就以这种方式设计应用程序。不幸的是,组件初始化在 WPF 中花费了这么多时间。
  • 对不起,我首先也想测试一下 Eamon Nerbonne 的答案。最后我更喜欢你的,因为它更容易理解。
【解决方案2】:

为什么不能在另一个线程上初始化它?我看到两种情况:

  1. 由于非 WPF 原因,在进入核心 WPF 初始化之前在另一个线程上预加载/预计算,初始化速度很慢。
  2. WPF 本身消耗了 4 秒的 CPU 时间(不过,这确实是 WTF 级别的 CPU 时间......)。如果是这样,您可以使用自己的消息泵启动另一个 STA 线程,该消息泵可以显示独立的 UI(例如:旋转轮),直到主线程完成加载。

您可以创建隐式创建新 Dispatcher 并在后台线程上运行的“对话框”,或者您可以显式创建自己的 Dispatcher(=消息泵)。

我使用以下方法:

public static Dispatcher StartNewDispatcher(ThreadPriority cpuPriority = ThreadPriority.Normal) {
    using (var sem = new SemaphoreSlim(0)) {
        Dispatcher retval = null;
        var winThread = new Thread(() => {
            retval = Dispatcher.CurrentDispatcher;
            sem.Release();
            Dispatcher.Run();
        }) { IsBackground = true, Priority = cpuPriority };
        winThread.SetApartmentState(ApartmentState.STA);
        winThread.Start();
        sem.Wait();
        return retval;
    }
}

这为您提供了真正的多线程 UI;但这也意味着您不能在两个 UI 之间进行数据绑定或以任何其他方式直接通信:毕竟,WPF 对象具有线程关联性。

在你走这条路之前,确认没有任何慢速组件可以使用分析器预加载:选项 1(在 WPF 初始化之前预加载重的东西)更简单、更干净。

【讨论】:

  • 你的第二点暗示了一个新的独立(事件透明)窗口,对吧?
  • 我已经更新了这个问题,提供了一些更详细的信息,以便更清楚。不幸的是,主窗口需要与用户控件进行通信,因此两者不能在单独的线程上运行。
  • 嗯,它通常意味着第二个窗口;但我想从技术上讲,它只需要一个屏幕外缓冲区,可以通过不同线程上的 InteropBitmap 使用(例如)。我以前从未这样做过;但这是可能的。
  • @Marc:我不是建议您在单独的线程上运行用户控件,而是建议您在另一个线程上运行一些“正在加载...”动画。确实,您的主 UI 仍会被阻止。
  • 所以我检查了所有慢的部分并改进了一些地方,但我仍然需要显示一个加载对话框。你能否给我一些关于你何时以及如何准确地用你的代码显示对话框的更多细节?我在主窗口的构造函数中调用“StartNewDispatcher()”,然后在“Window_Loaded()”事件处理程序中我想关闭加载对话框。我必须使用 retval.BeginInvoke(...) 来执行此操作吗?应该在什么时候初始化并显示对话框?就在 sem.Release() 之前?非常感谢您的想法和帮助!
【解决方案3】:

一种解决方案可能是将慢速部分(加载数据?不属于控件中的)移动到另一个线程,如您所提到的。

或者使用VirtualizingStackPanel 来延迟加载所需的内容。

能否详细说明延迟的原因?

【讨论】:

    猜你喜欢
    • 2021-11-26
    • 2010-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-27
    • 2021-06-05
    相关资源
    最近更新 更多