In the previous post I demonstrated use of the HierarchicalDataTemplate to style nodes based on the type of the object that they are bound to. That declarative recursion gets you true separation between data and presentation.
In this post, I will show how I made use of the Composite Pattern, INotifyPropertyChanged and BindingSource<T> to implement objects for DataBinding with the TreeView.
In the next post I will show how I used LINQ to XML to read the data from an XML file.
The Composite Pattern
In the example at hand, HierarchicalDataTemplate is able to apply styles recursively because the type with which it is associated has a property which is a collection of objects of types for which a HierarchicalDataTemplate is defined. That collection can then be bound to the ItemsSource property of the template.
This works particularly well when the objects implement the Composite Pattern. Using the pattern, you can construct trees of objects with any hierarchy because all objects can be stored in collections of the base type, Composite.
As the example shows, you can set the HierarchicalDataTemplate’s ItemsSource to the Children property which is a collection of Composite objects and then provide a HierarchicalDataTemplate for the specific type.
Implementing the DataBinding
As you know, in order to get the Source to update the Target in a databinding expression, the Source needs to implement some change notification interface.
One way to do this is to make the Source property into a DependencyProperty. A disadvantage of this approach is that it ties your business layer into the System.Windows namespace, which is rarely a good idea. A more lightweight approach is to implement the INotifyPropertyChanged interface on the Source type.
INotifyPropertyChanged defines one event that you are expected to raise whenever the value of one of the object’s properties changes. The name of the property whose value changed is provided as an argument of the event invocation. It is the responsibility of the class implementor to make sure that for every property (for which databinding should be implemented) a change in value generates the event. (This is usually done in the ‘set’ handler of the property).
We are not done yet, however.
In our example we need to bind nodes of TreeView to a collection. For this we also need a notification when objects are added or removed from the collection.
There are two standard ways to do this.
The first, legacy approach, is to use collections that implement IBindingList. IBindingList has a ListChanged event and is analogous to INotifyPropertyChanged – for collections. This sounds a little complex, but the good news is that you don’t have to implement IBindingList yourself, you can just use the generic BindingList<T> type to represent your collections of T. BindingList is implemented in the System.Component namespace, and is in many ways an IList<T> with an implementation of IBindingList.
The second approach is to use the ObservableCollection<T> class that was introduced by WPF in the System.Collections.ObjectModel namespace. You can read a discussion about the difference between the two approaches here.
In this example, however, I will use the BindingList because it doesnt require the business layer to reference the WPF assembly, WindowsBase.
So, this is the Composite class I defined for use with the TreeView from the previous post:
namespace OrganizationData
{
abstract public class Composite : INotifyPropertyChanged
{
#region Properties
string name;
public string Name
{
get { return name; }
set
{
name = value;
RaisePropertyChanged("Name");
}
}
BindingList<Composite> children;
public BindingList<Composite> Children
{
get { return children; }
set
{
children = value;
RaisePropertyChanged("Children");
}
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
And here are few business objects, derived from Composite. A Department has an additional Budget property.
namespace OrganizationData
{
public class Department : Composite
{
#region Properties
double budget;
public double Budget
{
get { return budget; }
set
{
budget = value;
RaisePropertyChanged("Budget");
}
}
#endregion
}
}
and an Employee has a Role property and a Salary property.
namespace OrganizationData
{
public class Employee : Composite
{
#region Properties
string role;
public string Role
{
get { return role; }
set
{
role = value;
RaisePropertyChanged("Role");
}
}
double salary;
public double Salary
{
get { return salary; }
set
{
salary = value;
RaisePropertyChanged("Salary");
}
}
#endregion
}
}
In the next post I will show how I used LINQ to XML to such objects from an XML file.