WPF优化UI线程上的加载

时间:2012-10-31 17:06:39

标签: wpf wpf-controls prism

我正在尝试优化WPF prism应用程序的加载时间。加载基本上是一个循环,使用反射来创建UI元素的实例,然后将它们添加到选项卡控件中的主窗口(shell)。

由于我们仅限于使用单个线程来创建所有对象,因此加速/创建更好的用户体验的最佳方法是什么?

这是我到目前为止的选择:

  1. 使用延迟加载。仅在用户首次单击时加载选项卡。但是,当它按需初始化时,第一次开启时间为4-5秒。

  2. 缓存所有反射调用。我实际上是这样做的,但它根本没有提速。大部分时间都发生在控件的渲染过程中......

  3. 对于这个棘手的问题,任何建议都会非常感激。

4 个答案:

答案 0 :(得分:3)

你几乎被卡住,因为你只能在主线程上加载对象,所以我认为你不会让它加载得更快。

你可以做的是分散用户的注意力:我有一个动画启动画面,大约需要10秒才能完成动画序列。这有很多用途:

  1. 它显示用户动作 - 因此他们有视觉提示,某些事情正在发生
  2. 它分散了它们并填充了初始加载所占用的空间
  3. 为确保动画流畅,您需要创建第二个调度程序。我是这样做的:

    public class AppEntry : Application
        {
            private static ManualResetEvent _resetSplashCreated;
    
            internal static Thread SplashThread { get; set; }
    
            internal static SplashWindow SplashWindow { get; set; }
    
            private static void ShowSplash()
            {
                SplashWindow = new SplashWindow();
                SplashWindow.Show();
                _resetSplashCreated.Set();
                Dispatcher.Run();
            }
    
            [STAThread]
            public static void Main()
            {
                _resetSplashCreated = new ManualResetEvent(false);
                SplashThread = new Thread(ShowSplash);
                SplashThread.SetApartmentState(ApartmentState.STA);
                SplashThread.IsBackground = true;
                SplashThread.Name = "Splash Screen";
                SplashThread.Start();
    
                _resetSplashCreated.WaitOne();
    
                var app = new App();
                app.DispatcherUnhandledException += new DispatcherUnhandledExceptionEventHandler(app_DispatcherUnhandledException);
                app.InitializeComponent();
                app.Run();
    
            }
    
            static void app_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
            {
               // MessageBox.Show(e.Exception.StackTrace);
            }
        }
    

    我在项目属性/应用程序选项卡中将AppEntry类设置为我的启动对象 我在App中的OnStartup方法结束时关闭了启动画面:

     AppEntry.SplashWindow.Dispatcher.BeginInvoke(DispatcherPriority.Background,
                                                                 new Action(() => AppEntry.SplashWindow.Close()));
    

    这更快吗?没有 用户是否认为它更快?是

    有时,如果你不能给他们速度,你可以给他们活动。这是一个很好的安慰剂。

答案 1 :(得分:2)

正如您所提到的,如果对象是DependencyObjects,则不能进行多线程处理。 Kent Boogart discusses this。这就是为什么你必须利用INotifyPropertyChanged并执行POCO对象来保存数据的原因。这样,您可以多线程获取数据,然后将这些数据绑定到您的UI。使用DependencyObjects的另一个缺点是你将应用程序绑定到WPF框架(DependencyObject是WPF程序集中System.Windows命名空间中定义的类(不记得是否是PresentationCore或PresentationFramework))。如果重构不是一个选项,你将不得不考虑像LastCoder提议的解决方案。请注意,您将能够执行非常少的多线程(如果有的话),因此您的应用程序不会始终响应。

答案 2 :(得分:1)

我会实现一个计时器,每个tick(迭代)加载一些控件或选项卡。计时器将在与UI相同的线程上运行(它的控制消息将在Windows消息循环中排队)。完成所有工作后,您可以终止计时器。

计时器间隔和每个刻度线加载的控件数量将归结为使用测试;尝试像100毫秒和2控制一个滴答,每秒约20个控件,所以如果你有10个选项卡,每个15个控件,它需要〜8秒,但UI不应该锁定为坏。

答案 3 :(得分:1)

加速加载的最佳答案是在构建可视树时简单地隐藏容器。

这可以防止屏幕不断需要自我更新。

当所有元素都已添加到可视化树中时,将容器可见性设置为可见会将标签容器呈现一次。

我们还对选项卡控件项实现了一些简单的延迟呈现。

净结果:加载时间从2分钟缩短到约20秒。