UI 线程在加载和更新 RadGridView 对象时停止

发布于 2024-11-27 09:03:57 字数 766 浏览 0 评论 0原文

下午,

我目前正在分析一个相当大的 C# WPF 应用程序,试图消除一些性能问题。其中一种涉及切换到特定(相当大)的 UserControl 时发生的 5-8 秒停顿。我已将范围缩小到此 UserControl 中包含的 RadGridView,该控件需要很长时间才能加载和更新自身,从而导致 UI 线程停顿并使整个应用程序无响应。显然,如果可能的话,我们希望消除这种停顿。

我尝试去掉网格上的任何自定义样式和 DataTriggers,但是虽然这总体上减少了 UI 线程的负载,但停顿仍然存在,似乎没有减少。通过 ANTS Profiler,似乎主要归咎于网格的测量和布局,以及 XAML 模板的一些加载。由于没有网格行,用户控件的加载速度明显加快,并且似乎仅添加少量行就足以导致这种停顿。网格为行和列启用了虚拟化,但这似乎没有帮助。检查后,调用图非常深,似乎是引发更新通知、更新布局、加载 XAML 以及最重要的是测量子 FrameworkElement 的调用才是罪魁祸首。

对于几个潜在的解决方案,我正在考虑将 UserControl 保留在内存中但隐藏以减少切换到它的成本,或者在 UserControl 加载后,可能会增量地填充网格。前者可能需要做很多工作,因为控件订阅了许多事物,需要根据需要连接、断开和重新连接。后者可能还涉及相当多的工作,但可能是一个更好的解决方案,因为这样至少我们可以尝试自己减轻停顿,或者至少在发生停顿时警告用户。

如果问题仍然存在,我们可能会要求 Telerik 看一下,但我想我应该先在这里问,以防有人以前遇到过这样的问题(甚至不一定是 RadGridView)并找到了解决方案一些描述。

干杯。

Afternoon all,

I'm currently profiling a rather large C# WPF app, trying to eliminate a few performance issues. One involves a 5-8 second stall that occurs when switching to a particular (rather large) UserControl. I've narrowed this down to a RadGridView contained in this UserControl that's taking a long time to load and update itself, stalling the UI thread and making the entire application unresponsive. Obviously, we'd like to eliminate this stall if possible.

I have tried stripping away any custom styles and DataTriggers on the grid, but while this acted to reduce load on the UI thread in general, the stall still remained, seemingly undiminished. Through ANTS Profiler, it seems that the measuring and layout of the grid is mostly to blame, along with some loading of XAML templates. With no grid rows, the UserControls loads significantly faster, and it seems that adding just a small number of rows is enough to bring about this stall. The grid has virtualization enabled for rows and columns, but this doesn't seem to help. The call graph is tremendously deep when examined, and it seems to be calls that raise update notifications, update layout, load XAML and, above all, measure child FrameworkElements that are to blame.

For a couple of potential solutions, I'm thinking about keeping the UserControl in memory but hidden to reduce the costs of switching to it, or populating the grid, perhaps incrementally, well after the UserControl has loaded. There might be a lot of work with the former, as the control subscribes to a number of things, which would need to be connected to, disconnected from and reconnected to as appropriate. The latter might also involve a fair bit of work, but might be a better solution, because then at least we could try to mitigate the stalling ourselves, or at least warn the user when it was occurring.

If the problem persists, we're likely to ask Telerik to have a look at it, but I thought I'd ask here first in case anyone has encoutered such an issue before (not necessarily with RadGridView, even) and found a solution of some description.

Cheers.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

风苍溪 2024-12-04 09:03:57

问题是您需要一个正确实现 ICollectionChanged 的​​集合。

将所有记录添加到源集合后尝试此操作(每次添加后不应引发任何 CollectionChanged 事件),它有效:

public void AddRange(List<TValue> values, int startingIndex)
{
    // add all the items to your internal list, but avoid raising events.

    // now raise CollectionChanged with an IList type.
    CollectionChanged?.Invoke(collection, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, addedValues, startingIndex));
}

注意我们传递的是 IList,而不是 TValue< /代码>。当它看到一个列表时,它会在处理之前将它们全部添加。 startingValue 通常是开始添加项目之前列表的 .Count。

步骤

  1. 对尚未实现 INotifyCollectionChanged 的​​集合类型进行子类化。那里有很多开源示例。
  2. 添加上面的方法并在添加项目后使用它。
  3. 我的 RadGridView 与 QueryableCollectionView 绑定,该视图使用此列表作为其 SourceCollection。我不知道你是否可以直接绑定这个列表。

RadGridView 的一个奇怪行为是,如果您以这种方式添加一百万条记录,速度会很快,但所有新行都将为空白,您可以看到 RadGridView 慢慢地用来自的数据填充它们最上面的新项目下来。我的解决方案是绑定 QueryableCollection 并将其 PageSize 设置得足够小,这样用户就看不到这一点。

The problem is you need a collection that implements ICollectionChanged properly.

Try this after adding all records to your source collection (one which should NOT raise any CollectionChanged events after each add), it works:

public void AddRange(List<TValue> values, int startingIndex)
{
    // add all the items to your internal list, but avoid raising events.

    // now raise CollectionChanged with an IList type.
    CollectionChanged?.Invoke(collection, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, addedValues, startingIndex));
}

Notice we're passing IList<TValue>, NOT TValue. When it sees a list, it adds them ALL before processing. The startingValue is usually the .Count of the list before you started adding items.

Steps:

  1. Subclass a collection type that doesn't already implement INotifyCollectionChanged. Plenty of open source examples are out there.
  2. Add the method above and use it after you've added your items.
  3. My RadGridView is binding against a QueryableCollectionView which uses this list as its SourceCollection. I don't know if you can bind against this list directly.

One strange behavior with RadGridView is that if you add say, a million records this way, it WILL be quick, but all the new rows will be blank, and you can watch RadGridView slowly populate them with data from the topmost new item down. My solution to this is to bind against QueryableCollection and set its PageSize sufficiently small so the users don't see this.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文