Wpf Treeview Hierarchicaldatatemplate - Binding to Object with Multiple Child Collections

WPF TreeView HierarchicalDataTemplate - binding to object with multiple child collections - Reloaded

Your HierarchicalDataTemplates are at the same level of the tree because your for loops are putting them into the same collection. Essentially your collection doesn't match your bindings in XAML.

I believe to get this to work you would need separate collections for each level you want. So you would need one collection for the continent and a second for the countries.

Then you would need to update your resources to this:

        <TreeView Name="tvwBuList" ItemsSource="{Binding BusinessunitList.Items}" HorizontalAlignment="Left" VerticalAlignment="Top" MinHeight="230" MinWidth="265" MaxHeight="230" MaxWidth="265">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:ContinentViewModel}" ItemsSource="{Binding Path=Continents}">
<TextBlock Text="{Binding Path=ContinentCode}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:CountryViewModel}" ItemsSource="{Binding Path=Countries}">
<TextBlock Text="{Binding Path=CountryCode}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:BusinessunitViewModel}">
<RadioButton GroupName="BUGroup" Content="{Binding Path=BuCode}" />
</DataTemplate>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<!--Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/-->
<Setter Property="IsExpanded" Value="True"/>
<Setter Property="Foreground" Value="Yellow"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>

That should give you what you want. I've used the same model for defining a message structure with the treeview and the only difference between our code is I have each level in its own collection and bind explicitly to that collection.

WPF TreeView HierarchicalDataTemplate - binding to object with multiple child collections

A HierarchicalDataTemplate is a way of saying 'this is how you render this type of object and here is a property that can be probed to find the child nodes under this object'

Therefore you need a single property that returns the 'children' of this node.
e.g. (If you can't make both Group and Entry derive from a common Node type)

public class Group{ ....
public IList<object> Items
{
get
{
IList<object> childNodes = new List<object>();
foreach (var group in this.SubGroups)
childNodes.Add(group);
foreach (var entry in this.Entries)
childNodes.Add(entry);

return childNodes;
}
}

Next you don't need a HierarchicalDataTemplate for entry since an entry doesn't have children. So the XAML needs to be changed to use the new Items property and a DataTemplate for Entry:

<TreeView Name="GroupView" Grid.Row="0" Grid.Column="0" ItemsSource="{Binding}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Group}" ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:Entry}" >
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>

And here's what that looks like.
Screenshot of Output

WPF TreeView HierarchicalDataTemplate - binding to object with different child collections

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 :-)

TreeView HierarchicalDataTemplates for more than one collection type

Assumption: the Project is given and cannot be changed. That means in the context of MVVM Project is the model. You can now create a view model that connects the view and model. It could look like this:

public class ProjectViewModel
{
public Project Project { get; set; }

public string Name
{
get
{
return Project.Name;
}
}

public int Priority
{
get
{
return Project.Priority;
}
}

public IList Children
{
get
{
if (Project.Projects.Count > 0)
{
return Project.Projects;
}

return Project.Tasks;
}
}
}

Then you adapt the template to the view model and you are done:

    <TreeView  x:Name="ProjectsTree" >
<TreeViewItem Header="Projects"
ItemsSource="{Binding ProjectsViewModelCollection, Mode=TwoWay}"
IsExpanded="True" >
<TreeViewItem.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Project}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"></TextBlock>
</HierarchicalDataTemplate>
</TreeViewItem.Resources>
</TreeViewItem>

WPF treeview with multiple types of child nodes

I don't know if this will work in the context, and I can't try it right now, but what about...

        <HierarchicalDataTemplate.ItemTemplate>
<DataTemplate TargetType="{x:Type=ocSpecialPoliciesItem}">
...
</DataTemplate>
<DataTemplate TargetType="{x:Type=ocSpecialPoliciesExplain}">
...
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>

Or something along those lines?

EDIT:
On the understanding that we want a single TreeView that has two sorts of item (sorry, this is c#, but I'm sure you can translate).

You can try making a base class, from which you derive both ocSpecialPoliciesItem and ocSpecialPoliciesExplain, and make an observable collection of the base class. Then use two DataTemplates in your TreeView.

class SpecialPoliciesBase {}

class ocSpecialPoliciesItem : SpecialPoliciesBase
{ ... }

class ocSpecialPoliciesExplain : SpecialPoliciesBase
{ ... }

class ocSpecialPolicies
{
// this is where you put ocSpecialPoliciesItem and
// ocSpecialPoliciesExplain
// I do not see any code for populating your two collections,
// but I suppose you must have some somewhere
public ObersvableCollection<SpecialPoliciesBase> SPCollection
{ get ... }
}

And in your XAML

<TreeView 
Name="TreeView2"
Margin="3"
ItemsSource="{
Binding ElementName=MainWindow,
Path=SPCollection,
UpdateSourceTrigger=PropertyChanged}">

<TreeView.ItemTemplate>
<HierarchicalDataTemplate ...>
<TextBlock Text="{Binding Path=RecallNum}" />

<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate TargetType={x:Type ocSpecialPoliciesItem} >
<!- this is where you put the template for Item !->
</DataTemplate>
<DataTemplate TargetType={x:Type: ocSpecialPoliciesExplain} >
<!- this is where you put the template for Explanations !->
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>

I have something like this working, but not with a treeview. I use an empty abstract ViewBase class, with several concrete View classes. My ViewModel has a Property called CurrentView, of type ViewBase, and there is a DataTemplate for each of the concrete views. My XAML picks out the right DataTemplate for whichever derived class CurrentView has.



Related Topics



Leave a reply



Submit