WPF UI is not updating on change of ConcurrentBag type collection

2.3k views Asked by At

I have collection which is bounded to datagrid in WPF UI.

My requirement is like i have to update the the value of a property 10 times a second for every item in collection.

So i have taken ConcurrentBag type collection. After updating the value for every item. I am firing RaisePropertyChange explicitly. But UI is not changing.

private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
    if (stockCollection != null)
    {
        stockCollection.ToList().ForEach((s) => s.Price = DateTime.Now.Millisecond);
        Action raiseStockCollectionProperty = new Action(() => RaisePropertyChangedEvent("StockCollection"));
        Dispatcher.BeginInvoke(raiseStockCollectionProperty);
    }
}
2

There are 2 answers

1
Eilistraee On BEST ANSWER

I don't think that the previous answer will provide a solution to your issue. You are in fact not updating the collection at all: You are updating a property of all the Stock instances in the collection. Updating the collection means adding or removing items in it. As the semantics are different, it's possible that the bound control won't check the properties of already existing objects in the collection when you fire a CollectionChanged event. It would be a sensible optimization.

I would suggest you to implement INotifyPropertyChange in your Stock class, and to fire the propertyChanged event here (on the good thread however, using Dispatcher) in the Price property setter.

private DateTime _price;
public DateTime Price
{
   get
   {
     return _price;
   }
   set
   {
     if(_price!=value)
     {
       _price = value;
       RaisePropertyChanged("Price");
     }
   }
}
protected void RaisePropertyChanged(string property)
{
   var propertyChanged = PropertyChanged;
   if(propertyChanged!=null)
   {
     propertyChanged(this,new PropertyChangedEventArgs(property));

   }
}
1
ken2k On

EDIT: I misread your question that is in fact about a property update, not a collection update. So if you actually wanted to update the Price property all of the items of your collection, then Where clause of the example below won't help you of course.


You're actually not modifying your collection ;)

stockCollection.ToList().ForEach((s) => s.Price = DateTime.Now.Millisecond);

You might want to do:

stockCollection = new ConcurrentBag(stockCollection.Where(...));

EDIT:

Means, I need to create a new collection object everytime?

As your collection does not implement INotifyCollectionChanged nor INotifyPropertyChanged, yes. I would suggest using ObservableCollection instead of your current collection type if possible. ObservableCollection is able to notify of the update of an item property, as well as raise an event when an item is added/deleted.

ObservableCollection<YourType> myCollection = new ObservableCollection<YourType>();
...

public ObservableCollection<YourType> MyCollection
{
    get
    {
        return this.myCollection;
    }

    set
    {
        if (value != this.myCollection)
        {
            this.myCollection = value;
            this.RaisePropertyChanged("MyCollection");
        }
    }
}
...
// Following lines of code will update the UI because of INotifyCollectionChanged implementation
this.MyCollection.Remove(...)
this.MyCollection.Add(...)

// Following line of code also updates the UI cause of RaisePropertyChanged
this.MyCollection = new ObservableCollection<YourType>(this.MyCollection.Where(z => z.Price == 45));