1

Im trying to learn how to use a contextmenu. I get the menu and it looks good but the commands do not bind in the contextmenu. They do work in the visible button stack panel below the contextmenu. I get this in the output :

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=MainGrid'. BindingExpression:Path=DataContext.StartClientCommand; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')

 <ListBox x:Name="lbSlaves" Grid.Row="1"  
             ItemsSource="{Binding Slaves}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" >
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel  />
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Vertical" Width="150" >
                        <StackPanel Orientation="Horizontal">
                            <CheckBox  IsChecked="{Binding IsSelected ,Mode=TwoWay}"/>
                            <TextBlock Text="{Binding FriendlyName, Mode=OneWay}" >
                                <TextBlock.ContextMenu>
                                    <ContextMenu>
                                        <MenuItem Header="Start" 
                                                  Command="{Binding  ElementName=MainGrid, Path=DataContext.StartClientCommand}" 
                                                  CommandParameter="{Binding}" />
                                        <MenuItem Header="Stop" 
                                                  Command="{Binding  ElementName=MainGrid, Path=DataContext.StopClientCommand}" 
                                                  CommandParameter="{Binding}"  />
                                        <MenuItem Header="Calibrate" 
                                                  Command="{Binding  ElementName=MainGrid, Path=DataContext.CalibrateClientCommand}" 
                                                  CommandParameter="{Binding}" />
                                    </ContextMenu>
                                </TextBlock.ContextMenu>
                            </TextBlock>
                        </StackPanel>
                        <Button 
                        Content="Start"  
                        Command="{Binding  ElementName=MainGrid, Path=DataContext.StartClientCommand}" 
                        CommandParameter="{Binding}" />

                        <Button 
                        Content="Stop"  
                        Command="{Binding  ElementName=MainGrid, Path=DataContext.StopClientCommand}" 
                        CommandParameter="{Binding}" />

                        <Button 
                        Content="Calibrate"  
                        Command="{Binding  ElementName=MainGrid, Path=DataContext.CalibrateClientCommand}" 
                        CommandParameter="{Binding}" />

                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
AnjumSKhan
  • 9,647
  • 1
  • 26
  • 38
Daarwin
  • 2,896
  • 7
  • 39
  • 69
  • Thake a look at this: http://stackoverflow.com/questions/3878620/binding-from-context-menu-item-to-parent-control – Babbillumpa Jul 01 '16 at 13:25

2 Answers2

3

This is a little tricky. As ContextMenu isn't a part of the visual tree you need to pass the data context in different way. You can do it as followed:

<TextBlock Text="{Binding FriendlyName, Mode=OneWay}" Tag="{Binding Path=DataContext, ElementName=MainGrid}" >
    <TextBlock.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Start" 
                      Command="{Binding Path=PlacementTarget.Tag.StartClientCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
                      CommandParameter="{Binding Path=PlacementTarget, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
            <MenuItem Header="Stop" 
                      Command="{Binding  Path=PlacementTarget.Tag.StopClientCommand , RelativeSource={RelativeSource AncestorType=ContextMenu}}"
                      CommandParameter="{Binding Path=PlacementTarget, RelativeSource={RelativeSource AncestorType=ContextMenu}}"  />
            <MenuItem Header="Calibrate" 
                      Command="{Binding  Path=PlacementTarget.Tag.CalibrateClientCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
                      CommandParameter="{Binding Path=PlacementTarget, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
        </ContextMenu>
    </TextBlock.ContextMenu>
</TextBlock>
filhit
  • 2,084
  • 1
  • 21
  • 34
michauzo
  • 356
  • 1
  • 9
1

ContextMenu is not part of the Visual Tree, so you can't reach your Grid.

You can get the containing control of ContextMenu using PlacementTarget property.

If you have desgined your ViewModel properly, then your DataContext would reach your CM. In your case, your DataTemplate's DataContext would be enough. If that is not you want, and your Commands are present as static members like the way we have in NavigationCommand class, then you need to assign Command like :

<MenuItem Command="{x:Static local:MyCommands.CustomCommand}" ... />
AnjumSKhan
  • 9,647
  • 1
  • 26
  • 38