Wpf DataVirtualization

14 浏览
0 Comments

Wpf DataVirtualization

我试图将一个非常大的项目列表加载到一个WPF DataGrid中。问题是:加载非常慢。目前我的列表中有大约20,000个项目,需要很长时间(在我最新的版本中大约需要10秒,但这还不够好)。我已经在处理这个问题几天了,但是我找不到真正有效的解决方案。

1)当然启用了UI虚拟化(这不再是问题)

2)我也尝试了一些解决方案,如Bea Stollnitz在这里描述的以及其他一些解决方案。这些解决方案很棒,但对我来说不起作用,因为我的集合必须在运行时进行更新、过滤和排序,而不需要重新加载集合。我找到的解决方案只适用于IList实现...我需要一个可观察的集合来保持我的项目更新。

这就是情况:我有一个包含我的领域数据对象的可观察集合(通过WCF异步更新)。我为列表项包装领域对象创建了一个ViewModel。当我打开视图时,我有一个第二个列表,每个领域对象都将填充一个ViewModel实例。这就是真正的问题。由于“目标”集合绑定到我的DataGrid,我必须将ViewModel的创建调度到UI线程中(否则.NET对来自另一个线程的集合更改并不是很满意)。因此,我正在用20,000个ViewModel创建调用污染调度器队列。创建ViewModel的成本很低,但我仍然在调度器队列中有20,000个调用,同时DataGrid在相同的线程中要求CPU填充自身。

我的想法(还没有完全完成):既然我已经实现了UI虚拟化,我希望在需要它们时才创建ViewModel,而不是在打开视图时。当用户能够看到前20个条目时,我只需要创建20个ViewModel而不是20,000个。这就是我的问题所在:我不知道怎么做。这就是你的作用所在 🙂

我希望有这样的东西(不能像这样工作...只是为了说明我的意思):


    
        
    

它不必是一个转换器,也可以是其他东西或者在代码后台完成。我不在意。我所需要的是:直接绑定到DomainObject集合,动态创建相应的ViewModel并使用刚创建的ViewModel而不是原始对象来填充单个行。有什么想法吗?

0
0 Comments

问题的出现原因:在打开视图时,需要为每个领域对象创建一个ViewModel实例,并将其添加到一个列表中。由于该列表与DataGrid绑定,因此必须将这些ViewModel实例的创建操作调度到UI线程中进行。

解决方法:ViewModel是非UI元素,可以在任何线程中创建。只有将这些ViewModel实例添加到ObservableCollection中的操作需要在UI线程中完成。可以在任何地方创建所有的ViewModel包装器,然后以批的方式将批添加到ObservableCollection中。可以通过给ObservableCollection添加一个扩展方法来解决ObservableCollection只能逐个添加项目的问题。可以在UI线程上调用该扩展方法,然后逐个添加项目。

如果必须获取所有数据,则建议分批获取数据。可以使用属性通知在ViewModel中保持过滤后的集合与源集合的同步。

public static class ObservableCollectionExtensions
{
    public static void AddRange(this ObservableCollection collection, IEnumerable items)
    {
        foreach (var item in items)
        {
            collection.Add(item);
        }
    }
}

如果无法避免在客户端进行过滤和排序,可以考虑将数据发送到服务器进行处理,然后返回适当的数据。

还可以尝试使用BindingList和上述的扩展方法结合使用,以减少在UI线程上的操作次数。

可以将ViewModel的创建操作调度到UI线程,然后使用批处理方式将ViewModel添加到ObservableCollection中,以减少UI线程上的操作次数。同时,可以考虑将数据分批获取,使用属性通知来保持过滤后的集合与源集合的同步,以优化性能。

0
0 Comments

Wpf DataVirtualization这个问题的出现的原因是ObservableCollection的更新过程中会导致UI更新,每个项目都会触发UI更新的事件。解决方法是使用BindingList来替代ObservableCollection,并且在添加一系列项目时禁用事件,然后在完成批量添加后再启用事件。具体的解决方法如下:

BindingList.RaiseListChangedEvents = false;

这将停止UI接收任何事件。然后在批量添加后,可以使用以下代码启用事件:

BindingList.RaiseListChangedEvents = true;
BindingList.ResetBindings();

在WPF中,可以使用BindingList作为ItemsSource来解决这个问题。尽管Microsoft在WPF中支持ObservableCollection,但是使用BindingList也是可以的。

0