2014-10-10 65 views
1

我一直在与WPFObservableCollection作战很长一段时间,现在我需要一些帮助。如何在使用ObservableCollection时停止DataGrid闪烁?

这是

  • 的UI必须能够显示日志的设置和要求

    • 使用的MongoDB为依托数据库
    • 在数据库中存储的日志数据的海量他们插入到数据库中(实时ish)
    • UI DataGrid绑定到ObservableCollection
    • UI DataGrid由于内存使用并且具有数百万条记录,所以是并且必须被分页
    • 查看日志时,如果创建并显示新条目,则必须删除一条;这仅仅是因为寻呼和具有一组页面大小(50个日志条目将永远只能在同一时间显示)
    • 使用ActiveMQ的时候一个新的日志条目已创建

    所以基本上这是信号滚动日志视图,一个入口进入,一个入口出去。我必须保持所选项目并实时显示更新(这就是为什么我使用ObservableCollection)。日志可以被过滤并且页面可以被导航,所以我认为当有新的日志条目并且显示它时最容易获取整个页面;这也可以让我得到以前可能错过的参赛作品。

    我遇到过很多问题,包括在后台线程上更新集合时遇到的问题,最近创建新条目时出现DataGrid“闪烁”问题。我假设闪烁是由于每次刷新整个集合而引起的,但替代方法(手动添加和删除项目)要复杂得多,并且由于筛选和分页的复杂性,我宁愿不去该路由。

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

    的XAML

    <DataGrid ItemsSource="{Binding Entries, IsAsync=True}" 
         AutoGenerateColumns="False" IsReadOnly="True" 
         CanUserSortColumns="False" 
         EnableColumnVirtualization="True" EnableRowVirtualization="True" 
         VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling"> 
        <DataGrid.Columns> 
         <DataGridTextColumn Header="Timestamp" Binding="{Binding Timestamp}" /> 
         <DataGridTextColumn Header="From" Binding="{Binding MessageFrom}" /> 
         <DataGridTextColumn Header="To" Binding="{Binding MessageTo}" /> 
         <DataGridTextColumn Header="Type" Binding="{Binding MessageType}" /> 
        </DataGrid.Columns> 
    </DataGrid> 
    

    的结合,我已经当我想起更多的事情我已经试过用

    //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; } 
    

    视图模型的代码,我将它张贴。

  • +0

    顺便说一句,因为内置的UI虚拟化,在WPF中可能没有必要使用分页或其他任何非常冒险的技术。 2 - 如果重新绑定整个系列,无论如何都会造成性能损失,请改用单个项目。 – 2014-10-10 20:13:22

    +0

    @HighCore这是不正确的。如果我从数据库中获取所有600万条记录而不使用查询来限制返回到50条记录的数量,那么客户端的内存用得非常快。即使WPF具有虚拟化功能,它仍然拥有内存中的600万条记录列表;它只是没有在同一时间显示。 – vane 2014-10-10 20:22:07

    +0

    是的,但是这与UI没有任何关系......如果在控制台应用程序中从数据库中获取6m记录,则会出现同样的问题。我的建议仍然是你应该操纵单个项目,而不是一直重新绑定整个系列。 – 2014-10-10 20:25:09

    回答

    0

    这就是我想到的,似乎工作得很好,虽然我不喜欢它,并认为我可能倾向于Lee O在上述评论中的建议;但在此期限即将到来的同时,这也是我所做的。 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); 
    }