Binding Dictionary<string,List<Class>> to TreeView - c#

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.

Related

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.

Multiple HierarchicalDataTemplate

I have some trouble with TreeView in WPF.
I have got three collection which contains each other.
So, i have got a Group type, in it there is a MasterActions ObservableCollection.
In MasterAction there is a SlaveActions ObservableCollection.
Group, MasterAction and SlaveAction classes have a Description property.
I would like to bind them to a TreeView.
I tried HierarchicalDataTemplate construction, but it is not working.
There is the Xaml code:
<TreeView x:Name="TreeViewResult" HorizontalAlignment="Stretch" ItemsSource="{Binding}" VerticalAlignment="Stretch" Margin="10,10,10,10" >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Groups}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="3" Text="{Binding Description}"/>
</StackPanel>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding MasterActions}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Description}"/>
</StackPanel>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SlaveActions}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Description}"/>
</StackPanel>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Codebehind:
Handler handler = new Handler();
Group group1 = new Group("group1_description");
MasterAction masterAction11 = new MasterAction("masterAction11");
SlaveAction slaveAction111 = new SlaveAction("slaveAction111");
SlaveAction slaveAction112 = new SlaveAction("slaveAction112");
MasterAction masterAction12 = new MasterAction("masterAction12");
MasterAction masterAction13 = new MasterAction("masterAction13");
SlaveAction slaveAction131 = new SlaveAction("slaveAction131");
handler.Add(group1);
handler.Add(masterAction11);
handler.Add(slaveAction111);
handler.Add(slaveAction112);
handler.Add(masterAction12);
handler.Add(masterAction13);
handler.Add(slaveAction131);
Group group2 = new Group("group2_description");
MasterAction masterAction21 = new MasterAction("masterAction21");
MasterAction masterAction22 = new MasterAction("masterAction22");
SlaveAction slaveAction221 = new SlaveAction("slaveAction221");
SlaveAction slaveAction222 = new SlaveAction("slaveAction222");
SlaveAction slaveAction223 = new SlaveAction("slaveAction223");
SlaveAction slaveAction224 = new SlaveAction("slaveAction224");
handler.Add(group2);
handler.Add(masterAction21);
handler.Add(masterAction22);
handler.Add(slaveAction221);
handler.Add(slaveAction222);
handler.Add(slaveAction224);
TreeViewResult.ItemsSource = handler.Groups;
Handler.Add() is an overloaded method. It manages all of three parameter to construct tree.
group1_description
masterAction11
slaveAction111
slaveAction112
masterAction12
masterAction13
slaveAction131
group2_description
masterAction21
masterAction22
slaveAction221
slaveAction222
slaveAction223
slaveAction224
When i run the code, it produces the next:
How can i reach to appear all MasterActions and SlaveActions description under the right node?
Thanks in advance.
It looks as if you try to go one level too deep with your HierarchicalDataTemplate. You set ItemsSource in your code to Groups
TreeViewResult.ItemsSource = handler.Groups;
and it actually does not matter how you set it (in code or via binding) more important is that top level item is a Group and you bind top level HierarchicalDataTemplate.ItemsSource to Groups. Instead of that move everything one level up and bind top level ItemsSource to MasterActions, next level to SlaveActions and lowest level can then be simple DataTemplate that would display Description without child nodes
<TreeView
x:Name="TreeViewResult"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="10,10,10,10" >
<!-- top node will be a Group -->
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding MasterActions}">
<TextBlock Margin="3" Text="{Binding Description}"/>
<!-- child node will be a MasterAction -->
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SlaveActions}">
<TextBlock Text="{Binding Description}"/>
<!-- leaf will be a SlaveAction -->
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
also, not related to your problem, but since you display single TextBlock you don't need StackPanel

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.

Unable to bind model to TreeView

This is a logging project, the Title provides the name of the item and the message logs each stage. Therefore, there are many Messages for each Title.
For the purpose of asking a question, I am simplifying my objects.
My Log class has 2 properties.
List<LogDetails>
string Title
And my LogDetails class has 1 property:
string Message
I am not able to bind the Message to my XAML. The Title binds as desired.
My xaml code:
<Window x:Class="BackUps.Logging.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myData="clr-namespace:BackUps.Logging"
Title="MainWindow"
Height="350"
Width="525">
<Grid>
<TreeView ItemsSource="{Binding}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type myData:Log}"
ItemsSource="{Binding LogDetailsList}">
<TextBlock Text="{Binding Title}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate DataType="{x:Type myData:LogDetails}">
<StackPanel>
<TextBlock Text="{Binding Message}" />
</StackPanel>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
and my code behind
public MainWindow(List<Log> logs)
{
InitializeComponent();
this.DataContext = logs;
}
My result is (where you can see the missing entries):
This is the Auto's window, which shows the object I'm trying to bind.
What have I missed or doing wrong?
I had a similar problem in the past, and fixed it by (completely) changing the xaml to the form:
<Grid>
<TreeView ItemsSource="{Binding}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type myData:Log}"
ItemsSource="{Binding LogDetailsList}">
<TextBlock Text="{Binding Title}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type myData:LogDetails}>
<TextBlock Text="{Binding Message}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>

Categories