Search code examples
c#windows-phone-7bindingmvvm-light

RaisePropertyChanged doesn't work for collections


I'm using mvvm-light and I noticed this strange behavior about the RaisePropertyChanged.

xaml:

<ListBox ItemsSource="{Binding Collection}"/>
<TextBlock Text="{Binding Text}"/>

Observable class:

public class A : ObservableObject
{
    private string _b;
    public string B
    {
        get { return this._b; }
        set
        {
            this._b = value;
            this.RaisePropertyChanged("B");
        }
    }
}

vm:

public MainViewModel(IDataService dataService) { this.Collection = new List<A>(...); }

public RelayCommand Command1
{
    get
    {
        return this._command1 ?? (this._command1= new RelayCommand(() =>
        {
            this.Collection.Add(new A());
            this.Collection[2].B = "updated";
            this.RaisePropertyChanged("Collection");
            this.RaisePropertyChanged("Text");
        }));
    }
}

public RelayCommand Command2
{
    get { return this._command2?? (this._command2 = new RelayCommand(() => { this.Text++; })); }
}

public List<A> Collection { get; set; }
public int Text { get; set; }

So, RaisePropertyChanged("Collection") doesn't update the binding while RaisePropertyChanged("Text") do. I can see it by executing the Command2 several times and the Command1 after that. If the Collection is an ObservableCollection then new element shows in a view, but updated item isn't, which means an internal mechanism of an ObservableCollection works, but not the RaisePropertyChanged.


Solution

  • First, an explanation of the issue:

    On Windows Phone, when setting a value for a dependency property, the framework internally check if the new value is different from the old one (for optimization purpose maybe). When you raise the PropertyChanged event or directly re-assign your collection to the ItemsSource property (which is just a wrapper around the ItemsControl.ItemsSourceProperty dependency property), the framework detects that the value actually didn't change and doesn't update the property. Therefore, the ListBox is never notified of your changes, and isn't updated.

    The ObservableCollection works because it uses a whole different mechanism: the ListBox directly subscribes to the CollectionChanged event of your collection, and thus isn't hindered by the limitations of the dependency properties.


    Now, how to get around this limitation? The only workarounds I can think of are:

    1. Use an ObservableCollection instead of a List
    2. Assign null to the ItemsSource property of your ListBox, then re-assign your collection
    3. Bind the ListBox to a property that will return a different collection every time it's called:

      public List<A> CollectionCopy
      {
          get
          {
              return this.Collection.ToList();
          }
      }