views:

118

answers:

2

I have an ObservableCollection bound to a WPFToolkit DataGrid in an MVVM pattern. Every 30 seconds I have a background thread that is querying a service for some data. When it returns, it marshals it to the UI thread and updates the ObservableCollection by first calling Clear() on the collection and then calling Add() for each item. When this happens, the data is updated properly to the screen, however, the datagrid flickers. How can I prevent this from happening?

A: 

I created a simple but maximum powerfull solution for your problem:

public class MyCollection<T> : ObservableCollection<T>
{
    private bool _isInEditMode = false;

    public void BeginEdit()
    {
        _isInEditMode = true;
    }

    public void CommitEdit()
    {
        _isInEditMode = false;
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (_isInEditMode == false)
        {
            base.OnCollectionChanged(e);
        }
    }
}

The my collection class prevents collection changed events while you are in edit mode. Start "BeginEdit" before clearing the items. Then add your new items. When you are finished, use "CommitEdit" and the view will refresh only once.

If have tested it with a simple listbox, where i add 1.000.000 string items. Try this out. Its funny :)

 private void Button_Click(object sender, RoutedEventArgs e)
    {
        MyCollection<string> list = testBox.ItemsSource as MyCollection<string>;
        //list.BeginEdit();
        for (int i = 0; i < 1000000; i++)
        {
            list.Add("test " + i);
        }
        list.CommitEdit();

    }

Remove the // from list.BeginEdit() and see the difference. Its about 15 seconds agains < 1 second.

Greetings,

Jan

JanW
Sorry Jan, this did not solve the problem. The flicker was still there. Fortunately, simply binding to a new collection each time solves the issue.
Chris Holmes
Ah okay thats a pity that it does not work for datagrids. Did not test it. With other ItemsControls it works great. Sure the solution with a new list is much more simple, if you always replace the whole list.
JanW
I created a similar extension to observable collection to add an AddRange method like the other collections have. I've never noticed a flicker from it, but i'm only adding batches of items, never doing a full clear...
John Gardner
+1  A: 

It appears you're simply replacing all of your data if you're clearing then adding each item one at a time. Instead of re-using your ObservableCollection, can you simply set your data grid's itemssource to a new ObservableCollection with your new items?

Scott
That was it. I thought reusing the collection was the proper way to avoid the flicker, but it turns out that your suggestion is the right way! Thanks!
Chris Holmes
No problem. The flicker I believe was caused when you were clearing the ObservableCollection... So the DataGrid went from full of old data, to empty (the flicker), then refilled when you added the new items. Now, you simply go from filled with old data, to filled with new data... no empty state in between.
Scott
I hate when the technique I use to avoid a problem is the technique that is causing the problem.
Robert Rossney