WPF: Add ContextMenu to TreeView - c#

For a second there I was proud that I finally managed to add a ContextMenu to a leaf in my TreeView, but then I realized it pops up on the wrong node.
My XAML:
<TreeView
Grid.Row="1"
Grid.Column="0"
x:Name="TvShowsTreeView"
ItemsSource="{Binding TvShows}"
SelectedItemChanged="TvShowsTreeViewOnSelectedItemChanged">
<!-- Season template -->
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Seasons}">
<TextBlock Text="{Binding Name}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Hide"></MenuItem>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
When I run this, I get a context menu on the TvShows, not on the Seasons.
I am close, right? :) Who can help me further here?

The correct XAML should be like this:
<TreeView
Grid.Row="1"
Grid.Column="0"
x:Name="TvShowsTreeView"
ItemsSource="{Binding TvShows}"
SelectedItemChanged="TvShowsTreeViewOnSelectedItemChanged">
<!-- TvShows template -->
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Seasons}">
<TextBlock Text="{Binding Name}" />
<!-- Seasons template -->
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate>
<TextBlock Text="{Binding Name}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Hide"></MenuItem>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
The additional HierarchicalDataTemplate specifies the template for Seasons, which itself does not need to bind ItemSource because it does not contain nodes.

Related

WPF TreeView: Focus Selected Item

I've got a TreeView, that displays a Constant-Object which the User searched for. Those may have other Constant-Objects as a Parent or as Children. After selecting one of those objects in the TreeView, properties of this object are displayed below.
I already managed to select the searched object after displaying the treeview, now I need to set focus onto it.
Screenshots:
Current state
What I want to achieve
TreeView XAML Code:
<ContentControl x:Name="SelectedItemHelper" Content="{Binding SelectedConst, Mode=OneWayToSource}" Visibility="Collapsed"/>
<TreeView x:Name="constOutput" Margin="25,76,25,0" Height="130" HorizontalAlignment="Stretch" VerticalAlignment="Top" SelectedItemChanged="constOutput_SelectedItemChanged" ItemsSource="{Binding FoundConstants}" Style="{StaticResource lightGrayTV}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Constant}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Path=Name}" Style="{StaticResource txtTV}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Constant}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Path=Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Constant}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Path=Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Constant}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Path=Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Constant}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
The SelectedConst property is being set to the SearchedConst property at the end of the search.
private void SearchForConst(object value)
{
FoundConstants.Clear();
SearchedConstant = ConstantInfo.GetSearchedConstant(Input);
...
}
SelectedConst = SearchedConstant;
OnPropertyChange(nameof(SearchedConstant));
OnPropertyChange(nameof(FoundConstants));
}
How can I force the TreeView to expand and focus onto the SearchedConst/SelectedConst?
Then you need to make a reproducible example. A small project that replicates the problem.

How to add Scrolling to the HierarchicalDataTemplate items inside TreeView?

I want to add scrollviewer to every child inside TreeView. I can add scrollviewer for TreeView but I cannot apply scrollviewer to children elements.
Here is my example of the structure what currently using
<TreeView x:Name="test" ItemsSource="{Binding Root}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding TestTemplates}"
DataType="{x:Type vm1:TestTemplateViewModel}">
<Grid>
<DockPanel>
<TextBlock Text="{Binding TextBlock}"/>
<Image Source="/Images/Test.png"
Visibility="true">
</DockPanel>
</Grid>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

Binding contextmenu command in a tree view with hierarchical data template

I have a treeview with hierarchical data template and I am trying to set DataContext for ContextMenu, so I could bind commands to it. I have done research and know that ContextMenu doesn't inherit DataContext of its parent. I tried to follow these posts: How to set the RelativeSource in a DataTemplate that is nested in a HierarchicalDataTemplate?
How to bind to the DataContext of a HierarchicalDataTemplate from its ItemTemplate XAML? but still can't get it to work. Any help would be appreciated. Here is my sample code:
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type viewModels:SiteViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
</StackPanel.Resources>
<Image Width="16" Height="16" Margin="3,0" />
<TextBlock Text="{Binding SiteName}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type viewModels:LevelViewModel}" ItemsSource="{Binding Children}" >
<StackPanel Orientation="Horizontal" >
<Image Width="16" Height="16" Margin="3,0" />
<TextBlock Text="{Binding LevelName}" >
<TextBlock.ContextMenu >
<ContextMenu>
<MenuItem Header="Test" Command="{Binding ?????????" CommandParameter="{Binding}"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
One way to solve it:
In my case I had something like that:
<DataTemplate DataType="...">
<TreeView>
<TreeViewItem Tag="{Binding ElementName=LocalControl, Path=DataContext}"
Header="{Binding ...}"
ContextMenu="{StaticResource ...}">
...
</TreeViewItem>
</TreeView>
</DataTemplate>
You need to bind Tag attribute of parent TreeViewItem to its DataContext, then somewhere in your hierarchical templates for context menu you should bind its DataContext to Tag of parent control:
<ContextMenu x:Key="CyclogramFolderContextMenu"
DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<TextBlock Text="Do something" >
<TextBlock.InputBindings>
<MouseBinding Command="{Binding Path=(viewModels:someViewModel.SomeCommand)}" MouseAction="LeftClick" />
</TextBlock.InputBindings>
</TextBlock>
</ContextMenu>

How to bind mouse click to ContentPresenter within a HierarchicalDataTemplate to ViewModel?

I have the following DataTemplate used in a TreeView.
How do I have LeftMouseClick on the ContentPresenter to bind to a property of the DataType in MVVM fashion?
Thanks for any help (or better idea).
<DataTemplate x:Key="sharedTemplate">
<StackPanel Orientation="Horizontal">
<CheckBox
Focusable="False"
IsChecked="{Binding IsChecked}"
VerticalAlignment="Center"
/>
<ContentPresenter
Content="{Binding Name, Mode=OneTime}"
Margin="2,0"
/>
</StackPanel>
</DataTemplate>
<TreeView
ItemContainerStyle="{StaticResource TreeViewItemStyle}"
ItemsSource="{Binding ReportTree}" >
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type r:ReportViewModel}"
ItemsSource="{Binding Children}"
>
<ContentControl Content="{Binding}"
ContentTemplate="{StaticResource sharedTemplate}" />
</HierarchicalDataTemplate>
................
Addendum: This works!
<DataTemplate DataType="{x:Type r:PrinterViewModel}">
<!--Bind the ContentControl to the DataType-->
<ContentControl Content="{Binding}"
ContentTemplate="{StaticResource sharedTemplate}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<h:EventToCommand
Command="{Binding DataContext.SelectItem, RelativeSource={RelativeSource FindAncestor, AncestorType=TreeView}}"
CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ContentControl>
</DataTemplate>
Addendum: As it turns out, the above Interaction.Trigger does work to correctly send the selected viewmodel back to the DataContext of the TreeView. Problem solved.

How to display context menu for treeview item in a hierarchial data template in wpf

How to display context menu for tree view item in wpf using the hierarchical data template? How to display context menu only for CountryTemplate:
<HierarchicalDataTemplate x:Key="DispTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Label}" Style="{StaticResource TreeTextStyle}" ToolTip="{Binding Path=Description}" Tag="{Binding Path=Tag}">
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="BuildingTemplate" ItemsSource="{Binding Path=Building}" ItemTemplate="{StaticResource BuildingTemplate}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Label}" Style="{StaticResource TreeTextStyle}" ToolTip="{Binding Path=Description}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="CityTemplate" ItemsSource="{Binding Path=City}" ItemTemplate="{StaticResource CityTemplate}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Label}" Style="{StaticResource TreeTextStyle}" ToolTip="{Binding Path=Description}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="CountryTemplate" ItemsSource="{Binding Path=Country}" ItemTemplate="{StaticResource CountryTemplate}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=RootName}" Style="{StaticResource TreeTextStyle}" ToolTip="{Binding Path=Description}"/>
</StackPanel>
</HierarchicalDataTemplate>
You also can add the ContextMenu to any visual child in the data template, for instance:
<HierarchicalDataTemplate x:Key="CountryTemplate" ItemsSource="{Binding Path=Country}" ItemTemplate="{StaticResource CountryTemplate}">
<StackPanel Orientation="Horizontal">
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Header" Command="{Binding Command}"/> <!--This command should be in the data context (each country item)-->
</ContextMenu>
</StackPanel.ContextMenu>
<TextBlock Text="{Binding Path=RootName}" Style="{StaticResource TreeTextStyle}" ToolTip="{Binding Path=Description}"/>
</StackPanel>
</HierarchicalDataTemplate>
One of the reasons why context menus do not work as cleanly as they could is because by default, they are in a different visual tree to everything else, so the DataContext cannot be found.
The key insight is to create a <Style> that defines a context menu,
then attach that style to a target element, which hooks up the context
menu. This shifts the context menu into a visual tree that is lined up with the default DataContext you want.
First, create the style:
<UserControl.Resources>
<ResourceDictionary>
<!-- For the context menu to work, we must shift it into a style, which means that the context menu is now in a
visual tree that is more closely related to the current data context. All we have to do then is set the style,
which hooks up the context menu. -->
<Style x:Key="ContextMenuStyle" TargetType="{x:Type StackPanel}">
<Setter Property="ContextMenu" Value="{DynamicResource TreeViewContextMenu}"/>
</Style>
<ContextMenu x:Key="TreeViewContextMenu">
<MenuItem Header="Test" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.CmdDisplayDetailsGraph}"/>
</ContextMenu>
Then, hook the context menu up anywhere you want, without running into issues caused by different visual trees.
Example 1:
<HierarchicalDataTemplate DataType="{x:Type snapshot:Details}" ItemsSource="{Binding DetailsList}">
<StackPanel Orientation="Vertical" Style="{StaticResource ContextMenuStyle}">
<ContentPresenter Content="{Binding}" ContentTemplate="{Binding View.DefaultDataRowTemplate}" />
</StackPanel>
Example 2:
<DataTemplate DataType="{x:Type snapshot:InstrumentDetails}">
<StackPanel Orientation="Vertical" Style="{StaticResource ContextMenuStyle}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Center">
<HierarchicalDataTemplate x:Key="CountryTemplate" ItemsSource="{Binding Path=Country}" ItemContainerStyle="{StaticResource CountryTemplateItemContainerStyle}" ItemTemplate="{StaticResource CountryTemplate}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=RootName}" Style="{StaticResource TreeTextStyle}" ToolTip="{Binding Path=Description}" />
</StackPanel>
</HierarchicalDataTemplate>
<Style x:Key="CountryTemplateItemContainerStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="ContextMenu" Value="{DynamicResource TreeViewContextMenu}"/>
</Style>
<ContextMenu x:Key="TreeViewContextMenu">
<MenuItem .../>
</ContextMenu>
As you can see, you can add your contextmenu in the Itemcontainerstyle of the HierarchicalDataTemplate
Basically I came up with this
<HierarchicalDataTemplate x:Key="ChildTemplate">
<StackPanel Orientation="Horizontal">
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy" CommandParameter="{Binding CopyTag}">
</MenuItem>
<MenuItem Header="Paste" CommandParameter="{Binding PasteTag}">
</MenuItem>
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.CopyPaste}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</StackPanel.ContextMenu>
<Image Source="/Images/Child.png" Stretch="None" VerticalAlignment="Center" HorizontalAlignment="Center" Style="{StaticResource TreeIconStyle}"/>
<TextBlock Text="{Binding Path=Label}" Style="{StaticResource TreeTextStyle}" ToolTip="{Binding Path=Description}" Tag="{Binding Path=Tag}">
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
And have separate parameters for copy and paste to differentiate copy and paste in a single command.

Categories