Derek Lakin

Change Notification for Composite Properties

I was working on Titan the other night to implement the functionality that enables users to create new folders in their configured services. Having implemented the core functionality, hooked it all up and started doing some testing I noticed that the new folder was not being displayed in the user interface.

Initially, this was particularly puzzling because the user interface is a ListView that binds to a a property on a business object that supports change notification by using the INotifyPropertyChanged interface. The following diagram shows a simplified view of the relevant classes:


The Folder class represents a folder on a service. The VirtualFolder class represents the merged result of two configured services, and so it contains a reference to two Folder instances. It also contains a list of VirtualFile instances (which are the merged result of the files on the two configured services) in the Files property and a lost of VirtualFolder instances, which are the folders that this folder contains; a recursive relationship.

The other two properties: Contents and Hierarchy, are what (in the context of this post) I call composite properties. They only provide a property getter, and the results are the combination of something else. For example, the following code example shows the Contents property, which is a combination of the Files and Folders properties.

public ObservableCollectionIVirtualFilesAndFolders> Contents { get { ObservableCollectionIVirtualFilesAndFolders> contents = new ObservableCollectionIVirtualFilesAndFolders>(); foreach (VirtualFolder folder in this.Folders) { contents.Add(folder as IVirtualFilesAndFolders); } foreach (VirtualFile file in this.Files) { contents.Add(file as IVirtualFilesAndFolders); } return contents; } }

So, the ListView in question binds to the Contents property of the current VirtualFolder. When the CreateFolder operation completes, it adds the new VirtualFolder to the Folders collection of the current VirtualFolder and hey presto, the user interface gets updated … or more precisely, with the above code, nothing happens on the user interface. But why?

The problem lies in the fact that the Contents property only provides a property getter. The implementation for the change notification for other properties uses a method to raise the PropertyChanged event in the property setter, so we need to ensure that the PropertyChanged event gets raised for any composite properties when their underlying sources get changed.

In this instance, the Files and Folders properties are ObservableCollections, so in the constructor for the VirtualFolder and in the property setters for the Files and Folders properties I subscribe to the CollectionChanged event and raise the PropertyChanged event accordingly, as shown in the following code example.

public class VirtualFolder: IVirtualFilesAndFolders, INotifyPropertyChanged { private VirtualFileCollection files = new VirtualFileCollection(); private VirtualFolderCollection folders = new VirtualFolderCollection(); public VirtualFolder() { this.files.CollectionChanged += new NotifyCollectionChangedEventHandler(FilesOrFoldersCollectionChanged); this.folders.CollectionChanged += new NotifyCollectionChangedEventHandler(FilesOrFoldersCollectionChanged); }

public VirtualFileCollection Files { get { return this.files; } set { this.files.CollectionChanged -= FilesOrFoldersCollectionChanged; UpdateProperty("Files", ref this.files, value); this.files.CollectionChanged += new NotifyCollectionChangedEventHandler(FilesOrFoldersCollectionChanged); Notify("Contents"); Notify("Hierarchy"); } }

public VirtualFolderCollection Folders { get { return this.folders; } set { this.folders.CollectionChanged -= FilesOrFoldersCollectionChanged; UpdateProperty("Folders", ref this.folders, value); this.folders.CollectionChanged += new NotifyCollectionChangedEventHandler(FilesOrFoldersCollectionChanged); Notify("Contents"); Notify("Hierarchy"); } }

So, every time the Files or Folders collections are updated, change notification is activated for the Contents (and Hierarchy) properties, the WPF data binding engine knows to update the binding, and the user interface gets updated. Job done!

kick it on