WPF Databinding ContextMenuItem's CommandParameter to TreeViewItem's DataContext

1.4k views Asked by At

I am using the Command pattern with (among other things) a context menu on a TreeViewItem, which uses HierarchicalDataTemplates. The MainWindowViewModel (a static resource for the Window) has properties that expose singleton objects that in turn have properties that represent the commands. I am able to execute the command just fine, but some of the commands need to pass the TreeViewItem's DataContext as the CommandParameter.

Here's a specific example: One node of the tree has the ItemsSource bound to an ObservableCollection of individual AnalysisMain objects. Each of the resulting subnodes has a ContextMenu (with a DataContext bound to the AnalysisController) which has (among others) a Remove MenuItem. The Remove MenuItem's Command property is bound to the CommandRemove command on the AnalysisController singleton object (and it executes just fine). But this also requires the CommandParameter to be bound to the AnalysisMain object that serves as the DataContext for the subnodes in the tree. I have tried using RelativeSource with the AncestorType set to TreeViewItem and the Path set to DataContext:

<HierarchicalDataTemplate DataType="{x:Type vmAnalysis:AnalysisMain}">
    <WrapPanel Orientation="Horizontal">
        <Image Source="Analysis\Icon_Analysis_Main_16_Normal.png" Margin="0,0,2,0" Width="16"/>
        <TextBlock Text="{Binding TreeViewTitle}">
            <TextBlock.ContextMenu>
                <ContextMenu DataContext="{StaticResource mainWindowViewModel}">
                    <MenuItem Header="Remove" Command="{Binding Path=AnalysisController.CommandRemove}"
                              CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}, AncestorLevel=4}, Path=DataContext}">
                    </MenuItem>
                </ContextMenu>
            </TextBlock.ContextMenu>
        </TextBlock>
    </WrapPanel>
</HierarchicalDataTemplate>

Without the AncestorLevel set, when I open the ContextMenu, I get the following in the Output window:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TreeViewItem', AncestorLevel='4''. BindingExpression:Path=DataContext; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'CommandParameter' (type 'Object')

I have tried several values for the AncestorLevel to no avail.

From examining the Visual Tree in Christian Mosers WPF Inspector, I don't see the context menu in the visual tree. Although the TreeViewItem shows the DataContext object. I just need a way to "navigate" to it in order to bind to it.


As an alternative, I have tried leaving the ContextMenu DataContext alone and setting the Command Binding's Source to point back to the AnalysisController. This also works for executing the command, but I am not able to bind to the TreeViewItem's DataContext for the CommandParameter:

<ContextMenu>
    <MenuItem Header="Remove" Command="{Binding Source={StaticResource mainWindowViewModel}, Path=AnalysisController.CommandRemove}"
              CommandParameter="{Binding Source={RelativeSource Self}, Path=DataContext}">
    </MenuItem>
</ContextMenu>

I have also tried just using CommandParameter="{Binding}", which also doesn't work. (In both cases, I just get null sent as the parameter. No warning / error is written to the Output window.) EDIT: For anyone else with this problem, the second option was doomed from the beginning, because I mistakenly put in Source={RelativeSource Self}, which would refer to the MenuItem and not the TreeViewItem. However, changing this with AncestorType / AncestorLevel makes no difference.

1

There are 1 answers

11
123 456 789 0 On BEST ANSWER

You have to use PlacementTarget property of the ContextMenu

<Setter Property="ContextMenu">
        <Setter.Value>
            <ContextMenu
        DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
            </ContextMenu>
        </Setter.Value>
 </Setter>

And on your MenuItem do a RelativeSource to ContextMenu then use PlacementTarget.DataContext as your binding to your CommandParameter