3
votes

I have populated a ListView with objects and I have bound a ContextMenu to those items in my ListView. The ContextMenu can only be opened by clicking on an item. The problem is that Caliburn Micro throws an error that it can't find a target method for ShowProperties().

I think this problem occurs because Caliburn doesn't have the correct DataContext of the ViewModel available. I've tried many solutions on Stackoverflow to make the ViewModel available to the ContextMenu item but to no avail e.g.:

WPF: Binding a ContextMenu to an MVVM Command

“No target found for method” thrown by Caliburn Message.Attach()

WPF Context Menus in Caliburn Micro

This is the XAML code of my view:

<Window x:Class="CueMaster.Views.AppView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:dragDrop="clr-namespace:GongSolutions.Wpf.DragDrop;assembly=GongSolutions.Wpf.DragDrop"
         xmlns:cal="http://www.caliburnproject.org"
         Height="500" Width="800">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="200" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

    <ListView Grid.Column="1" Margin="5"
        ItemsSource="{Binding Cues}" 
        dragDrop:DragDrop.IsDragSource="True" 
        dragDrop:DragDrop.IsDropTarget="True"
        dragDrop:DragDrop.DropHandler="{Binding}">

        <ListView.Resources>
            <ContextMenu x:Key="ItemContextMenu">
                <MenuItem Header="Properties" cal:Message.Attach="ShowProperties($dataContext)" Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}">
                    <MenuItem.Icon>
                        <Image Source="../PropertyIcon.png" />
                    </MenuItem.Icon>
                </MenuItem>
            </ContextMenu>
        </ListView.Resources>

        <ListView.ItemContainerStyle>
            <Style TargetType="{x:Type ListViewItem}" >
                <Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}" />
            </Style>
        </ListView.ItemContainerStyle>

        <ListView.View>
            <GridView >
                <GridViewColumn Width="70" Header="Cue"  DisplayMemberBinding="{Binding Id}" />
                <GridViewColumn Width="100" Header="Name"  DisplayMemberBinding="{Binding Name}" />
                <GridViewColumn Width="100" Header="Description"  DisplayMemberBinding="{Binding Description}" />
                <GridViewColumn Width="70" Header="Duration"  DisplayMemberBinding="{Binding Duration}" />
                <GridViewColumn Width="70" Header="Elapsed"  DisplayMemberBinding="{Binding Elapsed}" />
                <GridViewColumn Width="70" Header="Remaining"  DisplayMemberBinding="{Binding Remaining}" />
            </GridView>
        </ListView.View>
    </ListView>
</Grid>

What did I miss?

2

2 Answers

6
votes

Your overriding what CM will do by placing the Command binding. Since the visual tree has no idea the context menu exists let alone the datacontext that is the purpose behind.

<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
    <Setter Property="Tag" Value="{Binding Path=DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}"/>
    <Setter Property="ContextMenu">
        <Setter.Value>
            <ContextMenu cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
               <MenuItem Header="Properties"  cal:Message.Attach="ShowProperties($dataContext)" >
                <MenuItem.Icon>
                    <Image Source="../PropertyIcon.png" />
                </MenuItem.Icon>
            </MenuItem>
            </ContextMenu>
        </Setter.Value>
    </Setter>
</Style>
</ListView.ItemContainerStyle>

while I understand what you are trying to do with the Resources in the ListView you are shooting yourself in the foot with the Command binding. Drop the resource give the ItemContainerStyle a roll and see if it works. You can always break this out into a resource later. for testing purposes to see if it works try the internal style for now.

1
votes

Using mvermef's answer, I got it to work. The only thing that needed to be changed in his code is that the binding meant "Travel up the Visual Tree, find the first ContextMenu object above this one, and bind to the PlacementTarget.Tag property". The problem with that is that the binding is on the ContextMenu itself, so there is no parent ContextMenu object. Using RelativeSource Self fixes this.

<ListView.ItemContainerStyle>
            <Style TargetType="{x:Type ListViewItem}">
                <Setter Property="Tag" Value="{Binding Path=DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}"/>
                <Setter Property="ContextMenu">
                    <Setter.Value>
                        <ContextMenu cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
                            <MenuItem Header="Properties" cal:Message.Attach="ShowProperties($dataContext)" >
                                <MenuItem.Icon>
                                    <Image Source="../PropertyIcon.png" />
                                </MenuItem.Icon>
                            </MenuItem>
                        </ContextMenu>
                    </Setter.Value>
                </Setter>
            </Style>
</ListView.ItemContainerStyle>