Using a Behavior to Aggregate Values in a Master-Detail view in M-V-VM
It is very difficult to handle situations in a master-detail view, where the master side displays a value derived from aggregating the detail collection. The Model-View-ViewModel pattern makes this even more challenging, since most means of handling this require changing your model, or using extensive code behind.
For example, suppose you have a list of SalesPerson classes, each of which had a list of Sales, and you wanted to display this in a ListView using a GridView. It would be common to want to display each SalesPerson’s total sales in the grid, along with their name and other information.
Typical approaches to this rely on handling the data in the code behind. This is difficult to handle correctly, especially if you want edits in the details section (Sales) to be reflected in the master section in real time.
If, however, the Sales class implements INotifyPropertyChanged, and the list of Sales implements INotifyCollectionChanged or is a BindingList, there is another option.
By taking advantage of INotifyPropertyChanged, you can bind to each element within the collection, and be updated when individual elements change. INotifyCollectionChanged can be used to track additions or removals from the child collection. Wrapping this in a class makes tracking changes simple, if you’re just looking at a single SalesPerson at a time.
However, introducing the GridView makes this much more challenging. Now, you have a CellTemplate for each grid cell which would normally bind to one property of SalesPerson. Since you’re defining this in a CellTemplate, it’s difficult to setup a custom class and handle binding issues, and life becomes challenging.
The first approach I considered was to use an IValueConverter. This allows you to bind a TextBlock directly to your collection, providing a converter that would aggregate the values. This works for initially displaying the values, but doesn’t update if the user changes a value in the details section. The cell in the master’s grid doesn’t get updated.
Finally, I realized that I could make a custom behavior, which contained the wrapper class that wraps around the collection. The Behavior itself can bind to the collection, and handle the aggregation. In order to make this work properly, I also had to make the behavior capable of binding to the associated object’s property; in this case, the Text property of the TextBlock I was using.
By hooking this all together, I was able to make a Master-Detail work, and update in real time:
Starting with the above picture, I can now type values into the Details section, and everything updates in real-time:
There is a bit too much code to easily fit into this post, so I’ve uploaded the entire solution, fully commented, as a sample on Microsoft Expression Gallery : CollectionAggregator. All of the code is available on the Gallery for download.