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

Posted

技术标签:

【中文标题】WPF:将 ListBox ContextMenu 的命令参数绑定到 ListBox 的选定项【英文标题】:WPF: Binding the command parameter for ListBox ContextMenu to the Selected Item of the ListBox 【发布时间】:2017-07-22 17:24:46 【问题描述】:

是否可以将 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属性。只关注阅读它的人的组成部分。 【参考方案1】:

上下文菜单位于不同的树上,因此根据具体情况进行绑定很棘手。这里有两个选项:

1 通过名称绑定到列表框,例如

 Binding SelectedItem, ElementName=LB

2使用参考名称

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

Binding Source=x:Reference LB, Path=SelectedItem

至于为什么,引用x:Reference

在 WPF 和 XAML 2006 中,元素引用由 ElementName 绑定的框架级功能处理。对于大多数 WPF 应用程序和方案,仍应使用 ElementName 绑定。此一般指南的例外情况可能包括存在数据上下文或其他范围考虑因素导致数据绑定不切实际以及不涉及标记编译的情况。

【讨论】:

&lt;MenuItem Header="Foo" Command="Binding ContCommand" CommandParameter="Binding SelectedItem, ElementName=LB"/&gt; 产生绑定错误:System.Windows.Data 错误:4:找不到引用“ElementName=LB”的绑定源。绑定表达式:路径=选定项;数据项=空;目标元素是'MenuItem'(名称='');目标属性是'CommandParameter'(类型'Object') @AdrianS 你试过 x:reference 绑定了吗? x:reference 绑定导致抛出异常: System.Windows.Markup.XamlParseException: 'Cannot call MarkupExtension.ProvideValue 因为循环依赖。 MarkupExtension 内的属性不能引用引用 MarkupExtension 结果的对象。受影响的 MarkupExtensions 是:'System.Windows.Data.Binding' 行号 '18' 和行位置 '80'。'【参考方案2】:

Mode=FindAncestor 添加到RelativeSource 绑定。

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

【讨论】:

我得到“System.Windows.Data 错误:4:找不到与引用'RelativeSource FindAncestor,AncestorType='System.Windows.Controls.ListBox',AncestorLevel='1'' 绑定的源。 BindingExpression:Path=SelectedItem;DataItem=null;目标元素是'MenuItem'(Name='');目标属性是'CommandParameter'(类型'Object')"【参考方案3】:

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>

【讨论】:

【参考方案4】:

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

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

【讨论】:

以上是关于WPF:将 ListBox ContextMenu 的命令参数绑定到 ListBox 的选定项的主要内容,如果未能解决你的问题,请参考以下文章

如何在wpf用listbox中添加右键菜单

WPF如何去掉ContextMenu的显示和隐藏动画

wpf实现QQ表情弹出框效果

WPF:将 ContextMenu 绑定到 MVVM 命令

WPF ContextMenu 文本对齐

WPF 将 ListBox 绑定到枚举,显示描述属性