【问题标题】:Improve perceived WPF app startup time改善感知的 WPF 应用程序启动时间
【发布时间】:2011-01-19 02:26:45
【问题描述】:

我有一个 WPF 数据库查看器应用程序:它是一个简单的主窗口,其中包含一个用户控件和一个数据网格,显示从 SQLite 数据库中提取的数据。
问题是这个应用程序需要 6 秒才能启动,直到它可用。

我尝试在主窗口的构造函数中构建用户控件(并完成所有数据加载):
启动画面会以这种方式显示 5 秒,然后是 1 秒的空白主窗口,直到应用程序可以使用为止。
用户表示,事情(视觉上)发生需要很长时间。

然后我将用户控件创建(和数据加载)移到主窗口的 Loaded 事件处理程序中: 初始屏幕将显示 3 秒,然后是 3 秒的空白主窗口,直到应用程序准备就绪。
用户说它“更好”,但不喜欢半成品主窗口显示禁用状态这么长时间。

是否有一些关于感知应用程序加载时间的一般性建议,或者是否有任何其他关于如何改善这种情况的建议?
我相信理想情况下,主窗口会尽可能快地显示,以及一些沙漏或微调器,直到加载数据。但是我不能只将用户控件的创建移到后台工作人员中,因为这将在错误的线程上完成。

有人对这个问题有什么建议吗?

编辑:
请注意,现在我刚刚分配了一个 LINQ-to-EF 查询作为网格数据源。
一种可能的改进可能是在后台将此数据加载到数据表中,并且仅在加载后分配它...

编辑2: 我使用带有 System.Data.SQLite 和 EF4 的 .net 4 来加载数据。大约有 4000 行和 30 列。

【问题讨论】:

  • 您从数据库中加载了多少数据?您如何加载数据 - 使用 ORM(如果是,是哪个?)或普通 ADO.NET?
  • 让用户知道很多事情正在发生。查看实际情况并进行相应优化。登录屏幕或扩展启动画面(带有一些波浪形动画/进度条)可能正是他们想要的。
  • @alimbada:我已编辑问题以包含此信息。但它比实体框架等更普遍的问题。ADO.NET 或任何 ORM 将花费时间来加载数据,您也可以用 Thread.Sleep(..) 替换它。问题更多是关于如何处理这种情况。

标签: c# .net wpf performance app-startup


【解决方案1】:

异步加载数据。加载时在 GUI 上为用户呈现一些不错的东西。以下代码可以帮助您:

BackgroundWorker bgWorker = new BackgroundWorker() { WorkerReportsProgress=true};  
bgWorker.DoWork += (s, e) => {      
    // Load here your file/s      
    // Use bgWorker.ReportProgress(); to report the current progress  
};  
bgWorker.ProgressChanged+=(s,e)=>{      
    // Here you will be informed about progress and here it is save to change/show progress. 
    // You can access from here savely a ProgressBars or another control.  
};  
bgWorker.RunWorkerCompleted += (s, e) => {      
// Here you will be informed if the job is done. 
// Use this event to unlock your gui 
};  
bgWorker.RunWorkerAsync();  

该应用程序并不快,但似乎要快得多,因为 GUI 立即可见且响应迅速。也许您也可以在加载其余部分时向用户显示部分加载的数据。使用ProgressChanged-event 来执行此操作。

更新

我不确定我是否正确理解了您的问题。如果您的问题不是需要加载数据的时间,那么您的应用程序中有一些奇怪的地方。 WPF 是 IMO 非常快。控件创建不需要很多时间。正如您在几毫秒内提到的那样,我将更大的列表可视化。

尝试查看您的 UI 中是否存在阻碍 DataGrid 虚拟化项目的内容。也许你有问题。要分析 WPF 应用程序,我可以向您推荐 WPF Profiling Tools

【讨论】:

  • 另外,Silverlight 就是这样做的。 (语法不同,原理相同。)
  • 是的,这就是我迄今为止处理缓慢操作的方式,并且效果很好。唯一的问题是,在调用 bgWorker.RunWorkerAsync() 之前,您必须禁用 UI,否则可能会使用户感到困惑。
  • @Marc:是的,但是也许您可以只禁用 UI 的某些部分,这对用户来说是一种很好的体验。正如我所写,如果数据很好,您还可以显示加载的前几条记录或文件,并让用户已经使用它们。但很明显,所有这些好东西都会花费开发时间并使事情变得更加复杂。问题是,客户是否愿意为此付费,以及增加项目复杂性是否有意义......
  • 正确。该应用程序已经在运行,虽然有点“慢”。将其更改为一些 MVVM 设计、设计漂亮的微调器控件并再次对其进行测试将花费大量的开发时间。
  • 感谢 WPF 分析工具的链接,我会尽快测试这些。
【解决方案2】:

您可以做的最明显的事情是分析您的应用程序并找到启动时间的瓶颈。听起来最有可能的罪魁祸首是从数据库加载数据。

我学到的一个教训是,如果您使用 ORM,在加载大型数据集时,如果您更喜欢 POCO(普通旧 CLR/C# 对象)而不是 ORM 生成的数据库实体(参见下面的示例),加载时间速度会快很多,RAM 使用量也会显着减少。这样做的原因是 EF 将尝试加载整个实体(即它的所有字段),并可能加载与您的实体相关的全部数据,其中大部分您甚至不需要。您真正需要直接使用实体的唯一一次是在执行插入/更新/删除操作时。读取数据时,您应该获取应用程序需要显示和/或验证的字段。

如果你遵循 MVVM 模式,上述架构并不难实现。

使用 EF 将数据加载到 POCO 的示例:

var query = from entity in context.Entities
                select new EntityPoco
                {
                    ID = entity.ID,
                    Name = entity.Name
                };

return query.ToList();

POCO 是非常简单的类,每个字段都有自动属性。

我们的应用程序中的每个实体通常都有存储库,每个存储库负责获取/更新与该实体相关的数据。视图模型引用了它们需要的存储库,因此它们不直接使用 EF。当用户进行需要持久化的更改时,我们使用存储库中的其他方法,然后仅加载实体的子集(即用户更改的实体)并应用必要的更新 - 一些验证由视图模型完成,可能还有其他验证通过约束/触发器等在数据库中进行。

【讨论】:

  • +1 为您推荐。我确实分析了应用程序。这实际上或多或少是我无法改进的 3s 应用程序(和框架)初始化。其他 3 来自用户控件的创建和数据加载。我还考虑过使用基本的 ADO.NET 数据读取器来提高性能。
  • 您是否有任何指向 MVVM 示例的良好链接,这些示例展示了它们如何处理后台数据加载?他们在加载过程中对视图做了什么?
  • 对不起,我手头没有任何在线示例;这主要是我从同事那里学到的东西。我添加了自己的示例,并详细说明了我的原始答案。我个人可以保证它可以工作,因为我们目前正在处理的应用程序最初需要一分半钟才能加载。现在已降至不到 10 秒。我们在启动时从 SQL Server 数据库读取 300k 到 400k...
【解决方案3】:

这有很多原因。

1) 部署机器的配置可能相当低。
2) 数据绑定不正确或有问题。

可能的解决方案是:
1) 延迟加载数据
2)优化性能。 http://msdn.microsoft.com/en-us/library/aa970683.aspx

我看到应用程序在 wpf 中呈现 5M 条记录不到一秒。

PS:由于列顺序访问,另一个最不可能的原因可能是 30 列。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-22
    • 1970-01-01
    • 2012-12-01
    • 2020-10-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多