Search code examples
c#wpfdata-bindingmvvmprism-4

Access ViewModel / DataConext inside ContextMenu


How can I get the original DataContext of the UserControl inside of a ContextMenu.

The code below, you can see that there is a Button in the DataTemplate, which binds correctly. However, when trying to bind the datasource of the contextmenu, I recieve the following error:

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

What do I need to do to allow the ContextMenu to bind to the ViewModel?

===============================================================================

The ViewModel is assigned to the datacontext of the view in the codebehind:

View:

<TreeView ItemsSource="{Binding Clients}"
          cmd:TreeViewSelect.Command="{Binding SelectionChangedCommand}"
          cmd:TreeViewSelect.CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=SelectedItem}">
    <TreeView.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Name}">
                    <TextBlock.ContextMenu>
                        <ContextMenu DataContext="{Binding DataContext, 
                            RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeView}}}">
                            <MenuItem Header="{Binding TestString}" />
                        </ContextMenu>
                    </TextBlock.ContextMenu>
                </TextBlock>

                <Button  DataContext="{Binding DataContext, 
                            RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeView}}}"
                         Content="{Binding TestString}" Command="{Binding EditSelectedClientCommand}" />
             </StackPanel>
        </DataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

ViewModel:

public class ClientListViewModel : ViewModelBase
{
    public String TestString { 
        get {
            return "TESTING";  
        }
    }

    private ClientList _clients = null;
    private readonly IClientService _clientService = null;
    private readonly IEventAggregator _eventAggregator = null;
    private Client _selectedClient = null;
    private ICommand _selectionChangedCommand = null;
    private ICommand _editSelectedClientCommand = null;
    ....
}

Solution

  • ContextMenus do not appear in the visual tree which causes RelativeSource-bindings to fail, you can still get the DataContext one way or another though. You could try this for example:

    <TextBlock Text="{Binding Name}"
               Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=TreeView}}">
        <TextBlock.ContextMenu>
            <ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
                <MenuItem Header="{Binding TestString}" />
                <!-- ... --->
    

    The PlacementTarget is the TextBlock, and the DataContext is tunneled through the Tag. Just one way to do this (at least i hope it works), i have also seen some libraries which bridge this gap differently but i do not recall their origin...