0

I've got a viewmodel that contains a collection of objects and a bunch of commands.

public class MainWindowVM : NotifyPropertyChangedBase
{
    private CollectionViewSource employeeViewSource;

    private ICommand cmdOpenDetailEmployee;

    public MainWindowVM()
    {
        nsDataProviderEmployees = new NSDataProvider();

        employeeViewSource = new CollectionViewSource();

        cmdOpenDetailEmployee = new RelayCommand<object>((parameter) => {...});

        this.LoadData();
    }

    public CollectionViewSource EmployeeViewSource => employeeViewSource;
    public ICommand CmdOpenDetailEmployee => cmdOpenDetailEmployee;
}

In my application I want to use this command in a context menu of the datagrid showing the employees.

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MyApp.UI"
        xmlns:DataModel="clr-namespace:MyApp.DataModel;assembly=MyApp.DataModel" x:Class="MyApp.UI.MainWindow"
        xmlns:vm="clr-namespace:MyApp.UI.ViewModels"
        mc:Ignorable="d"
        Title="MyApp - Main" Height="751.826" Width="1111.005" Loaded="Window_Loaded" Icon="Resources/MyApp.ico">
    <Window.DataContext>
        <vm:MainWindowVM />
    </Window.DataContext>
    <Grid x:Name="grdMain">
        <DataGrid DataContext="{Binding Path=EmployeeViewSource}" x:Name="employeeDataGrid" EnableRowVirtualization="True" ItemsSource="{Binding}" Margin="10,77,10,0" RowDetailsVisibilityMode="VisibleWhenSelected">
            <DataGrid.ContextMenu>
                <ContextMenu DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">
                    <MenuItem Header="OpenDetail..."
                              Command="{Binding CmdOpenDetailEmployee}"
                              CommandParameter="{Binding}"/>
                </ContextMenu>
            </DataGrid.ContextMenu>
            <DataGrid.Columns>...</DataGrid.Columns>

        </DataGrid>
    </Grid>
</Window>

The Problem is I am unable to come up with a combination of bindings that both let me use the EmployeeViewSource property or my ViewModel as DataContext for the grid AND the CmdOpenDetailEmployee property of my ViewModel as the DataContext for my ContextMenu and MenuItems.

According to all the posts I've been able to find this should work but the command isn't executed when I click the menu item.

Kempeth
  • 1,856
  • 2
  • 22
  • 37
  • First, see whether the problem is actually with the binding or with the command. Check if you can find binding errors (see also here: https://stackoverflow.com/questions/8850143/binding-errors-not-showing-on-output-window). Also check in the debugger if the Execute method of your command is actually being invoked. From the question it is not really possible to tell more... –  Nov 13 '18 at 11:04
  • I retract the last sentence of my previous comment. :-) Come to think of it, it might be the binding using `FindAncestor`. Since the context menu is a popup window, it might not have the main window as an ancestor (i am not 100% sure here). The binding could very well just find the popup window element of the context menu (if there is such). Try using a binding with `ElementName` instead (you would need to give your main window a name, of course), and see what happens... –  Nov 13 '18 at 11:09
  • @elgonzo Here's there error from the Output `System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1''. BindingExpression:(no path); DataItem=null; target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')` The execute method of my command isn't called. And I haven't managed to get a binding with ElementName to work either: `Command="{Binding ElementName=mainWindow, Path=CmdOpenDetailEmployee}"` – Kempeth Nov 13 '18 at 12:26

1 Answers1

0

Bind to the DataContext of the PlacementTarget of the ContextMenu, and let the DataGrid inherit its DataContext from the window:

<DataGrid x:Name="employeeDataGrid" 
        EnableRowVirtualization="True"
        ItemsSource="{Binding EmployeeViewSource.View}" Margin="10,77,10,0" 
        RowDetailsVisibilityMode="VisibleWhenSelected">
    <DataGrid.ContextMenu>
        <ContextMenu>
            <MenuItem Header="OpenDetail..."
                              Command="{Binding PlacementTarget.DataContext.CmdOpenDetailEmployee, 
                                    RelativeSource={RelativeSource AncestorType=ContextMenu}}"
                              CommandParameter="{Binding}"/>
        </ContextMenu>
    </DataGrid.ContextMenu>
    <!--<DataGrid.Columns>...</DataGrid.Columns>-->
</DataGrid>
mm8
  • 163,881
  • 10
  • 57
  • 88
  • If I remove the DataContext from the DataGrid then I no longer get any data displayed... – Kempeth Nov 13 '18 at 12:15
  • Why are you defining a CollectionViewSource in the view model? Anyway, you could bind to EmployeeViewSource.View. See my edit. – mm8 Nov 13 '18 at 12:19
  • Legacy code which I'm trying to move towards MVVM. I'm pretty new to this myself. What would be a better approach? --- Either way: If I remove the DataGrid DataContext and bind ItemsSource to the View the grid works and I can bind the MenuItem straight to the command with `{Binding CmdOpenDetailEmployee}` Thanks! – Kempeth Nov 13 '18 at 12:31