3

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?

Fabian Pas
  • 444
  • 5
  • 18

2 Answers2

6

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.

mvermef
  • 3,814
  • 1
  • 23
  • 36
  • Thanks, but unfortunately it still doesn't find the ShowProperties method, with or without $dataContext.. I do get the message that Tag of PlacementTarget can't be resolved in data context type of System.Windows.UIElement, anything that might lead on to that? – Fabian Pas Sep 09 '15 at 21:00
  • how is your project structured? separated assemblies? Container type (if any)? – mvermef Sep 09 '15 at 21:03
  • I have referenced the Caliburn.Micro and Caliburn.Micro.Platform packages using NuGet. I'm using the normal Caliburn structure; Views and ViewModels directories. The complete view XML is as the one I've posted. I'm not sure what you mean by container types. – Fabian Pas Sep 09 '15 at 21:07
  • I got it to work using your tip indeed. Instead I changed the RelativeSource of the ContextMenu to RelativeSource={RelativeSource Self}. I will post the full XAML in an answer. Thanks a lot! – Fabian Pas Sep 09 '15 at 21:16
  • @FabianPas perhaps if this answer helped you solve your problem, you could accept it as an answer? :) – Jamey Sep 16 '16 at 09:46
  • I just did! It's been a long time, haha. Thank you for the reminder! – Fabian Pas Sep 16 '16 at 11:55
1

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>
Fabian Pas
  • 444
  • 5
  • 18