Search code examples
c#wpfmvvmcontextmenuitemtemplate

How to bind command to ancestor datacontext? WPF :: MVVM


I have a ContextMenu and a button, inside a TabControl, I got the button Command to work correctly, but couldn't figure out how to bind the Context menu items commands. Could you point out what I'm doing wrong?

Note: Both commands CloseTabCommandand CloseAllTabsCommand, are working fine when binding them to the button.

Xaml code:

<TabControl ItemsSource="{Binding TabItems}">
                <TabControl.ItemTemplate>
                    <DataTemplate>
                        <DockPanel Width="120" ToolTip="{Binding HeaderText}">
                            <DockPanel.ContextMenu>
                                <ContextMenu>
                                    <MenuItem Header="Close Tab"
                                              Command="{Binding DataContext.CloseTabCommand, RelativeSource={RelativeSource AncestorType=TabControl}}"
                                              CommandParameter="{Binding ItemId}" />
                                    <MenuItem Header="Close All Tabs"
                                              Command="{Binding DataContext.CloseAllTabsCommand, RelativeSource={RelativeSource AncestorType=TabControl}}" />
                                </ContextMenu>
                            </DockPanel.ContextMenu>
                            <Button
                                Command="{Binding DataContext.CloseTabCommand, RelativeSource={RelativeSource AncestorType=TabControl}}"
                                CommandParameter="{Binding ItemId}"
                                Content="X"
                                Cursor="Hand"
                                DockPanel.Dock="Right"
                                Focusable="False"
                                FontFamily="Courier"
                                FontWeight="Bold"
                                FontSize="10"
                                VerticalContentAlignment="Center"
                                Width="15" Height="15" />
                            <ContentPresenter Content="{Binding HeaderText}" VerticalAlignment="Center" />
                        </DockPanel>
                    </DataTemplate>
                </TabControl.ItemTemplate>
                <TabControl.ItemContainerStyle>
                    <Style TargetType="TabItem">
                        <Setter Property="IsSelected" Value="{Binding IsSelected}" />
                    </Style>
                </TabControl.ItemContainerStyle>
            </TabControl>

ViewModel code:

private ObservableCollection<TabItemViewModel> _tabItems;
        public ObservableCollection<TabItemViewModel> TabItems {
            // if _tabItems is null initiate object.
            get { return _tabItems; }
            set { SetProperty(ref _tabItems, value); }
        }

Edit:

Binding to a Command declared in TabItemViewModel(TabControl ItemsSource) class works fine. but I want to bind commands to ViewModel for current UserControl


Solution

  • Bind the Tag property of the DockPanel to the view model and then bind the Command property of the MenuItem to the PlacementTarget of the ContextMenu:

    <DockPanel Width="120" ToolTip="{Binding HeaderText}"
               Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=TabControl}}">
        <DockPanel.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Close Tab"
                          Command="{Binding PlacementTarget.Tag.CloseTabCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
                          CommandParameter="{Binding ItemId}" />
                ...
    

    A ContextMenu resides in its own visual tree and this is why you can't use a RelativeSource to the bind to the parent TabControl as there is no parent TabControl further up in the visual tree.