21

I try to do something like this:

<DataGrid Name="myGrid" ItemSource="{Binding Path=MyCollection}">
   <DataGrid.ContextMenu>
       <ContextMenu>
          <MenuItem 
              Command="{Binding RemoveRow}" 
              CommandParameter="{Binding ElementName=myGrid, Path=SelectedItem}">
       </ContextMenu>
   </DataGridContextMenu>
</DataGrid>

but I got null always (I tried also SelectedIndex and SelectedValue)

if I pass the following parameter to the execution code, it works:

<MenuItem Command="{Binding RemoveRow}" CommandParameter="1">
Mantosh Kumar
  • 5,659
  • 3
  • 24
  • 48
Maya
  • 989
  • 4
  • 12
  • 19

2 Answers2

23

Try something like this in your CommandParameter,

<DataGrid.ContextMenu>
     <ContextMenu>
           <MenuItem Header="MyHeader" 
                     Command="{Binding MyCommand}"
                     CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.SelectedItem}" />
</DataGrid.ContextMenu>

I already tested it and it should work.

Rayan Elmakki
  • 1,144
  • 13
  • 7
18

It doesn't work because the ContextMenu is not part of the visual or logical tree of the DataGrid, so it doesn't inherit the DataContext.

As far as I know, there is know easy solution to this problem using only the built-in binding system. However, using a simple "proxy" class as explained here, you can work around this problem:

<DataGrid Name="myGrid" ItemSource="{Binding Path=MyCollection}">
   <DataGrid.Resources>
       <local:BindingProxy x:Key="proxy" Data="{Binding}" />
   </DataGrid.Resources>
   <DataGrid.ContextMenu>
       <ContextMenu>
          <MenuItem 
              Command="{Binding Data.RemoveRow, Source={StaticResource proxy}}" 
              CommandParameter="{Binding ElementName=myGrid, Path=SelectedItem}">
       </ContextMenu>
   </DataGridContextMenu>
</DataGrid>

However you still have a problem: ElementName=myGrid doesn't work (again, because ContextMenu isn't in the visual or logical tree of the DataGrid, so it's not in the same name scope). A simple solution is to bind the SelectedItem of the DataGrid to a property of the ViewModel, and use that property instead of the command parameter:

<DataGrid Name="myGrid" ItemSource="{Binding Path=MyCollection}"
          SelectedItem="{Binding SelectedItem}">
   <DataGrid.Resources>
       <local:BindingProxy x:Key="proxy" Data="{Binding}" />
   </DataGrid.Resources>
   <DataGrid.ContextMenu>
       <ContextMenu>
          <MenuItem 
              Command="{Binding Data.RemoveRow, Source={StaticResource proxy}}">
       </ContextMenu>
   </DataGridContextMenu>
</DataGrid>
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • Nice answer! i did not know this issue. but explainme plaese if its like this why when i clicked the menu item the RemoveRow is actually called even if the ContextMenu is no part of the tree? – Maya Nov 17 '11 at 07:30
  • what do you mean "yes, that's right"? i didnt understand why Command="{Binding RemoveRow}" working if the ContextMenu dont know about th DataContext? can you explain me this please? – Maya Nov 17 '11 at 08:53
  • OK, I misunderstood your question, I thought you were asking for confirmation... You can find more details in [this article](http://www.codeproject.com/KB/WPF/ContextMenuDataContext.aspx) – Thomas Levesque Nov 17 '11 at 09:21