18

I have a ListView which displays a list of string values. I want to add a context menu entry for each item in the list to remove the selected item. My XAML looks like this:

<ListView x:Name="itemsListView" ItemsSource="{Binding MyItems}">
  <ListView.ContextMenu>
    <ContextMenu>
      <MenuItem Header="Remove"
                Command="{Binding RemoveItem}"
                CommandParameter="{Binding ElementName=itemsListView, Path=SelectedItem}" />
    </ContextMenu>
  </ListView.ContextMenu>
</ListView>

The problem is that the CommandParameter value is always null. I've added an additional button to remove the selected item to check if my command works. The button has exactly the same binding and removing items via the button works. The button looks like this:

<Button Content="Remove selected item"
        Command="{Binding RemoveItem}"
        CommandParameter="{Binding ElementName=itemsListView, Path=SelectedItem}"/>

The command looks like this:

private ICommand _removeItem;

public ICommand RemoveItem
{
  get { return _removeItem ?? (_removeItem = new RelayCommand(p => RemoveItemCommand((string)p))); }
}

private void RemoveItemCommand(string item)
{
  if(!string.IsNullOrEmpty(item))
    MyItems.Remove(item);  

}

Any ideas why the selected item is null when opening the context menu? Maybe a focus problem of the listview?

M.E.
  • 2,759
  • 3
  • 24
  • 33
  • 1
    Have a look in your Output window, I bet you'll find a binding error message. Since a ContextMenu is a new window, I'm not sure it can access itemsListView. – Vivien Ruiz Jun 18 '12 at 11:58
  • Have a look around http://stackoverflow.com/questions/1013558/elementname-binding-from-menuitem-in-contextmenu and http://stackoverflow.com/questions/2617122/wpf-menuitem-command-binding-to-elementname-results-to-system-windows-data-error – Vivien Ruiz Jun 18 '12 at 12:01

3 Answers3

46

H.B. is right. but you can also use RelativeSource Binding

    <ListView x:Name="itemsListView" ItemsSource="{Binding MyItems}">
        <ListView.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Remove"
            Command="{Binding RemoveItem}"
            CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}" />
            </ContextMenu>
        </ListView.ContextMenu>
    </ListView>
blindmeis
  • 22,175
  • 7
  • 55
  • 74
  • Worked with the RelativeSource - Thanks! – M.E. Jun 18 '12 at 12:16
  • Note that this doesn't work with multiple selections (SelectedItem won't necessarily point to the element you actually press the mouse over) or if you disable selection on right click (SelectedItem will be null). – Gábor Apr 07 '19 at 13:15
4

ContextMenus are disconnected, you cannot use ElementName bindings. One workaround would be using Binding.Source and x:Reference which requires you to extract parts that use it to be in the resources (due to cyclical dependency errors). You can just put the whole context menu there.

An example:

<ListBox Name="lb" Height="200">
    <ListBox.Resources>
        <ContextMenu x:Key="cm">
            <MenuItem Header="{Binding ActualHeight, Source={x:Reference lb}}" />
        </ContextMenu>
    </ListBox.Resources>
    <ListBox.ContextMenu>
        <StaticResource ResourceKey="cm" />
    </ListBox.ContextMenu>
</ListBox>
H.B.
  • 166,899
  • 29
  • 327
  • 400
1

This work for me CommandParameter="{Binding}"

Vivek Malik
  • 165
  • 1
  • 10