微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

单击事件未在 ListBoxItem ContextMenu 上触发

如何解决单击事件未在 ListBoxItem ContextMenu 上触发

我正在尝试将 ContextMenu 添加ListBoxItem,但未触发 Click 事件。

我尝试了 ContextMenu.MenuItem.Click 事件。 我也尝试绑定 Command,但输出窗口中出现绑定错误,如:

“无法找到引用 'RelativeSource FindAncestor,AncestorType='System.Windows.Window',AncestorLevel='1'' 的绑定源。BindingExpression:Path=NavigateCommand;”

这里是完整的示例代码

XAML

<ListBox>
    <ListBoxItem>1</ListBoxItem>
    <ListBox.ItemContainerStyle>
        <Style targettype="{x:Type ListBoxItem}"
               BasedOn="{StaticResource {x:Type ListBoxItem}}">
            <Setter Property="ContextMenu">
                <Setter.Value>
                    <ContextMenu MenuItem.Click="ContextMenu_Click">
                        <MenuItem Header="Navigate"
                                  Command="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType=Window,AncestorLevel=1},Path=NavigateCommand}"
                                  Click="NavigateItemClick" />
                    </ContextMenu>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

代码隐藏

public MainWindow()
{
    InitializeComponent();
    NavigateCommand = new DelegateCommand(Navigate);
}

public DelegateCommand NavigateCommand { get; set; }


private void Navigate()
{
    MessageBox.Show("Command Worked");
}

private void NavigateItemClick(object sender,RoutedEventArgs e)
{
    MessageBox.Show("Item Click Worked");
}

private void ContextMenu_Click(object sender,RoutedEventArgs e)
{
    MessageBox.Show("Any Item click Worked");
}
        

有什么方法可以调用 Click 事件处理程序或绑定 Command 吗?

解决方法

ContextMenu 与您的 ListBox 不是同一可视化树的一部分,因为它显示在单独的窗口中。因此,使用 RelativeSourceElementName 绑定不能直接工作。但是,您可以通过将 Window(您在代码隐藏中定义 NavigateCommand 的位置)与 RelativeSource 绑定到 {{} 的 Tag 属性来解决此问题1}}。这是有效的,因为它们是同一视觉树的一部分。 ListBoxItem 属性是一个通用属性,您可以为其分配任何内容。

获取或设置可用于存储有关此元素的自定义信息的任意对象值。

然后使用 TagPlacementTarget 属性作为间接访问它打开的 ContextMenuTag

ListBoxItem

本质上,您将 <Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}"> <Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/> <Setter Property="ContextMenu"> <Setter.Value> <ContextMenu> <MenuItem Header="Navigate" Command="{Binding PlacementTarget.Tag.NavigateCommand,RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/> </ContextMenu> </Setter.Value> </Setter> </Style> 数据上下文绑定到 WindowTag,它是 ListBoxItemPlacementTarget,然后可以绑定ContextMenu 通过 NavigateCommand 属性。

添加 Tag 事件处理程序也是可能的,但如果 Click 定义在 ContextMenu 中,您必须以不同方式添加它,否则它将不起作用,您会得到这个奇怪的异常。

无法将 Style 类型的对象转换为 System.Windows.Controls.MenuItem 类型。

System.Windows.Controls.Button 添加带有事件设置器的 MenuItem 样式,您可以在其中分配事件处理程序。

Click
,

我认为没有必要编写“hackish”代码。我认为你应该改进你的代码。

上下文菜单应该只在其当前的 UI 上下文上运行,因此命名上下文菜单。当您右键单击代码编辑器时,您将找不到例如“打开文件”菜单项 - 这将与上下文无关。

菜单的上下文是实际的项目。因此,命令应该在项目的数据模型中定义,因为上下文菜单应该只定义针对项目的命令。

请注意,ContextMenu 始终隐式托管在 Popup 内(如 ChildPopup)。 Popup 的子元素永远不能成为可视化树的一部分(因为内容是在专用的 Window 实例中呈现的,而 Window 不能是另一个元素的子元素,{{1 }} 始终是其自己的可视化树的根),因此使用 Window 进行树遍历是行不通的。
您可以参考 Binding.RelativeSource(而不是 ContextMenu.PlacementTarget)来获取实际的 DataContext(展示位置目标)。 ListBoxItemDataContext 是数据模型(下例中的 WindowItem 类)。

例如引用例如ListBoxItem 的当前 OpenWindowCommand 的底层数据模型来自 ListBoxItem 使用:

ContextMenu

WindowItem.cs
为列表中的项目创建数据模型。该数据模型公开了对项目本身进行操作的所有命令。

"{Binding RelativeSource={RelativeSource AncestorType=ContextMenu},Path=PlacementTarget.DataContext.OpenWindowCommand}"

MainWindow.xaml.cs
使用数据绑定从 MainWindow 初始化 ListBox:

class WindowItem
{
  public WindowItem(string windowName) => this.Name = windowName;

  public string Name { get; }

  public ICommand OpenWindowCommand => new RelayCommand(ExecuteOpenWindow);

  private void ExecuteOpenWindow(object commandParameter)
  {
    // TODO::Open the window associated with this instance
    MessageBox.Show($"Window {this.Name} is open");
  }

  public override string ToString() => this.Name;
}

MainWindow.xaml

partial class MainWindow : Window
{
  public ObservableCollection<WindowItem> WindowItems { get; }

  public MainWindow()
  {
    InitializeComponent();
    this.DataContext = this;

    var windowItems = new 
    this.WindowItems = new ObservableCollection<WindowItem> 
    {
      new WindowItem { Name = "Window 1" },new WindowItem { Name = "Window 2" }
    }
  }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。