Wpf 如何在使用ObservableCollection时阻止DataGrid闪烁?

Wpf 如何在使用ObservableCollection时阻止DataGrid闪烁?,wpf,observablecollection,Wpf,Observablecollection,我已经和WPF和ObservableCollection斗争了很长一段时间,现在我需要一些帮助 这是设置和要求 使用MongoDB作为后台数据库 在数据库中存储大量日志数据 UI必须能够在日志插入数据库时实时显示日志 UI DataGrid绑定到ObservableCollection 由于内存使用和数百万条记录,UI DataGrid是且必须分页的 查看日志时,如果创建并显示了一个新条目,则还必须删除一个条目;这仅仅是因为分页和设置页面大小的缘故,一次只能显示50个日志条目 使用ActiveM

我已经和WPF和ObservableCollection斗争了很长一段时间,现在我需要一些帮助

这是设置和要求

使用MongoDB作为后台数据库 在数据库中存储大量日志数据 UI必须能够在日志插入数据库时实时显示日志 UI DataGrid绑定到ObservableCollection 由于内存使用和数百万条记录,UI DataGrid是且必须分页的 查看日志时,如果创建并显示了一个新条目,则还必须删除一个条目;这仅仅是因为分页和设置页面大小的缘故,一次只能显示50个日志条目 使用ActiveMQ在创建新日志项时发出信号 所以本质上这是一个滚动日志视图,其中一个条目进入,一个条目退出。我必须维护所选项目并实时显示更新,这就是我使用ObservableCollection的原因。日志可以被过滤,页面可以被导航,所以我认为当有新的日志条目时,获取整个页面并显示它是最容易的;这也将允许我获取以前可能遗漏的条目

我在这方面遇到了很多问题,包括在后台线程上更新集合的问题,最近的一个问题是DataGrid在创建新条目时闪烁。我假设每次刷新整个集合都会导致闪烁,但是手动添加和删除项的替代方法要复杂得多,而且由于过滤和分页的复杂性,我不想这样做

有没有什么好的模式或建议来阻止这种闪烁,或者有没有更好的方法来实现实时更新的可过滤日志视图

Xaml

我使用的绑定的ViewModel代码

//option 1
public ViewModel() { this.Entries = new ObservableCollection<Model>(); }

public void UpdateData()
{
    this.Entries.Clear();
    foreach (Model m in FetchModels())
        this.Entries.Add(m);
}

public ObversableCollection<Model> Entries { get; private set; }

//option 2
private List<Model> m_Entries = new List<Model>();

public void UpdateData()
{
    this.m_Entries.Clear();
    this.m_Entries.AddRange(FetchModels());
    this.NotifyPropertyChanged(() => this.Entries);
}

public ObversableCollection<Model> Entries { get { return new ObservableCollection<Model>(this.m_Entries); } }

//option 3
public ViewModel() { this.Entries = new ObservableCollection<Model>(); }

public void UpdateData()
{
    var tmp = this.Entries;
    this.Entries = null;

    tmp.Clear();
    foreach (Model m in FetchModels())
        tmp.Add(m);

    this.Entries = tmp;
    this.NotifyPropertyChanged(() => this.Entries);
}

public ObversableCollection<Model> Entries { get; private set; }

只要我记得我尝试过的更多东西,我就会发布它。

这是我想到的,似乎效果很好,尽管我不喜欢它,并且认为我可能会倾向于上面评论中李奥的建议;但在这段时间里,由于最后期限即将到来,我所做的就是这样。UpdateData在它自己的线程上

public void UpdateData()
{
    var items = this.m_Cache.All<Model>().OrderByDescending(x => x.Timestamp).Take(50).Skip(0).ToList();

    lock (this.m_Sync)
    {
        var toRemove = this.Entries.Except(items).ToList();
        var toAdd = items.Except(this.Entries).ToList();

        foreach (Model m in toAdd)
            this.Entries.Add(m);

        foreach (Model m in toRemove)
            this.Entries.Remove(m);
    }

    this.NotifyOfPropertyChange(() => this.TotalRecords);
}

顺便说一句,由于内置的UI虚拟化,WPF中可能不需要分页或任何其他相当粗糙的技术。2-如果你重新绑定整个集合,无论怎样都会有性能损失,请改为操作单个项。@HighCore不是这样的。如果我从数据库中抓取所有600万条记录,而不使用查询将返回的数量限制为50条记录,那么客户端的内存会很快用完。即使WPF实现了虚拟化,它的内存中仍然有600万条记录;它只是没有同时显示所有内容。是的,但是这与UI没有任何关系。。。如果您在控制台应用程序中从数据库中抓取600万条记录,您将遇到同样的问题。我的建议仍然是,你应该操作单个项目,而不是一直重新绑定整个集合。@HighCore顺便说一句,我最初没有发布代码,因为该项目是一个敏感项目,我无法发布代码。我所发布的用来安抚反对票的上帝的代码不是项目中的实际代码,而是尽可能接近的代码,而不会遇到麻烦。我实现了这个的一个变体,它工作得很好。
public void UpdateData()
{
    var items = this.m_Cache.All<Model>().OrderByDescending(x => x.Timestamp).Take(50).Skip(0).ToList();

    lock (this.m_Sync)
    {
        var toRemove = this.Entries.Except(items).ToList();
        var toAdd = items.Except(this.Entries).ToList();

        foreach (Model m in toAdd)
            this.Entries.Add(m);

        foreach (Model m in toRemove)
            this.Entries.Remove(m);
    }

    this.NotifyOfPropertyChange(() => this.TotalRecords);
}