I have the following markup in a ServiceTree control, which should present a TreeView of services in a ServiceGroup:
<Grid>
<TreeView ItemsSource="{Binding Services}" DataContext="{Binding}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}" DataType="{x:Type businessService:BusinessService}">
<TreeViewItem Header="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
In the top level binding, Services is the collection of services that belongs to a ServiceGroup.
In the main window, I use the following data binding to create an OdcExpander from the Odyssey project:
<ItemsControl.ItemTemplate>
<DataTemplate DataType="groups:ServiceGroup">
<odc:OdcExpander Header="{Binding UIMetadata.MenuText}" HeaderBackground="{Binding UIMetadata.MenuTabBackColor}">
<XTime900Shell:ServiceTree />
</odc:OdcExpander>
</DataTemplate>
</ItemsControl.ItemTemplate>
This works. I get one expander correctly bound to each ServiceGroup, but on the groups with services, i.e. the "Employees" group, there is a space where the treeview should be, proportional in height to the number of services it should show, so it is binding to the Services collection property, and creating an item for each service, but not displaying anything for the service 'Name` property, which I know is correctly populated.
I think the error is that your are using a TreeViewItem, you should not. If you are creating a data template you can show any visual item, for instance a text block. The tree view item will be the container item, that you will show as a text box (for instace). you should do like this:
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}" DataType="{x:Type businessService:BusinessService}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
Hope works...
Related
I'm new to MVVM. I created a TreeView with hierarchy of 3 levels: level1: Program, level2: Action, level3: Position. I also created a ListView under the TreeView (see xaml) . Right now the items of the ListView are not bound to anything (the names "Type", "Speed" are only placeholders.)
When selecting an item from level2 (Action) in the TreeView, i'd like to see it's properties (type, speed) appear on the ListView below.
Is there a way to do it through a change in the View only (xaml), without using code in the ViewModel ? maybe there's a way to use a certain binding, that can be changed when pressing on the item in the TreeView?
xaml (model):
<TreeView
Grid.Row="0"
x:Name="MainTreeView"
HorizontalAlignment="Stretch"
Margin="10"
VerticalAlignment="Stretch"
ItemsSource="{Binding Programs}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Actions}" DataType="{x:Type VM:ProgramVM}">
<Label Content="{Binding ProgramName}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Poses}" DataType="{x:Type VM:ActionVM}">
<Label Content="{Binding Actiontype}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate DataType="{x:Type VM:PositionVM}">
<Label Content="{Binding PosName}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<ListView Grid.Row="1" Margin="10" Name="lvUsers">
<ListView.View>
<GridView>
<GridViewColumn Header="Type" Width="100" DisplayMemberBinding="{Binding Type}" />
<GridViewColumn Header="Speed" Width="100" DisplayMemberBinding="{Binding Speed}" />
</GridView>
</ListView.View>
</ListView>
This is a well known issue with the treeview in WPF. You have to create an 'is selected' property for each object in your tree so that you know what the user has chosen, then you can just show the selected object in your listview.
It's been a while since I played with a treeview, but here are some of the links that I think helped me in the past.
Data binding to SelectedItem in a WPF Treeview
WPF MVVM TreeView SelectedItem
I want to display some objects in a treeview, but so far unfortunately without success.
I've a ObservableCollection <ICustom> of objects: Settings.ListOfCustomers
The interface of the object ICustom:
int Id { get; }
int age { get; }
CustomerType CustomerType { get; }
ObservableCollection<IValue> ListOfValues { get; }
The ObservableCollection<IValue> ListOfValues has also some properties like:
String Name { get; }
My View:
<TreeView ItemsSource="{Binding ListOfCustomers, UpdateSourceTrigger=PropertyChanged}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type customerConfig:ICustomer}">
<TextBlock Text="{Binding Id}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type valueConfig:IValue}" ItemsSource="{Binding ListOfValues}">
<StackPanel>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
Question:
How can I display display these objects in a TreeView? My approach (see "My View") does not work.
To Question 1: ObservableCollections have the advantage, that all notifications to the view are done automatically. So you do not have to worry about NotifyPropertyChanged events for adding/removing objects from the collection.
To Question 2: Your ViewModel is your DataContext? I do not see where there is a property ObservableCollection<ICustomer>? Can you provide more detail about relationships in this classes?
EDIT:
Based on the answer of mm8, the x:Type attribute should be of a concrete type. So the code should look something like this:
I suggest that you have an ObservableCollection<ICustomer> ListOfCustomers in your ViewModel, then you can bind in the view:
<TreeView ItemsSource="{Binding ListOfCustomers}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type Customer}">
<TextBlock Text="{Binding Id}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type modSettings:Value}"
ItemsSource="{Binding ListOfValues}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
I think this thread provides a more detailed solution for your problem: Implement WPF treeview with different Parent Nodes a well as different child nodes?
EDIT2:
I changed my code a bit, to match your requirements. This should display all nodes:
<TreeView ItemsSource="{Binding ListOfCustomers}" >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=ListOfValues}" DataType="{x:Type Customer}">
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type modSettings:Value}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<TextBlock Text="{Binding Path=Id}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
I have converted all Lists to ObservableCollection, also the nested Lists. Is this necessary and correct?
It is not necessary unless you intend to add items to the collection dynamically at runtime. Then you need to use an ObservableCollection<T> for these to automatically show up in the TreeView. Othwerwise you might as well use a List<T>.
How can I display display these objects in a TreeView?
You get the error message because you add the templates to the Items property of the TreeView. They should be added to the Resources property, i.e. you need to add a <TreeView.Resources> element.
You should also read this:
Why can't a DataTemplate bind to an interface when that DataTemplate was explicitly returned from a DataTemplateSelector?
Templates with the DataType property set to an interface type are not applied. So you should either define a HierarchicalDataTemplate per concrete type or use a DataTemplateSelector.
I am working on a IRC client in C# WPF that has a TreeView to show the servers and channels the client is currently connected to in this format:
Server
Channel
This is my current code:
<HierarchicalDataTemplate DataType="{x:Type IRCLib:ServerConnection}"
ItemsSource="{Binding Path=ActiveChannels}">
<TreeViewItem Header="{Binding Path=Config.ServerName}" Foreground="Black"/>
</HierarchicalDataTemplate>
What I want to do now is if a Channel object loaded from ActiveChannels has it's IsActive property set to true, is set that object's TreeViewItem IsSelected property to true.
I've been looking for an answer for a couple hours now and I haven't been able to find anything that helps me achieve this, but if this question is already answered here I'm sure on of you guys would be able to point me in the right direction.
Just set binding of IsSelected property at TreeViewItem to IsActive property of your Channel object. (Ensure dataContexts to be right)
try this template instead of what you have now:
<HierarchicalDataTemplate DataType="{x:Type IRCLib:ServerConnection}"
ItemsSource="{Binding Path=ActiveChannels}">
<CheckBox IsChecked="{Binding IsActive}" Content="{Binding Path=Config.ServerName}" Foreground="Black"/>
</HierarchicalDataTemplate>
You will see that all items would be checked according to IsActive property
So I tried some more things out and finally found what I was looking for, which was surprisingly easy.
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type IRCLib:ServerConnection}"
ItemsSource="{Binding Path=ActiveChannels}">
<TreeViewItem Header="{Binding Path=Config.ServerName}"
Foreground="Black"
IsExpanded="True"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type IRCLib:Channel}">
<TreeViewItem Header="{Binding Path=Name}"
IsSelected="{Binding Path=IsActive}"/>
</HierarchicalDataTemplate>
</TreeView.Resources>
By using another HierarchicalDataTemplate that targets TreeViewItems created from a Channel object I am able to to correctly set the IsSelected property.
I've not used the TreeView before other than in a few tutorials to get the hang of them. I thought I had, it turns out I haven't.
I'm trying to bind my TreeView to an object.
The Object is
public List<MyGrandparents> MyGrandParents {get;set;}
Within the MyGrandParent class, there is a property
public List<MyParents> MyParents{get;set;}
and lastly, within the MyParent class there is a property
public List<MyChildren> MyChildren {get;set;}
Within each class, there are other properties such as Name (no base classes though so no shared code at this stage)
I want to bind the lot to a tree view, so at 'root' level I only see grandparents. I can expand grandparents to only see parents, whom I can also expand to see children.
The issue I have is only the highest level is binding (grandparent level). My XAML is
<TreeView ItemsSource="{Binding MyGrandParents}">
<TreeView.Resources>
<DataTemplate DataType="{x:Type local:MyGrandParent}">
<TextBlock Text="{Binding Name}" Margin="0,0,10,0" />
</DataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:MyParent}" ItemsSource="{Binding MyGrandParents}">
<TextBlock Text="Don't bind to test"></TextBlock>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
I am lost as to why this isn't binding to give me a nice Tree.
You are not following correct pattern to use HierarchicalDataTemplate.
Item which might contain children should be declare as HierarchicalDataTemplate with ItemsSource set to child collection you want to show beneath it.
And item not containing any child should be declare as DataTemplate instead.
It should be like this -
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:MyGrandParent}"
ItemsSource="{Binding MyParents}">
<TextBlock Text="{Binding Name}" Margin="0,0,10,0" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:MyParent}"
ItemsSource="{Binding MyChildren}">
<TextBlock Text="Don't bind to test"></TextBlock>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:MyChildren}">
<TextBlock Text="Don't bind to test"></TextBlock>
</DataTemplate>
</TreeView.Resources>
how do I create treeview like this one:
<TreeViewItem Header="Customers" ItemsSource="{Binding Customers}">
Customers
Anna
Delete
Open
Peter
Delete
Open
Andrew
Delete
Open
I would like to create child item template something like this
<TreeViewItem Header="{Binding Header}">
<TreeViewItem Header="Delete"/>
<TreeViewItem Header="Open"/>
</TreeViewItem>
But it does not quite work that well because I end up having treeviewitem with datatemplate treeviewitem, but I would like to override controltemplate of child elements, but not parent.
Sure, I want to avoid my binding to be TreeViewItem, nor I want to create children with those static obejct "Open", "Delete".
Here is one of the best articles about TreeView I ever read.
Inside TreeView.Resources you could declare several DataTemplates with different DataType if Delete and Open commands were items of some collection. (TargetType for the commands would be ICommand).
But it seems to mee you do not need TreeView at all.
Customers is a header of the list. If you want it to be epxpandable use Expander control.
Then it is sufficient to provide one data template for each customer.
<DataTemplate DataType="CustomerTypeName">
<Expander Header="{Binding CustomerName}">
<Button Command="{Binding DeleteCustomerCmd}" Content="Delete" Margin="15,0,0,0"/>
<Button Command="{Binding OpenCustomerCmd}" Content="Open" Margin="15,0,0,0"/>
<Expander/>
<DataTemplate>
But here you'll have some troubles with selection highlight.
public class CommandWrapper
{
ICommand Command {get;set;}
string CommandName {get;set;}
}
public class CustomerViewModel
{
Customer Customer {get;set;}
IEnumerable<CommandWrapper> Commands {get;}
}
Let Customers be collection of CustomerViewModel.
Then the following XAML can help:
<TreeView ItemsSource="{Binding ...}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="TypeHoldingCustomersCollection"
ItemsSource="{Binding Customers}">
<TextBlock Text="Customers"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="CustomerViewModel"
ItemsSource="{Binding Commands}">
<TextBlock Text="{Binding Path=Customer.Name}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="CommandWrapper">
<Button Content="{Binding CommandName}" Command="{Binding Command}"/>
</DataTemplate>
</TreeView.Resources>
</TreeView>