[英]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 .因此,不能直接使用
RelativeSource
或ElementName
绑定。 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
绑定绑定到ListBoxItem
的Tag
属性来解决此问题。 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.然后使用
ContextMenu
的PlacementTarget
属性作为间接访问打开它的ListBoxItem
的Tag
。
<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
数据上下文绑定到ListBoxItem
的Tag
,后者是ContextMenu
的PlacementTarget
,然后可以通过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 typeSystem.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). ListBoxItem
的DataContext
是数据模型(下面示例中的 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.