WPF MVVM TreeView Groups - c#

I have 3 class's:
Building
Office
Person
In a Building there are offices and in each office there are persons.
My ViewModel have a ObservableCollection of Offices and one of the properties is the Building and ObservableCollection.
What is the best way to create TreeView that is binded to office and it group by Building, Offices, persons?
Example:
Building1
Office1
Person1
Person2
Person3
Office2
Person4
person5
Building2
Office3
Person6
Person7
Person8
Office4
Person9
person10

If you want to get this layout in your treeview you will be creating HieratchicalDataTemplates for Buildings and Offices and a DataTemplate for a Person. I am assumijng that you have a text property called Name in all 3 of your ViewModels.
<TreeView ItemsSource="{Binding Buildings}">
<TreeView.Resources>
<HierarchicalDataTemplate ItemsSource="{Binding Offices}" DataType="{x:Type VM:BuildingViewModel}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Persons}" DataType="{x:Type VM:OfficeViewModel}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type VM:PersonViewModel}">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TreeView.Resources>
</TreeView>

Related

WPF: The key is already defined in this scope

I'm working with TreeView and I want two ObservableCollections (KategorijeLekovi and KategorijeRadnici) to be shown under a node (which is an object of class Apoteka). I get this error: "The key is already defined in this scope." and it reffers to the second DataType="{x:Type local:Apoteka}". However, if I delete this DataType, I get a different error: "All objects added to an IDictionary must have a Key attribute or some other type of key associated with them."
<TreeView Grid.Column="0" Grid.Row="0" Name="trv1" >
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Apoteka}" ItemsSource="{Binding KategorijeLekovi}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Naziv}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Apoteka}" ItemsSource="{Binding KategorijeRadnici}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Naziv}"/>
</StackPanel>
</HierarchicalDataTemplate>
...
Thank you all for answering, I managed to solve it with your help. Instead of having two ObservableCollections I made one, which had items from both KategorijeLekovi and KategorijeRadnici
You just need to use the x:key attribute
<HierarchicalDataTemplate x:Key="LekoviTemplate" DataType="{x:Type local:Apoteka}" ItemsSource="{Binding KategorijeLekovi}">
<HierarchicalDataTemplate x:Key="RadniciTemplate" DataType="{x:Type local:Apoteka}" ItemsSource="{Binding KategorijeRadnici}">
Then set it in the tree view,
<TreeView ItemTemplate="{StaticResource LekoviTemplate}" ...
or
<TreeView ItemTemplate="{StaticResource RadniciTemplate}" ...

WPF TreeView - Confusing data binding and style for child nodes

I am confused of WPFs data binding and style declaration for TreeViews, especially for child nodes.
My ViewModel contains an object with the following hierarchy:
- Component1
- SubcomponentA
- SubcomponentB
- Component2
- SubcomponentX
- SubcomponentY
- SubcomponentZ
I would like to modify the XAML file so I do not have to do anything within the .cs file.
This piece of code actually works:
<TreeView Name="tvComponent" ItemsSource="{Binding BpModule.BpComponentPrototypes.Elements}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding BpSubcomponents.Elements}">
<StackPanel Orientation="Horizontal">
<CheckBox Name="cb_run"></CheckBox>
<TextBlock Text="{Binding ShortName}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
However, I would like to create different styles for root and child nodes.
I tried different approaches with almost completely different XAML code. But the major problem was to describe the dependency of the binding of child nodes to their parent and so they remained empty during runtime.
Can you help me out?
Thank you.
In my opinion you can use the implicit DataTemplate mechanism (take a look here to the DataType property). In this way you can define more DataTemplates, each one of them "linked" to a specific Type.
<TreeView Name="tvComponent" ItemsSource="{Binding BpModule.BpComponentPrototypes.Elements}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:RootType}"ItemsSource="{Binding ...}">
...
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:ComponentType}" ItemsSource="{Binding BpSubcomponents.Elements}">
<StackPanel Orientation="Horizontal">
<CheckBox Name="cb_run"></CheckBox>
<TextBlock Text="{Binding ShortName}" />
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:SubcomponentType}">
...
</DataTemplate>
</TreeView.Resources>
</TreeView>
Otherwise you can always use a DataTemplateSelector to create your own logic which chooses the right template for you.
Well, somehow I achieved to provide different designs for root and children nodes. Here is the XAML code:
<TreeView Name="tvSoftwareComponentPrototypes" ItemsSource="{Binding Path=BpModule.BpComponentPrototypes.Elements}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding BpSubcomponents.Elements}">
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Name="cb_run"></CheckBox>
<Image Source="/Resources/SubComponent.png" Margin="5,0,3,0" />
<TextBlock Text="{Binding ShortName}" />
</StackPanel>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Name="cb_swc" Checked="cb_swc_Checked" Unchecked="cb_swc_Unchecked"></CheckBox>
<Image Source="/Resources/Component.png" Margin="5,0,3,0" />
<TextBlock Text="{Binding ShortName}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
And the next problem is that I cannot access the Checkboxes at runtime.
WPF freaks me out :/
EDIT:
Hold on .. actually this is quite easy by changing the checkbox to the following XAML code:
<CheckBox Name="cb_swc" IsChecked="{Binding Path=PropertyName, Mode=TwoWay}"/>

WPF: Binding a Recursive Dictionary to TreeView

I want to use XAML to display a TreeView of Dictionary<string, object>. Every Dictionary's Value is either a string, a Dictionary<string, object> or Dictionary<string, object[]>. I'm unable to find XAML that displays all three cases. The following XAML will show a nice TreeView but the terminal nodes (string), are blank lines. What's the correct XAML or do I have to use a converter?
<TreeView>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Value}">
<TextBlock Text="{Binding Key}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
You should provide more than one template with specialization for the strings:
<TreeView xmlns:system="clr-namespace:System;assembly=mscorlib">
<TreeView.Resources>
<HierarchicalDataTemplate ItemsSource="{Binding Value}">
<TextBlock Text="{Binding Key}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type system:String}">
<TextBlock Text="{Binding}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
Create multiple hierarchical data templates, then use an ItemTemplateSelector to select between them based on the type of the object being displayed.

Binding Dictionary<string,List<Class>> to TreeView

I have collection
Dictionary<string, List<Manager>> Stuff;
Manager is class with some properties.
I would like to bind this dictionary to TreeView or ListView like this:
Key as title, then list of Managers as children. For example:
Director (it's key from dictionary)
John (it's property Manager.Name)
Steve
Owner
Jack
I tried something like this:
<TreeView ItemsSource="{Binding Stuff}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate>
<TextBlock Foreground="Red" Text="{Binding Key}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
It shows me titles but I don't know how to make it further.
Or maybe it will be better to change collection and bind it in some other way.
EDIT
I unnecessarily complicated job with this dictionary. I created a class Stuff with property string Name;, and List<Managers> Managers;.
Use it in main class as ObservableCollection<Stuff> Stuff; and it works fine with this XAML:
<TreeView ItemsSource="{Binding Stuff}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Managers}">
<TextBlock Foreground="Red" Text="{Binding Name}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
So yeah using Dictionary wasn't best in this case.
Try this :
<TreeView Name="treeView1" ItemsSource="{Binding Stuff}" >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Value}">
<TextBlock Foreground="Red" Text="{Binding Path=Key}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Try this:
<TreeView ItemsSource="{Binding Stuff}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Values}">
<TextBlock Foreground="Red" Text="{Binding Key}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
However, I'd always recommend using an ObservableCollection<CustomDataType> as the ItemsSource because it just makes the whole job simpler. For more help with the HierarchicalDataTemplate, please see the HierarchicalDataTemplate Class page on MSDN.

Binding WPF TreeView to Object

How to bind TreeView to the grouping collection ListCollectionView?
code cs:
ListCollectionView view=new ListCollectionView(Global._subjectCollection);
view.GroupDescriptions.Add(new PropertyGroupDescription("Fund"));
view.GroupDescriptions.Add(new PropertyGroupDescription("Cipher"));
treeView1.ItemsSource = view.Groups;
code XAML:
<TreeView>???</TreeView>
Check out Bea Stollnitz's blog entry "How do I display grouped data in a TreeView?".
You use a HierarchicalDataTemplate to present the CollectionViewGroup. The Name property will have the value of the property you grouped by, so the value of Fund or Cipher, and the Items property will have the nested group for outer groupings and the actual object for the innermost grouping. It will look something like this:
<Window.Resources>
<!-- Template for actual object -->
<DataTemplate x:Key="ThirdTemplate">
<TextBlock Text="{Binding OtherData}"/>
</DataTemplate>
<!-- Template for Cipher groups -->
<HierarchicalDataTemplate x:Key="SecondTemplate"
ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource ThirdTemplate}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
<!-- Template for Fund groups -->
<HierarchicalDataTemplate x:Key="FirstTemplate"
ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource SecondTemplate}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</Window.Resources>
<TreeView Name="treeView1" ItemTemplate="{StaticResource FirstTemplate}"/>

Categories