0

See EDIT section at the bottom...

In a WPF / MVVM-based application, I have a ViewModel that contains an ObservableCollection of objects. Each of these objects has an enum "Status" member that implements INotifyPropertyChanged.

In the View, I need to display the count of objects in the collection that have a particular "Status". Since each object's status can be updated dynamically, the View needs to reflect those dynamic changes in the displayed values.

Currently, I have a ViewModel property that returns the count of each status (one property for each status). For instance:

public int TranslatedWithErrorsCount
{
    get { return FileCollection.Count(n => n.Status == TranslateStatus.TranslatedWithErrors); }
}

However, since the value of each count property is just a by-product of the current state of the collection (and therefore I don't ever set it), it's not clear to me how to best trigger the chain of events that starts with the change of an object's status and ends with the displayed count values updating dynamically.


* EDIT *

At the time of posting, I already had a working solution. I didn't want to muddy the waters with what I had already done as I assumed there was probably a better way. Anyway, based on the current responses, maybe my existing solution is more acceptable than I expected.

Currently, I:

  1. Subscribe to my collection's CollectionChanged event and in the event handler, I manually fire PropertyChanged events on my "Count" properties.
  2. I based the collection itself on "TrulyObservableCollection" instead of "ObservableCollection" in order to catch individual Status change events in the underlying objects.

This all seems to work as expected, but I assumed there might be a cleaner way.

Community
  • 1
  • 1
Jeff Godfrey
  • 718
  • 8
  • 18

2 Answers2

2

Attach an eventhandler to FileCollection.CollectionChanged

In EventHandler call RaisePropertyChanged("TranslatedWithErrorsCount") on your ViewModel.

If you want to check if Status property changes... You would have to attach and detach handlers from PropertyChanged of items in CollectionChandged handler. Easiest way to store such handlers is dictionary.

EDIT: Actually that is quite fine way of dealing with those: https://stackoverflow.com/a/8490996/1749204

Community
  • 1
  • 1
Arek Bal
  • 712
  • 8
  • 13
  • What if only the `Status` property of one or more elements in the collection changes, but not the collection itself? – Clemens Mar 12 '14 at 14:36
  • Edited to take such case into account. Do you need more help in implementing this? I might show you how if necesarry. – Arek Bal Mar 12 '14 at 14:40
  • Thanks for the input. Actually, your suggestion is exactly my current solution (I didn't want to muddy the waters with what I've already done as it seemed a bit brute-force'ish). Specifically, I've done a combination of 1) firing my property change events manually in the CollectionChanged event handler and 2) basing my collection on "TrulyObservableCollection" instead of "ObservableCollection" to catch the individual Status change events. While it seems to work as expected, I assumed there might be a more elegant way... – Jeff Godfrey Mar 12 '14 at 14:49
1

You could hook up the CollectionChanged on your ObservableCollection and in its event handler hook up changes of the Status (Probably wont compile, but you get the idea):

private void OnPropertyChanged(object sender, NotifyPropertyChangedEventArgs e)
{
    PropertyChanged("TranslatedWithErrorsCount");
}


FileCollection.CollectionChanged += (sender, e) => 
{
    PropertyChanged("TranslatedWithErrorsCount"); 

    if (e.NewItems != null)
    {
        foreach (var item in e.NewItems)
        {
            var inpc = item as INotifyPropertyChanged;

            inpc.PropertyChanged += OnPropertyChanged;
        }
    }

    if (e.OldItems != null)
    {
        foreach (var item in e.OldItems)
        {
            var inpc = item as INotifyPropertyChanged;

            inpc.PropertyChanged += (sender, e) => OnPropertyChanged;
        }
    }
}
Håkan Fahlstedt
  • 2,040
  • 13
  • 17