Multiple HierarchicalDataTemplate - c#

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

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 TreeView binding multiple sub-collections

I am building an application to manage connections, databases, tables, etc. I am needing to bind a collection of items with multiple sub-collections (see below). I am pretty new to WPF and am not sure if an answer to this question already exists. I've searched but haven't found any examples of the scenario I am faced with.
server1
-database1
--functions <- "static" node
---function1
---function2
--users <- "static" node
---user1
---user2
-database2
--functions <- "static" node
---function3
---function4
--users <- "static" node
---user3
---user4
When I try to bind it, I can get the data to display but it isn't in the format needed above. It's displayed like this.
server1
-database1
--function1
--function2
--user1
--user2
object hierarchy:
class DatabaseViewModel
{
public string Name
{
// normal getters and setters for 2way binding
}
public IObservableCollection<DbFunctionViewModel> Functions
{
// normal getters and setters for 2way binding
}
public IObservableCollection<DbUserViewModel> Users
{
// normal getters and setters for 2way binding
}
}
Markup:
<TreeView x:Name="Connections">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type viewModels:DbConnectionViewModel}" ItemsSource="{Binding Databases}">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="Name" Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type viewModels:DbDatabaseViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="Name" Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type viewModels:DbFunctionViewModel}">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="Name" Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type viewModels:DbUserViewModel}">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="Name" Text="{Binding UserName}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
I tried returning a CompositeCollection containing both and it didn't seem to work the way i need it to.
public IList Children
{
get
{
return new CompositeCollection
{
new CollectionContainer { Collection = Functions },
new CollectionContainer { Collection = Users }
};
}
}
My question is, how do you bind all the users to a node named users and all the functions to a node named functions? Any advice would be greatly appreciated. Thanks!
To get your Children property to work, you need to define a data template for CollectionContainer:
<HierarchicalDataTemplate DataType="{x:Type viewModels:CollectionContainer}"
ItemsSource="{Binding Collection}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
You should add a string Name property to CollectionContainer:
public IList Children
{
get
{
return new[]
{
new CollectionContainer("Functions", Functions),
new CollectionContainer("Users", Users),
};
}
}
A few hints:
Stack panels are redundant.
x:Name is redundant if you're binding using Text={Binding ...}.

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.

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

Categories