2
votes

I have a Silverlight application which interacts with a WCF service. It periodically receives new items to add to a list from this service, and each new element is added to the end of an ObservableCollection (collection.Add() for each new element).

The items themselves don't change once they are received, and the items' class inherits INotifyPropertyChanged, however when I add new items (received from WCF), the DataGrid doesn't update.
I am also using a custom formatter for the DataGrid binding, but I don't think this is a problem as the initial set of items appear correctly (when the ItemsSource is first set).

I would have expected the new elements to appear, as I have confirmed that the ObservableCollection is emitting the correct add event. Since ObservableCollection inherits from INotifyCollectionChanged, shouldn't it update the DataGrid?

The only solution I have found so far is:

dataGrid.ItemsSource = null;
dataGrid.ItemsSource = collection;

Any ideas on how to get it updating? This method blocks the UI for a noticable amount of time.
Thanks

UPDATE: Code

The elements are expanded and extracted in the WCF callback event:

// The ItemWrapper allows the Binding converter to be passed the entire trade object, rather than just each property.
ObservableCollection<ItemWrapper<ExpandedTrade>> pastTrades = new ObservableCollection<ItemWrapper<ExpandedTrade>>();
....

       // Extract and expand data - MinimalTrade is the data sent through WCF
       var convertedTrades = from MinimalTrade t in e.trades
                                  select new ItemWrapper<ExpandedTrade>(
                                      new ExpandedTrade(t,
                                          usernames.ContainsKey(t.UserToId) ? usernames[t.UserToId] : null, potentialWealth != null ? potentialWealth.CurrentWealth : null)); // Get name, otherwise null.
       // Data now expanded (to show full information like usernames
       // pastTrades is an observableCollection
            foreach (var trade in convertedTrades)
            {
                pastTrades.Add(trade);
            }
            OnNewMyTradeHistory(pastTrades);

The OnNewMyTradeHistory event then does this:

if (tradeHistory.ItemsSource == null) tradeHistory.ItemsSource = trades;

This only sets ItemsSource once (to the ObservableCollection) and the add events are firing, but nothing is happening on the UI side.

The WCF callbacks might be happening in another thread.

2
Are you binding to a dependency property? - Phil Gan
This should work. You are going to have to show us some more code. How do you add items to your collection? Can you provide a very trimmed down version of your problem that you can provide in its entirety here? - ColinE
Are you updating the datagrid source on a background thread? I've had problems getting the PropertyChanged event to fire correctly when working with some background threads - Rachel
I tried running the update and databinding in the UI thread (using the UserControl's Dispatcher) but the same thing still happened. - neuropie
INFO: I have found the solution, but Stackoverflow won't let me post an answer until 5 hours later. Will do so then. The solution was to remove the custom Equals() method from the ItemWrapper class. - neuropie

2 Answers

1
votes

I found the solution!

I had implemented the Equals, GetHashCode and ToString methods in both ItemWrapper and ExpandedTrade:

ItemWrapper.cs: (Calls the equivalent methods in the child class)

    public override bool Equals(object obj)
    {
        if(obj is T) return Quote.Equals(obj);
        if (obj is ItemWrapper<T>) return Quote.Equals(((ItemWrapper<T>)obj).Quote);
        return this == obj;
    }
    public override int GetHashCode() { return Quote.GetHashCode(); }
    public override string ToString() { return Quote.ToString(); }

ExpandedTrade.cs:

    public override bool Equals(object obj)
    {
        if (obj == null) return false;
        ExpandedQuote q = obj as ExpandedQuote;
        if (q == null) return false;
        return q.Id == Id;
    }

    public override int GetHashCode() { return Id; }

After removing these methods, it worked. I'd imagine that the DataGrid was testing for equality somewhere, and somehow something was returning an incorrect test. The IDs are unique, but by using the default test of equality by reference, it now works.

0
votes

Tell me if this flow is correct:

  • DataGrid.ItemsSource == null;
  • [Update]
    • Create new observable collection: CollectionA
    • Get items and add them to CollectionA
    • [Event]
      • DataGrid.ItemsSource == null -> ItemsSource = CollectionA
  • [Update]
    • Create new observable collection: CollectionB
    • Get items and add them to CollectionB
    • [Event]
      • DataGrid.ItemsSource != null -> Do Nothing
  • => DataGrid.ItemsSource == CollectionA?

Or is pastTrades a field which is only initialized once? Brackets and method boundaries would help.