WPF TreeView HierarchicalDataTemplate - binding to object with different child collections
I am trying to bind a collection to wpf TreeView
control using data templates. Each item(Person) in the collection also contains two different collections(Cars, Books) of type car and book.
Here's simplified list of the objects involved to save space.
public class Person
{
public string Name
public List<Book> Books;
public List<Car> Cars;
}
public class Book
{
public string Title
public string Author
}
public class Car
{
public string Manufacturer;
public string Model;
}
Here is how I am binding
public MainWindow()
{
InitializeComponent();
this.treeView1.ItemsSource = this.PersonList();
}
public List<Person> PersonList()
{
List<Person> list = new List<Person>();
Book eco = new Book { Title = "Economics 101", Author = "Adam Smith"};
Book design = new Book { Title = "Web Design", Author = "Robins" };
Car corola = new Car { Manufacturer = "Toyota", Model = "2005 Corola"};
Car ford = new Car { Manufacturer = "Ford", Model = "2008 Focus"};
Person john = new Person { Name = "John", Books = new ObservableCollection<Book> { eco, design }, Cars = new ObservableCollection<Car> { corola } };
Person smith = new Person { Name = "Smith", Books = new ObservableCollection<Book开发者_如何转开发> { eco, design }, Cars = new ObservableCollection<Car> { ford } };
list.AddRange(new[] {john, smith });
return list;
}
Here is the Xaml code
<Grid>
<TreeView Name="treeView1">
</TreeView>
</Grid>
I am looking to see the tree display to look like this.
>John
>Books
Economics 101 : Adam Smith
Web Design : Robins
>Cars
Totota : 2005 Corola
>Smith
>Books
Economics 101 : Adam Smith
Web Design : Robins
>Cars
Ford: 2008 Focus
this sign >
is used to show the tree folder and should not be considered in the template.
It is a bit complicated since your tree has two different child collections. WPF does not support a scenario with multiple ItemsSource
definitions. Therefore you need to combine those collection into a CompositeCollection. The type matching of the composite elements (i.e. Car, Book) will be done automatically.
In XAML you need to define so-called HierarchicalDataTemplates that match your type definitions. If local
points to the namepace where Book
, Car
and Person
are defined, the simplified HierarchicalDataTemplates
could look like this:
<HierarchicalDataTemplate DataType="{x:Type local:Person}"
ItemsSource="{Binding CompositeChildren}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Book}">
<TextBlock Text="{Binding Path=Title}" />
<!-- ... -->
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Car}">
<TextBlock Text="{Binding Path=Model}" />
<!-- ... -->
</HierarchicalDataTemplate>
Then you need to hook up your collection to the tree control. There are a few possibilities to do this, the easiest would be to define a property in your Window
class and define a Binding:
<TreeView Items={Binding ElementName=myWindow, Path=Persons}/>
This should point you into the right direction, but don't take my code as compile ready :-)
CompositeCollection
is a good answer, except you can't bind the child collections to the DataContext
because it's not a Freezable
. You can only bind them to resources. (See this question for more on that.) That makes it kind of hard to use in a HierarchicalDataTemplate
, since the ItemsSource
in one needs to bind to a property in the current context to be useful.
If you don't need change notification on the collections, you can easily implement a property in your view model, e.g.:
public IEnumerable<object> Items
{
get { return Books.Concat(Cars); }
}
If you need change notification on the collection, it's a lot less trivial.
You need a DataTemplate
, otherwise WPF doesn't know how to display your object.
精彩评论