WPF: Binding a Recursive Dictionary to TreeView - c#

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.

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}"/>

Adding context menu to tree view by XAML only

Trying to add a context menu to a TreeView with just xaml code.
Tv Show
Season 1
Season n
The context menu should only show when I right click a Season node.
Is that possible? I know how to solve it with code behind, but I'd like to learn to use WPF as it is intended. I have trouble finding out if I should be able to solve this with using only xaml.
Current xaml:
<TreeView
Grid.Row="1"
Grid.Column="0"
ItemsSource="{Binding TvShows}" x:Name="TvShowsTreeView"
SelectedItemChanged="TvShowsTreeViewOnSelectedItemChanged">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="tvShows:TvShow" ItemsSource="{Binding Seasons}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Try using the ItemTemplate property of HierarchicalDataTemplate. It should look like this:
<HierarchicalDataTemplate DataType="tvShows:TvShow" ItemsSource="{Binding Seasons}">
<TextBlock Text="{Binding Name}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate DataType="TypeOfSeasonInYourApplication">
<TextBlock Text="{Binding Name}">
<TextBlock.ContextMenu>
<ContextMenu>
<!-- Place MenuItems here -->
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
I actually didn't test that myself so please let me know if that works or not.

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}"/>

Can I automatically wrap HierarchicalDataTemplate items inside of a TreeViewItem?

I have a 4-level tree structure, defined by:
<HierarchicalDataTemplate DataType="{x:Type src:Level1}" ItemsSource="{Binding Path=Level2Items}">
<TextBlock Text="{Binding Path=Level1Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type src:Level2}" ItemsSource="{Binding Path=Level3Items}">
<TextBlock Text="{Binding Path=Level2Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type src:Level3}" ItemsSource="{Binding Path=Level4Items}">
<TextBlock Text="{Binding Path=Level3Name}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type src:Level4}">
<TextBlock Text="{Binding Path=Level4Name}"/>
</DataTemplate>
And it works great. The only thing is, I can't programmatically select any of my bound items, because they're not of type TreeViewItem (and therefore don't have the "IsSelected" property). Is there a way to automatically wrap databound items in a particular container type (in this case: TreeViewItem)?
If your items are in a TreeView, they'll be wrapped in a TreeViewItem automatically by the TreeView's ItemContainerGenerator. You can do something like this to ensure the IsSelected property on TreeViewItem maps to a property on your data class:
<TreeView>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding MyIsSelectedProperty}"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>

Categories