I have a UserControl which contains a ContentControl. The ContentControl contains a ContextMenu. I want to set the DataContext of the ContextMenu to a property in the UserControl's viewmodel. The property in this case is another viewmodel.

Below is the .xaml for my user control:

<UserControl x:Class="TestProj.MainWindow"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:views="clr-namespace:OnyxWPF.Views"
             mc:Ignorable="d">
    <UserControl.Resources>
        <BooleanToVisibilityConverter x:Key="BoolToVis" />
    </UserControl.Resources>
    <Grid>
        <ContentControl Content="{Binding MainMap}">
            <ContentControl.ContextMenu>
                <ContextMenu DataContext="{Binding ContextMenuViewModel}" Visibility="{Binding IsPopUpEnabled, Converter={StaticResource BoolToVis}}" >
                    <MenuItem Header="Pan" Command="{Binding SetPanModeCmd}">
                        <MenuItem.Icon>
                            <Image Source="Images/panHand.ico" />
                        </MenuItem.Icon>
                    </MenuItem>
            </ContentControl.ContextMenu>
        </ContentControl>
    </Grid>
</UserControl>

Here's the code behind for the user control:

public partial class MainWindow : UserControl
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MainViewModel();
    }
}

The MainViewModel - this is the DataContext for the above UserControl:

public class MainViewModel
{
    public WpfMap MainMap { get; set; } //Content control's context is bound to this

    public ContextMenuViewModel ContextMenuViewModel { get; set; } //this should be the context menu's datacontext

    public bool IsPopUpEnabled { get; set; } //determines if the contextmenu will appear or not

    public MainViewModel()
    {
        MainMap = new WpfMap();
        ContextMenuViewModel = new ContextMenuViewModel();
        IsPopUpEnabled = true;
    }
}

Finally, here's the viewmodel for my contextmenu:

public class ContextMenuViewModel
{
    public RelayCommand SetPanModeCmd { get; set; }

    public ContextMenuViewModel()
    {
        SetPanModeCmd = new RelayCommand(SetPanMode);
    }

    private void SetPanMode()
    {
        //stuff
    }
}

What I have in the xaml for the ContextMenu above does not work. I've tried a few different variations using RelativeSource but with no luck. Snippets of what I've tried below:

<ContextMenu DataContext="{Binding Path=Parent.DataContext.ContextMenuViewModel, RelativeSource={RelativeSource Self}}"

<ContextMenu DataContext="{Binding DataContext.ContextMenuViewModel, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}"

<ContextMenu DataContext="{Binding ContextMenuViewModel, RelativeSource={RelativeSource AncestorType=ContentControl}}"

<ContextMenu DataContext="{Binding DataContext.ContextMenuViewModel, RelativeSource={RelativeSource AncestorType=UserControl}}"

None of the above have worked.

1 Answers

1
Community On

Any UIElement from the visual tree inherits parent's DataContext. In that case ContextMenu has MainViewModel as a DataContext after you assigned it to parent ContentControl. So, it means that you can get acces to any of properties of MainViewModel including ContextMenuViewModel. By the way, WPF data binding supports any number of nested properties. And you XAML will look like this:

    <UserControl x:Class="TestProj.MainWindow"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                 xmlns:views="clr-namespace:OnyxWPF.Views"
                 mc:Ignorable="d">
         <UserControl.Resources>
             <BooleanToVisibilityConverter x:Key="BoolToVis" />
         </UserControl.Resources>
         <Grid>
             <ContentControl Content="{Binding MainMap}">
                 <ContentControl.ContextMenu>
                     <ContextMenu Visibility="{Binding IsPopUpEnabled, Converter={StaticResource BoolToVis}}">
                         <MenuItem Header="Pan" 
                                   Command="{Binding ContextMenuViewModel.SetPanModeCmd}">
                             <MenuItem.Icon>
                                 <Image Source="Images/panHand.ico" />
                             </MenuItem.Icon>
                         </MenuItem>
                     </ContextMenu>
                 </ContentControl.ContextMenu>
             </ContentControl>
         </Grid>
     </UserControl>

P.S. In order to clarify: Command="{Binding ContextMenuViewModel.SetPanModeCmd}" - ContextMenuViewModel is a name of MainViewModel's property, not of the Type.