WPF TreeView HierarchicalDataTemplate set child to active based on ItemsSource object property - c#

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.

Related

TreeView with nested List

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.

How to make same color for treeview item and parent?

I am creating Treeview in wpf.
Each parent item set random foreground color using converter.
I want all its children to set the same color. using only xaml.
Here is part of my code:
<HierarchicalDataTemplate DataType="{x:Type sotc:Category}"
ItemsSource="{Binding Path=NoteList}">
<TextBlock Text="{Binding Path=Name}" Forground="{Binding Path=Name, Convert={StaticResource Converter1}}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type sotc:Note}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
What I wish to do is to set the second treeview item (in the xaml code), Children in HierarchicalData the same forgound color as its parent.
Is there a way to do that?
Thank Ahead,
You can try by using the FindAncestor RelativeSource mode.
Just replace your Note template with this one:
<DataTemplate DataType="{x:Type sotc:Note}">
<TextBlock Text="{Binding Path=Name}"
Foreground="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=2, AncestorType=TreeViewItem}, Path=Header.Name, Converter={StaticResource Converter1}}"/>
</DataTemplate>
I used a DataTemplate, but you can keep using a HierarchicalDataTemplate if you prefer.

Treeview not showing my children

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>

Why are my databound TreeView items blank?

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...

WPF TreeView how to add TreeViewItem control template for child elements of TreeViewItem

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>

Categories

Resources