简体   繁体   中英

Binding a context menu in a WPF MVVM project

I am struggling to understand where I'm going wrong with my MVVM WPF project. I am trying to create a custom contextmenu command. I have a workspace, which is a view, within a HCC in my MainWindow. The workspace (ProductRecordView) contains a listview populated by an observable collection by the viewmodel (ProductRecordViewModel). The data context is set by Option 8 , in the MainWindowResources XAML. So I have created the contextmenu in the PRV as follows:

<ListView.ContextMenu>
  <ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
    <MenuItem Header="Delete" Command="{Binding Path=DeleteRecord}"/>
  </ContextMenu>
</ListView.ContextMenu>

and in the PRVM I have the following things:

#region Presentation Properties

public ICommand DeleteRecord
{
    get
    {
        if (_deleteMe == null)
        {
            _deleteMe = new RelayCommand(
                param => this.Delete(),
                param => this.CanDelete
                );
        }

        return _deleteMe;
    }
}

#endregion Presentation Properties

#region Public Methods

public void Delete()
{
    MessageBox.Show("Test Execute");//this is just for testing the binding of Delete
}

#endregion Public Methods

#region Private Helpers

bool CanDelete = false; //this is just for testing the binding of CanDelete

#endregion Private Helpers

When I run the application and right click on an item in the list view, I am presented with the context menu as described in the first snippet, however it is not binding as intended as neither of the bound outcomes occur. How do I properly bind in this case, what am I doing wrong?

The DataContext of ProductRecordView is set in MainWindowResources as follows:

  <DataTemplate DataType="{x:Type vm:ProductRecordViewModel}">
  <vw:ProductRecordView />
</DataTemplate>

I know that this DataContext is working as intended as if I try to create a button in the view and assign a data binding to it, the binding works perfectly and executes the command in the ProductRecordViewModel

Let's look at the error message more precisely.

BindingExpression path error: 'DeleteRecord' property not found on 'object' ''ListCollectionView' (HashCode=12713695)'. BindingExpression:Path=DeleteRecord; DataItem='ListCollectionView' (HashCode=12713695); target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')

This states, that the data binding system tries to find a DeleteRecord property in the MenuItem 's DataContext which is of type ListCollectionView .

So, considering your expression

DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}

it's clear that the PlacementTarget.DataContext is of type ListCollectionView too. Since PlacementTarget is the ListView itself, it's obvious that the ListView 's DataContext is this ListCollectionView object.

I suppose, you define your ListView 's DataContext somehow like this:

<ListView DataContext="{Binding Items}" ItemsSource="{Binding}"/>

The better solution would be to leave the ListView 's DataContext alone so that it "inherits" it's value from your UserControl (thus will be of type ProductRecordViewModel ) while changing the binding for the ItemsSource :

<ListView ItemsSource="{Binding Items}"/>

Why is the ProductRecordView in a datatemplate? I get the suspicion that the list items are PRV which is each bound to a PRVM - is that correct? Something like this

<ListView>
    <ListView.ContextMenu>
            <ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
                <MenuItem Header="Delete" Command="{Binding Path=DeleteRecord}"/>
            </ContextMenu>
        </ListView.ContextMenu>
    <ListView.ItemTemplate>
        <DataTemplate DataType="{x:Type vm:ProductRecordViewModel}">
            <vw:ProductRecordView /> <!-- each PRV is bound to a PRVM - which as the delete record command -->
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

If that is the case, then the problem is that datacontext of the listviewitems is not the same as the listview itself. Instead you probably want the context menu on the listviewitems directly:

<ListView>        
        <ListView.ItemTemplate>
            <DataTemplate DataType="{x:Type vm:ProductRecordViewModel}">
                <vw:ProductRecordView>
                    <vw:ProductRecordView.ContextMenu>
                        <ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
                            <MenuItem Header="Delete" Command="{Binding Path=DeleteRecord}"/>
                        </ContextMenu>
                    <vw:ProductRecordView.ContextMenu>
                </vw:vw:ProductRecordView>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM