Adrian S
Adrian S

Reputation: 554

WPF: Binding the command parameter for ListBox ContextMenu to the Selected Item of the ListBox

Is it possible to bind the CommandParameter for a ListBox ContextMenu to the Selected Item of the ListBox? I should say that the ContCommand is in the main window and it is called when the Context Menu item is clicked - however, I need to get the parameter to work properly.

I tried this but the binding fails:

<Window x:Class="ListBoxContextMenu.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ListBoxContextMenu"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

    <Grid>
        <StackPanel>
            <TextBlock Text="ListBox here:"/>
            <ListBox ItemsSource="{Binding Items}" MinHeight="100" TabIndex="0" x:Name="LB">
                <ListBox.ContextMenu>
                    <ContextMenu>
                        <MenuItem Header="Foo" Command="{Binding ContCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}},Path=SelectedItem}"/>
                    </ContextMenu>
                </ListBox.ContextMenu>
            </ListBox>
        </StackPanel>
    </Grid>
</Window>

C# code for MainWindow:

using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using MvvmFoundation.Wpf;

    namespace ListBoxContextMenu
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                DataContext = this;
                Loaded += (sender, e) => MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
                ContCommand = new RelayCommand<object>((object o) =>
                {
                    System.Diagnostics.Debug.WriteLine("Context Menu pressed");
                });
            }

            public ObservableCollection<string> Items { get; set; } = new ObservableCollection<string>{"Fred", "Jim", "Sheila"};
            public RelayCommand<object> ContCommand { get; set; }
        }
    }

Upvotes: 4

Views: 6570

Answers (4)

Bilal
Bilal

Reputation: 1382

instead of binding it to the listbox bind it to the listboxitem that have been clicked he is the concerned !! not the listbox he hold the object that you are seeking

                <ListBox x:Name="lstAllTags"  FocusVisualStyle="{x:Null}"  ItemsSource="{Binding ResearchedTagsResult}" Margin="0" Background="{x:Null}" BorderBrush="{x:Null}" ItemTemplate="{DynamicResource SearchTagDataTemplate}" FontFamily="Consolas" Foreground="{DynamicResource {x:Static SystemColors.InfoBrushKey}}" MouseMove="LstAllTags_MouseMove" MouseLeave="LstAllTags_MouseLeave" HorizontalContentAlignment="Stretch" Focusable="False" FontSize="13" SelectionChanged="LstTags_SelectionChanged" BorderThickness="0">

                    <ListBox.Resources>

                        <!--Defines a context menu-->
                        <ContextMenu x:Key="ContextMenu">
                            <MenuItem  Command="{Binding DeleteTagCmd }" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=DataContext}" Foreground="{DynamicResource AppTextForeground}" DataContext="{DynamicResource TagManagement_instance}"  Header="Edit"  BorderBrush="#FF919191" BorderThickness="0" Padding="0">
                                <MenuItem.Icon>
                                    <Image Source="/Resx/pencil.png"/>
                                </MenuItem.Icon>
                            </MenuItem>


                        </ContextMenu>

                    </ListBox.Resources>

                </ListBox>

Upvotes: 0

mm8
mm8

Reputation: 169420

The ListBox is not a visual ancestor of the ContextMenu because the latter resides in its own visual tree.

But you could bind to the PlacementTarget of the ContextMenu, which is the ListBox.

This works:

<ListBox ItemsSource="{Binding Items}" MinHeight="100" TabIndex="0" x:Name="LB">
    <ListBox.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Foo" Command="{Binding ContCommand}" 
                              CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}},
                                Path=PlacementTarget.SelectedItem}"/>
        </ContextMenu>
    </ListBox.ContextMenu>
</ListBox>

Upvotes: 16

ΩmegaMan
ΩmegaMan

Reputation: 31721

The context menu is on a different tree and so binding is tricky depending on the situation. Here are two options:

1 Bind to the listbox via its name such as

 Binding SelectedItem, ElementName=LB

2 Use the reference name

Sometimes an element name binding fails and one has to use the x:ref name (which you have)

Binding Source={x:Reference LB}, Path=SelectedItem

As to the why, to quote x:Reference

In WPF and XAML 2006, element references are addressed by the framework-level feature of ElementName binding. For most WPF applications and scenarios, ElementName binding should still be used. Exceptions to this general guidance might include cases where there are data context or other scoping considerations that make data binding impractical and where markup compilation is not involved.

Upvotes: 1

Brandon Kramer
Brandon Kramer

Reputation: 1118

Add Mode=FindAncestor to the RelativeSource binding.

CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}, Path=SelectedItem}"

Upvotes: 0

Related Questions