简体   繁体   English

单击事件未在 ListBoxItem ContextMenu 上触发

[英]Click Event not firing on ListBoxItem ContextMenu

I'm trying to add a ContextMenu to ListBoxItem , but the Click event is not fired.我正在尝试将ContextMenu添加到ListBoxItem ,但未触发Click事件。

I tried ContextMenu.MenuItem.Click event.我试过ContextMenu.MenuItem.Click事件。 I also tried binding Command , but a binding error appears in the output window like:我也尝试绑定Command ,但输出窗口中出现绑定错误,例如:

"Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1''. BindingExpression:Path=NavigateCommand;" “无法找到与引用 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1'' 进行绑定的源。BindingExpression:Path=NavigateCommand;”

Here is complete sample code.这是完整的示例代码。

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

CODE BEHIND代码隐藏

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

Is there any way to invoke the Click event handler or bind the Command ?有什么方法可以调用Click事件处理程序或绑定Command吗?

The ContextMenu is not part of the same visual tree as your ListBox , as it is displayed in a separate window. ContextMenu不是与ListBox相同的可视化树的一部分,因为它显示在单独的窗口中。 Therefore using a RelativeSource or ElementName binding does not work directly .因此,不能直接使用RelativeSourceElementName绑定。 However, you can work around this issue by binding the Window (where you define the NavigateCommand in code-behind) with a RelativeSource binding to the Tag property of ListBoxItem .但是,您可以通过将Window (您在代码隐藏中定义NavigateCommand )与RelativeSource绑定绑定到ListBoxItemTag属性来解决此问题。 This works, since they are part of the same visual tree.这是有效的,因为它们是同一视觉树的一部分。 The Tag property is a general purpose property that you can assign anything to. Tag属性是一个通用属性,您可以为其分配任何内容。

Gets or sets an arbitrary object value that can be used to store custom information about this element.获取或设置可用于存储有关此元素的自定义信息的任意对象值。

Then use the PlacementTarget property of the ContextMenu as indirection to access the Tag of the ListBoxItem that it was opened for.然后使用ContextMenuPlacementTarget属性作为间接访问打开它的ListBoxItemTag

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

In essence, you bind the Window data context to the Tag of ListBoxItem which is the PlacementTarget of the ContextMenu , that can then bind the NavigateCommand through the Tag property.本质上,您将Window数据上下文绑定到ListBoxItemTag ,后者是ContextMenuPlacementTarget ,然后可以通过Tag属性绑定NavigateCommand

Adding a Click event handler is also possible, but in case the ContextMenu is defined in a Style , you have to add it differently, otherwise it will not work and you get this strange exception.添加Click事件处理程序也是可能的,但如果ContextMenu定义在Style ,则必须以不同的方式添加它,否则它将不起作用并且您会收到这个奇怪的异常。

Unable to cast object of type System.Windows.Controls.MenuItem to type System.Windows.Controls.Button .类型无法施放对象System.Windows.Controls.MenuItem键入System.Windows.Controls.Button

Add a MenuItem style with an event setter for Click , where you assign the event handler.Click添加一个带有事件设置器的MenuItem样式,您可以在其中分配事件处理程序。

<Style TargetType="{x:Type ListBoxItem}"
       BasedOn="{StaticResource {x:Type ListBoxItem}}">
   <Setter Property="ContextMenu">
      <Setter.Value>
         <ContextMenu>
            <MenuItem Header="Navigate">
               <MenuItem.Style>
                  <Style TargetType="MenuItem">
                     <EventSetter Event="Click" Handler="MenuItem_OnClick"/>
                  </Style>
               </MenuItem.Style>
            </MenuItem>
         </ContextMenu>
      </Setter.Value>
   </Setter>
</Style>

I don't think it is necessary to write "hackish" code.我认为没有必要编写“hackish”代码。 I think you should refine your code instead.我认为你应该改进你的代码。

A context menu should only operate on its current UI context, hence the name context menu.上下文菜单应该只对其当前的 UI 上下文进行操作,因此命名上下文菜单。 When you right click your code editor, you won't find eg an "Open File" menu item - this would be out of context.当您右键单击您的代码编辑器时,您将找不到例如“打开文件”菜单项 - 这将与上下文无关。

The context of the menu is the actual item.菜单的上下文是实际的项目。 Therefore, the command should be defined in the data model of the item as the context menu should only define commands that target the items.因此,命令应该在项目的数据模型中定义,因为上下文菜单应该只定义针对项目的命令。

Note that the ContextMenu is always implicitly hosted inside a Popup (as Child of Popup ).请注意, ContextMenu始终隐式托管在Popup (作为Popup Child项)。 The child elements of Popup can never part of the visual tree (since the content is rendered inside a dedicated Window instance and Window can't be a child of another element, Window is always the root of its own visual tree), therefore tree traversal using Binding.RelativeSource can't work. Popup的子元素永远不能成为可视化树的一部分(因为内容是在一个专用的Window实例中呈现的,而Window不能是另一个元素的子元素, Window总是它自己的可视化树的根),因此树遍历使用Binding.RelativeSource不能工作。
You can reference ContextMenu.PlacementTarget (instead of DataContext ) to get the actual ListBoxItem (the placement target).您可以引用ContextMenu.PlacementTarget (而不是DataContext )来获取实际的ListBoxItem (放置目标)。 The DataContext of the ListBoxItem is the data model (WindowItem class in the example below). ListBoxItemDataContext是数据模型(下面示例中的 WindowItem 类)。

For example to reference the eg OpenWindowCommand of the underlying data model of the current ListBoxItem from inside the ContextMenu use:例如,要从ContextMenu内部引用当前ListBoxItem的底层数据模型的例如OpenWindowCommand ,请使用:

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

WindowItem.cs窗口项.cs
Create a data model for the items in the list.为列表中的项目创建数据模型。 This data model exposes all the commands to operate on the item itself.该数据模型公开了对项目本身进行操作的所有命令。

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.cs主窗口.xaml.cs
Initialize the ListBox from your MainWindow using data binding:使用数据绑定从 MainWindow 初始化 ListBox:

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

MainWindow.xaml主窗口.xaml

<Window>
  <Window.Resources>

    <!-- Command version -->
    <ContextMenu x:Key="ListBoxItemContextMenu">
      <MenuItem Header="Open"
                Command="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.DataContext.OpenWindowCommand}" />
    </ContextMenu>

    <!-- Optionally define a Click handler in code-behind (MainWindow.xaml.cs) -->
    <ContextMenu x:Key="ListBoxItemContextMenu">
      <MenuItem Header="Open"
                Click="OpenWindow_OnMenuItemClick" />
    </ContextMenu>
  </Window.Resources>

  <ListBox ItemsSource="{Binding WindowItems}">
    <ListBox.ItemContainerStyle>
      <Style TargetType="ListBoxItem">
        <Setter Property="ContextMenu" Value="{StaticResource ListBoxItemContextMenu}">
        </Setter>
      </Style>
    </ListBox.ItemContainerStyle>
  </ListBox>
</Window>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM