WPF Context menu doesn't bind to right databound item

前端 未结 7 1286
青春惊慌失措
青春惊慌失措 2020-12-08 11:03

I have a problem when binding a command in a context menu on a usercontrol that is on a tab page. The first time I use the menu (right-click on the tab) it works great, but

相关标签:
7条回答
  • 2020-12-08 11:42

    I found this method using the Tag property very useful when binding from a context menu deep inside a control template:

    http://blog.jtango.net/binding-to-a-menuitem-in-a-wpf-context-menu

    This makes it possible to bind to any datacontext available to the control that the context menu was opened from. The context menu can access the clicked control through "PlacementTarget". If the Tag property of the clicked control is bound to a desired datacontext, binding to "PlacementTarget.Tag" from inside the context menu will slingshot you directly to that datacontext.

    0 讨论(0)
  • 2020-12-08 11:45

    I know this is already an old post, but I would like to add another solution for those one who are looking for different ways to do it.

    I could not make the same solution to work in my case, since I was trying to do something else: open the context menu with a mouse click (just like a toolbar with a submenu attached to it) and also bind commands to my model. Since I was using an Event Trigger, the PlacementTarget object was null.

    This is the solution I found to make it work only using XAML:

    <!-- This is an example with a button, but could be other control -->
    <Button>
      <...>
    
      <!-- This opens the context menu and binds the data context to it -->
      <Button.Triggers>
        <EventTrigger RoutedEvent="Button.Click">
          <EventTrigger.Actions>
            <BeginStoryboard>
              <Storyboard>
                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.DataContext">
                  <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{Binding}"/>
                </ObjectAnimationUsingKeyFrames>
                <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
                  <DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True"/>
                </BooleanAnimationUsingKeyFrames>
              </Storyboard>
            </BeginStoryboard>
          </EventTrigger.Actions>
        </EventTrigger>
      </Button.Triggers>
    
      <!-- Here it goes the context menu -->
      <Button.ContextMenu>
        <ContextMenu>
          <MenuItem Header="Item 1" Command="{Binding MyCommand1}"/>
          <MenuItem Header="Item 2" Command="{Binding MyCommand2}"/>
        </ContextMenu>
      </Button.ContextMenu>
    
    </Button>
    
    0 讨论(0)
  • I had the same issue recently with a ContextMenu located in a ListBox. I tried to bind a command the MVVM way without any code-behind. I finally gave up and I asked a friend for his help. He found a slightly twisted but concise solution. He is passing the ListBox in the DataContext of the ContextMenu and then find the command in the view model by accessing the DataContext of the ListBox. This is the simplest solution that I have seen so far. No custom code, no Tag, just pure XAML and MVVM.

    I posted a fully working sample on Github. Here is an excerpt of the XAML.

    <Window x:Class="WpfListContextMenu.MainWindow" 
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            Title="MainWindow" Height="350" Width="268">
      <Grid>
        <DockPanel>
          <ListBox x:Name="listBox" DockPanel.Dock="Top" ItemsSource="{Binding Items}" DisplayMemberPath="Name"
                   SelectionMode="Extended">
            <ListBox.ContextMenu>
              <ContextMenu DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
                <MenuItem Header="Show Selected" Command="{Binding Path=DataContext.ShowSelectedCommand}"
                          CommandParameter="{Binding Path=SelectedItems}" />
              </ContextMenu>
            </ListBox.ContextMenu>
          </ListBox>
        </DockPanel>
      </Grid>
    </Window>
    
    0 讨论(0)
  • 2020-12-08 11:58

    The cleanest way I have found to bind commands to context menu items involves using a class called CommandReference. You can find it in the MVVM toolkit on Codeplex at WPF Futures.

    The XAML might look like this:

    <UserControl x:Class="View.MyView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:vm="clr-namespace:ViewModel;assembly=MyViewModel"
                    xmlns:mvvm="clr-namespace:ViewModelHelper;assembly=ViewModelHelper"
               <UserControl.Resources>
                    <mvvm:CommandReference x:Key="MyCustomCommandReference" Command="{Binding MyCustomCommand}" />
    
                    <ContextMenu x:Key="ItemContextMenu">
                        <MenuItem Header="Plate">
                            <MenuItem Header="Inspect Now" Command="{StaticResource MyCustomCommandReference}"
                                    CommandParameter="{Binding}">
                            </MenuItem>
                        </MenuItem>
                   </ContextMenu>
        </UserControl.Resources>
    

    MyCustomCommand is a RelayCommand on the ViewModel. In this example, the ViewModel was attached to the view's datacontext in the code-behind.

    Note: this XAML was copied from a working project and simplified for illustration. There may be typos or other minor errors.

    0 讨论(0)
  • 2020-12-08 11:59

    The key thing to remember here is context menus are not part of the visual tree.

    Therefore they don't inherit the same source as the control they belong to for binding. The way to deal with this is to bind to the placement target of the ContextMenu itself.

    <MenuItem Header="Change" Command="{Binding 
        Path=PlacementTarget.ChangeCommand, 
        RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"
    />
    
    0 讨论(0)
  • 2020-12-08 12:03

    Check out: ContextMenuServiceExtensions.DataContext

    0 讨论(0)
提交回复
热议问题