13

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>
Contango
  • 76,540
  • 58
  • 260
  • 305
TrustyCoder
  • 4,749
  • 10
  • 66
  • 119

4 Answers4

17

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>
Raúl Otaño
  • 4,640
  • 3
  • 31
  • 65
  • How do I bind to the command defined in the root of the viewmodel. So far the FindAncestor method is not working. Can you please show me an example of this? – TrustyCoder Nov 16 '12 at 22:06
  • In that cases where i cannot access easly to the view model for make the binding, i try with this FindAncestor binding: Command="{Binding DataContext.Command, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MainWindow}}}", generaly the main window's datacontext has the root view model, and there is the command you want. – Raúl Otaño Nov 19 '12 at 17:21
  • That does not work with Context menu for some reason because it is not a part of the visual tree. – TrustyCoder Nov 19 '12 at 17:29
  • Yes you are right, then you should raise the command from your view model. Maybe using a kind of messages (mvvm light or implemented by your self) or raise events for all your view model hierarchy, this is a complex way of do it. – Raúl Otaño Nov 19 '12 at 18:06
  • Yes for even small things like this XAML is a real pain. I have to come up with solutions that beyond normal. – TrustyCoder Nov 19 '12 at 18:09
6

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">
Contango
  • 76,540
  • 58
  • 260
  • 305
3
<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

Jack
  • 174
  • 2
  • 9
  • This worked great for me, and, I like how the ContextMenu is defined close to the Item. – John Jesus Jul 03 '14 at 16:18
  • 1
    This worked for me. To avoid a compile error, I had to swap the blocks around (ensure the XAML tags `HierarchicalDataTemplate` come after the `Style` and `ContextMenu` tags). The reason it works is because shifting the ContextMenu into a style shifts everything into a more closely related visual tree, which means the DataContext can be found without any trouble. – Contango Feb 03 '15 at 22:45
3

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.

TrustyCoder
  • 4,749
  • 10
  • 66
  • 119