ContextMenu not working

Jan 16, 2011 at 4:54 AM

I read all the threads related to context menu and ended up with the following 

 

 

<ScrollViewer>
    <StackPanel x:Name="LayoutRoot" Background="Transparent">
        <ItemsControl x:Name="Notes" Margin="0,0,-12,0" >
            <ItemsControl.ItemTemplate >
                <DataTemplate>
                    <StackPanel Margin="0,0,0,17" Width="432" cal:Message.Attach="[Event MouseLeftButtonUp] = [Action GoToNote($datacontext)]" >
                        <Border BorderThickness="1">
                            <toolkit:ContextMenuService.ContextMenu>
                                <toolkit:ContextMenu cal:Action.TargetWithoutContext="{Binding}">
<toolkit:MenuItem Header="Delete" cal:Message.Attach="[Event Click] = [Action DeleteNote()]" /> </toolkit:ContextMenu> </toolkit:ContextMenuService.ContextMenu> <TextBlock Text="{Binding Name}" TextWrapping="NoWrap" Style="{StaticResource PhoneTextExtraLargeStyle}" /> </Border> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </StackPanel> </ScrollViewer>

 

I also have the following in my bootstrapper

ConventionManager.AddElementConvention<MenuItem>(MenuItem.ItemsSourceProperty, "DataContext", "Click");

When I click on the context menu, I get an exception:

System.Exception was unhandled  Message=No target found for method DeleteNote.  StackTrace:       at Caliburn.Micro.ActionMessage.Invoke(Object eventArgs)       at System.Windows.Interactivity.TriggerAction.CallInvoke(Object parameter)       at System.Windows.Interactivity.TriggerBase.InvokeActions(Object parameter)       at System.Windows.Interactivity.EventTriggerBase.OnEvent(EventArgs eventArgs)       at System.Windows.Interactivity.EventTriggerBase.OnEventImpl(Object sender, EventArgs eventArgs)       at Microsoft.Phone.Controls.MenuItem.OnClick()       at Microsoft.Phone.Controls.MenuItem.OnMouseLeftButtonUp(MouseButtonEventArgs e)       at System.Windows.Controls.Control.OnMouseLeftButtonUp(Control ctrl, EventArgs e)       at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, String eventName)       at Microsoft.Xna.Framework.Input.UnsafeNativeMethods.CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam)       at Microsoft.Xna.Framework.Input.SafeNativeMethods.CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam)       at Microsoft.Xna.Framework.Input.WindowMessageHooker.Hook.WndProc(IntPtr msgWnd, UInt32 msg, IntPtr wParam, IntPtr lParam)

I have the action defined as

public void DeleteNote(){ }

on my ViewModel.

Any idea why it cannot find DeleteNote?

View Name = NotesView

ViewModel = NotesViewModel

NotesView is a user control which is activated inside a page called MainPage.  I tried creating DeleteNote method on the MainPageViewModel and same thing, the framework cannot find it.

Jan 16, 2011 at 9:15 AM

Inside the template, the DataContext refers to each individual Note VM, so the expression cal:Action.TargetWithoutContext="{Binding}" sets the single Note VM as the target of the DeleteNote action.
Is it what you intended, or does the DeleteNote method live at the level of the parent VM, which hold the Notes collection? I cannot figure out for sure from the class definitions you reported.

 

 

Jan 16, 2011 at 12:52 PM
DeleteNote action lives in the parent VM that contains the list of notes. VM name is NotesViewModel.

Sent from my Windows Phone

From: marcoamendola
Sent: Sunday, January 16, 2011 5:15 AM
To: eibrahim@gmail.com
Subject: Re: ContextMenu not working [caliburnmicro:241956]

From: marcoamendola

Inside the template, the DataContext refers to each individual Note VM, so the expression cal:Action.TargetWithoutContext="{Binding}" sets the single Note VM as the target of the DeleteNote action.
Is it what you intended, or does the DeleteNote method live at the level of the parent VM, which hold the Notes collection? I cannot figure out for sure from the class definitions you reported.

Jan 16, 2011 at 1:54 PM

I figured it out using ElementName and Path.  Thanks for your help.

<StackPanel x:Name="LayoutRoot" Background="Transparent">
    <ItemsControl x:Name="Notes" Margin="0,0,-12,0" >
        <ItemsControl.ItemTemplate >
            <DataTemplate>
                <StackPanel Margin="0,0,0,17" Width="432" cal:Message.Attach="[Event MouseLeftButtonUp] = [Action GoToNote($datacontext)]" >
                    <Border BorderThickness="1">
                                
                        <TextBlock Text="{Binding Name}" TextWrapping="NoWrap"
                                    Style="{StaticResource PhoneTextExtraLargeStyle}" 
                                    />
                    </Border>
                    <toolkit:ContextMenuService.ContextMenu>
                        <toolkit:ContextMenu cal:Action.TargetWithoutContext="{Binding ElementName=LayoutRoot, Path=DataContext}">
                            <toolkit:MenuItem Header="Delete" cal:Message.Attach="[Event Click] = [Action Delete()]" />
                        </toolkit:ContextMenu>
                    </toolkit:ContextMenuService.ContextMenu>
                </StackPanel>
                  
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</StackPanel>

Jan 16, 2011 at 3:25 PM

It was just a clue, actually :-)
Glad it helped.

Mar 9, 2011 at 11:54 AM

I have the same issue that you had, so I'm using your solution in the following code:

<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneBackgroundBrush}">
        <ListBox x:Name="Favorites">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Button Margin="6" Padding="0" BorderThickness="0" framework:Message.Attach="ShowFavoriteProfile($datacontext)">
                        <toolkit:ContextMenuService.ContextMenu>
                            <toolkit:ContextMenu framework:Action.TargetWithoutContext="{Binding ElementName=LayoutRoot, Path=DataContext}">
                                <toolkit:MenuItem Header="remove favorite" framework:Message.Attach="RemoveFavorite($datacontext)" />
                            </toolkit:ContextMenu>
                        </toolkit:ContextMenuService.ContextMenu>
                        <Image Source="{Binding Picture}" Stretch="UniformToFill" Width="173" Height="173" />
                    </Button>
                </DataTemplate>
            </ListBox.ItemTemplate>
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <toolkit:WrapPanel Orientation="Horizontal" />
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
        </ListBox>
    </Grid>

The "ShowFavoriteProfile($datacontext)" on the button click works perfectly, but the "RemoveFavorite($datacontext)" on the MenuItem click doesn't!

By what I can see, the problem is that the MenuItem points to the item's DataContext and not the root DataContext, although I specifically set the TargetWithoutContext property to find the LayoutRoot's DataContext...

Any thoughts over this one?

Mar 9, 2011 at 6:54 PM

ContextMenu is a strange beast even in WPF; I really can't remember if in SL3 (which WP7 is based upon) there is something preventing CM to correcly bubble the actions to the Action.Target.
If you can build a small repro I could try to help you diagnose the issue.

As a workaround, you might provide a (let's say) RemoveFromParent method in the ViewModel representing the Favourite, then change the Message.Attach accordingly.
RemoveFromParent has only to delegate to its parent passing its instance.

May 23, 2011 at 8:33 PM
Edited May 23, 2011 at 8:40 PM

Sorry. Maybe you know how to solve my problem.  I have the same case as author of this topics but in additional I also have ItemsPanel with WrapPanel (actually it does't matter, it also can be a StackPanel).
I also try set:

 <ContextMenu caliburn:Action.TargetWithoutContext="{Binding}" />

or

caliburn:Action.TargetWithoutContext="{Binding ElementName=Page, Path=DataContext}"

 
It doesn't work it both cases.  Binding always equal to element of My List<T> and of course ContextMenu can't found event handler.

 

Without this code everything works correct:

<ListBox.ItemsPanel>

     <ItemsPanelTemplate>

          <toolkit:WrapPanel IsItemsHost="True" />

     </ItemsPanelTemplate>

</ListBox.ItemsPanel>       

 

Any idea how I can receive access to ViewModel binding (with WrapPanel)? 

Thanks

May 31, 2011 at 11:18 PM

eibrahim --- Thanks dude, your solution perfectly solved my problem

Dec 29, 2011 at 9:29 PM

You could try something like:

<Grid x:Name="LayoutRoot" >
    <ListBox>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Tag="{Binding DataContext, ElementName=LayoutRoot}" Text="{Binding DisplayName}">
                    <TextBlock.ContextMenu>
                        <ContextMenu cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
                            <MenuItem Header="Properties..." cal:Message.Attach="ShowProperties($datacontext)" />
                        </ContextMenu>
                    </TextBlock.ContextMenu>
                </TextBlock>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

The Tag property of the ContextMenu owner (in this example, the TextBlock) is used to 'store' the viewmodel of the view.

The ContextMenu owner is available to the ContextMenu via the PlacementTarget property, i.e. . ContextMenuService will set PlacementTarget to the owner of the ContextMenu when the ContextMenu opens.

This then makes it possible to reference the viewmodel from the ContextMenu, and thus set the viewmodel as the target for ContextMenu actions. 

Apr 11, 2013 at 10:11 AM
@badscooter Thanks your approach worked for me!
May 8, 2015 at 6:57 AM
@badscooter's approach helped me as well, thank you!