WPF:将ListBox ContextMenu的命令参数绑定到ListBox的选定项

3

是否可以将ListBox ContextMenu的CommandParameter绑定到ListBox的所选项?需要说明的是,ContCommand在主窗口中,并且在单击上下文菜单项时被调用 - 但是,我需要使参数正常工作。

我尝试了这个但绑定失败了:

<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>

MainWindow 的 C# 代码:

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; }
        }
    }

移除你的代码示例中与主题无关的部分,例如 using / NameSpace 和 Window 属性。只关注于对读者有用的核心部分。 - ΩmegaMan
4个回答

14
ListBox 并不是 ContextMenu 的可视祖先,因为后者存在于自己的可视树中。

但您可以绑定到 ContextMenuPlacementTarget,它是 ListBox

这样做是有效的:

<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>

1
上下文菜单位于不同的树上,因此绑定取决于具体情况可能会比较棘手。以下是两个选项: 1. 通过列表框的名称进行绑定,例如:
 Binding SelectedItem, ElementName=LB

2 使用参考名称

有时元素名称绑定失败,必须使用 x:ref 名称(您已经拥有)。

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

关于为什么,引用x:Reference

在WPF和XAML 2006中,元素引用通过ElementName绑定的框架级特性进行处理。对于大多数WPF应用程序和场景,仍应使用ElementName绑定。这个一般性指导的例外情况可能包括数据上下文或其他范围考虑使数据绑定不切实际且不涉及标记编译的情况。


<MenuItem Header="Foo" Command="{Binding ContCommand}" CommandParameter="{Binding SelectedItem, ElementName=LB}"/> 会产生一个绑定错误: System.Windows.Data Error: 4 : 找不到参考'ElementName=LB'的绑定源。BindingExpression:Path=SelectedItem; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'CommandParameter' (type 'Object')。 - Adrian S
@AdrianS,你尝试过使用x:reference绑定了吗? - ΩmegaMan
x:Reference绑定会导致异常抛出:System.Windows.Markup.XamlParseException:'由于循环依赖关系,无法调用MarkupExtension.ProvideValue。 MarkupExtension内部的属性不能引用引用MarkupExtension结果的对象。受影响的MarkupExtensions是:'System.Windows.Data.Binding' 行号 '18' 和行位置 '80'。' - Adrian S

0

不要将其绑定到列表框,而是将其绑定到已单击的列表框项。 他才是相关的对象!!不是列表框本身,他持有你正在寻找的对象

                <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>

0
Mode=FindAncestor 添加到RelativeSource绑定中。
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}, Path=SelectedItem}"

1
我收到了“System.Windows.Data错误:4:找不到引用'RelativeSource FindAncestor,AncestorType ='System.Windows.Controls.ListBox',AncestorLevel ='1''的绑定源。 BindingExpression:Path = SelectedItem; DataItem = null;目标元素为'MenuItem'(名称= '';目标属性为'CommandParameter'(类型为'Object')”。 - Adrian S

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接