0

I have a WPF ItemsControl where I want to be able to delete items via a context menu but I can't get the information which item the user right-clicked on to the relevant handler in the view model (meaning the object I set as the DataContext of my view)

So here is my ItemsControl:

<ItemsControl Grid.Row="1"
                ItemsSource="{Binding CurrentComponents, Converter={StaticResource EcComponentsToTrackComponents}, ConverterParameter=Video}"
                dd:DragDrop.IsDragSource="True"
                ItemTemplate="{StaticResource EcComponentPanelTemplate}"
                ItemContainerStyle="{StaticResource EcComponentPanelStyle}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <controls:TrackPanel MaxTime="{Binding MaxTime}"
                                    MinTime="{Binding MinTime}" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Delete"
                        Command="{Binding DeleteComponentCM}"
                        CommandParameter="{Binding }">
            </MenuItem>
        </ContextMenu>
    </ItemsControl.ContextMenu>
</ItemsControl>

Now to the ViewModel The ItemSource binds sucessfully to:

public IEnumerable<EcComponent> CurrentComponents => _currentProgram?.EcComponents;

The Binding to the DeleteComponentCM RelayCommand already works

public RelayCommand<object> DeleteComponentCM { get; private set; }
...
DeleteComponentCM = new RelayCommand<object>(deleteComponentCM, () => !IsRunning);
...
private void deleteComponentCM(object component)
{
    if (component is EcComponent ecEomponent)
    {
        _currentProgram.EcComponents.Remove(ecEomponent);
        RaisePropertyChanged(nameof(CurrentComponents));
    }
}

So if I put a breakpoint at if (component is EcComponent ecEomponent) and click on Delete in the context menu I will stop at this line but component now is of course the DataContext object itself (so basically this) when inside deleteComponentCM.

So my question is: What do I have to change about CommandParameter="{Binding }" to pass the item to deleteComponentCM where the user right clicked on to the open the context menu?

While researching this problem I found this: Bind Context Menu inside ItemsControl? but I could not get the solution there to work. Also this solution feels very hacky ... in bad way ... I least in my mind there should be a straight forward solution ... I mean when I have a context menu for items in an ItemsControl isn't it something very normal that I want to know on which item the user clicked on when handling commands from context menu items ???

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Moritz Nadler
  • 39
  • 1
  • 7
  • Consider setting the ContextMenu on the top level element of the ItemTemplate. Or perhaps in the ItemContainerStyle. – Clemens Feb 15 '22 at 13:26
  • I already tried to define the context menu in the DataTemplate. While I guess that CommandParameter="{Binding }" will then bind to the item, Command="{Binding DeleteComponentCM} does then not work anymore because DeleteComponentCM is defined in the ViewModel of the hole view (its DataContext) and not in the item class (EcComponent in my case). So I get this error in debug output during runtime: "'DeleteComponentCM' property not found on 'object' ''EcComponent'" In in this case the question would transform into: What to change about Command="{Binding DeleteComponentCM}" to get it to work? – Moritz Nadler Feb 16 '22 at 17:05
  • You would have to bind the Command to the property of the parent DataContext like `Command="{Binding DataContext.DeleteComponentCM, RelativeSource={RelativeSource AncestorType=ItemsControl}}"`. – Clemens Feb 16 '22 at 17:21
  • This does not work. After doing it, opening the context menu causes this error in the VS output: System.Windows.Data Error: 40 : BindingExpression path error: 'DeleteComponentCM' property not found on 'object' ''EcComponent' (HashCode=52840473)'. BindingExpression:Path=DataContext.DeleteComponentCM; DataItem='ContextMenu' (Name=''); target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand') – Moritz Nadler Feb 17 '22 at 08:59
  • Try an ElementName Binding instead of RelativeSource. The idea should be clear, you bind to a property of the DataContext of some parent element, e.g. the ItemsControl, or the MainWindow. – Clemens Feb 17 '22 at 09:02

0 Answers0