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>
Related
I am new in c# and wpf. I have a treeveiew and what I need is - when user click on one of the items, mark this item as selected. For example as it is implemented in DataGrid when user click on the row it marks as selected and user can easaly understand which row was selected.
My implementation of TreeView is
<TreeView Name="Tv_request"
Grid.Row="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding Path=RequestSet}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate
ItemsSource="{Binding Childrens}"
DataType="{x:Type local:IRequest}">
<TreeViewItem MouseUp="TreeViewItem_MouseUp"
Header="{Binding TypePage}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
Way I can get clicked item - by TreeViewItem_MouseUp event, but I need to implement mark selection item.
How to implement it?
EDIT
In order to get clicked item I am using such approach
private void TreeViewItem_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var tree = sender as TreeViewItem;
if (tree != null)
{
IRequest item = tree.DataContext as IRequest;
Presenter?.TreeViewSelectedItem(item);
}
}
Such way I can have access to my selected item, but if I change it to
private void TreeViewItem_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var tree = sender as ContentPresenter;
if (tree != null)
{
IRequest item = tree.DataContext as IRequest;
Presenter?.TreeViewSelectedItem(item);
}
}
So, tree.DataContext return me NodeType and logically it is ok, because I set
<ContentPresenter Content="{Binding NodeType}"
MouseUp="TreeViewItem_MouseUp"/>
instead of
<TreeViewItem MouseUp="TreeViewItem_MouseUp"
Header="{Binding NodeType}"/>
So, question is, how I can get entire clicked object not just his NodeType?
Firstly, your item template is creating a TreeViewItem inside a TreeViewItem. It should look like this:
<HierarchicalDataTemplate
ItemsSource="{Binding Childrens}"
DataType="{x:Type local:IRequest}">
<ContentPresenter Content="{Binding TypePage}"/>
</HierarchicalDataTemplate>
The TreeViewItem will be automatically created by WPF, and the HierarchicalDataTemplate you provide will be applied to it.
Secondly, TreeViewItem has a property called IsSelected. You can use the Style to bind this property to a property of your IRequest object:
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
<Setter Property="IsSelected" Value="{Binding RequestIsSelected}" />
</Style>
Or you can use an EventSetter to provide a handler for the TreeViewItem.Selected event if you prefer:
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
<EventSetter RoutedEvent="Selected" Value="TreeViewItem_Selected" />
</Style>
You could also use the TreeView.SelectionChanged event instead of dealing with individual tree view items, but be aware that you (annoyingly!) can't select a new item from the TreeView itself.
If you don't mess things up by nesting a hierarchicaltemplate inside an itemtemplate you don't need to do anything at all to get a selected treeview item coloured.
Look at the xaml here:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.hierarchicaldatatemplate?view=netcore-3.1
<HierarchicalDataTemplate DataType = "{x:Type src:League}"
ItemsSource = "{Binding Path=Divisions}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType = "{x:Type src:Division}"
ItemsSource = "{Binding Path=Teams}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type src:Team}">
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
Notice what you give it as a template appears inside a treeviewitem. Not hierarchicaldatatemplate within itemtemplate.
Similarly, here: https://www.wpf-tutorial.com/treeview-control/treeview-data-binding-multiple-templates/
<TreeView Name="trvFamilies">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type self:Family}" ItemsSource="{Binding Members}">
<StackPanel Orientation="Horizontal">
<Image Source="/WpfTutorialSamples;component/Images/group.png" Margin="0,0,5,0" />
<TextBlock Text="{Binding Name}" />
<TextBlock Text=" [" Foreground="Blue" />
<TextBlock Text="{Binding Members.Count}" Foreground="Blue" />
<TextBlock Text="]" Foreground="Blue" />
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type self:FamilyMember}">
<StackPanel Orientation="Horizontal">
<Image Source="/WpfTutorialSamples;component/Images/user.png" Margin="0,0,5,0" />
<TextBlock Text="{Binding Name}" />
<TextBlock Text=" (" Foreground="Green" />
<TextBlock Text="{Binding Age}" Foreground="Green" />
<TextBlock Text=" years)" Foreground="Green" />
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
If you try that code in the last link, notice when you click a node - a family or member - you get a blue background.
This is how you mark a selected treeviewitem.
Don't mess up your templates and it "just works".
If you need to know which one was selected then this is a bit tricky because you can't just bind selecteditem like a datagrid. This is indirectly due to the hierarchical nature of the control meaning each node has it's own itemcontrol for children. A treeviewitem is a headereditemscontrol.
This answer illustrates one of the behaviors commonly used to enable binding of selecteditem:
Data binding to SelectedItem in a WPF Treeview
Once you bind the selected item like this you can then work with that property in the viewmodel. When it changes the setter will be hit and you can put code in there to do work with that item.
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}"/>
Is it possible to attach different DataTemplates so that the children render differently to the same HierarchicalDataTemplate, which as I understand controls the View of the header
I am trying to do it without success::
<Window.Resources>
<DataTemplate
x:Key="ChildTemplate1">
<Label Content="Blah">
</DataTemplate>
<DataTemplate
x:Key="ChildTemplate2">
<Label Content="Blah2">
</DataTemplate>
<HierarchicalDataTemplate
x:Key="RootTemplate"
ItemTemplate={StaticResource ChildTemplate2}"
ItemsSource="{Binding Path=Children}">
<Label Content="Header">
</HierarchicalDataTemplate>
</Window.Resources>
<TreeView ItemTemplate="{StaticResource RootTemplate}">
<TreeViewItem ItemTemplate="{StaticResource ChildTemplate1}"/>
</TreeView>
So again, what I am trying to do is override the DataTemplate set in HierarchicalDataTemplate(ChildTemplate2) with ChildTemplate1
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.
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}"/>