![](/img/trans.png)
[英]WPF DataGridRow ContextMenu MenuItem Click Event Not Firing
[英]Click Event not firing on 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
相同的可视化树的一部分,因为它显示在单独的窗口中。 因此,不能直接使用RelativeSource
或ElementName
绑定。 但是,您可以通过将Window
(您在代码隐藏中定义NavigateCommand
)与RelativeSource
绑定绑定到ListBoxItem
的Tag
属性来解决此问题。 这是有效的,因为它们是同一视觉树的一部分。 Tag
属性是一个通用属性,您可以为其分配任何内容。
获取或设置可用于存储有关此元素的自定义信息的任意对象值。
然后使用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>
本质上,您将Window
数据上下文绑定到ListBoxItem
的Tag
,后者是ContextMenu
的PlacementTarget
,然后可以通过Tag
属性绑定NavigateCommand
。
添加Click
事件处理程序也是可能的,但如果ContextMenu
定义在Style
,则必须以不同的方式添加它,否则它将不起作用并且您会收到这个奇怪的异常。
类型无法施放对象
System.Windows.Controls.MenuItem
键入System.Windows.Controls.Button
。
为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>
我认为没有必要编写“hackish”代码。 我认为你应该改进你的代码。
上下文菜单应该只对其当前的 UI 上下文进行操作,因此命名上下文菜单。 当您右键单击您的代码编辑器时,您将找不到例如“打开文件”菜单项 - 这将与上下文无关。
菜单的上下文是实际的项目。 因此,命令应该在项目的数据模型中定义,因为上下文菜单应该只定义针对项目的命令。
请注意, ContextMenu
始终隐式托管在Popup
(作为Popup
Child
项)。 Popup
的子元素永远不能成为可视化树的一部分(因为内容是在一个专用的Window
实例中呈现的,而Window
不能是另一个元素的子元素, Window
总是它自己的可视化树的根),因此树遍历使用Binding.RelativeSource
不能工作。
您可以引用ContextMenu.PlacementTarget
(而不是DataContext
)来获取实际的ListBoxItem
(放置目标)。 ListBoxItem
的DataContext
是数据模型(下面示例中的 WindowItem 类)。
例如,要从ContextMenu
内部引用当前ListBoxItem
的底层数据模型的例如OpenWindowCommand
,请使用:
"{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.DataContext.OpenWindowCommand}"
窗口项.cs
为列表中的项目创建数据模型。 该数据模型公开了对项目本身进行操作的所有命令。
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;
}
主窗口.xaml.cs
使用数据绑定从 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" }
}
}
}
主窗口.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.