具有绑定项目的 WPF ContextMenu:ContextMenuOpening 事件中的 Items.Count == 0

发布于 2024-08-25 01:45:48 字数 2187 浏览 3 评论 0原文

我有一个 ContextMenu ,其 ItemsSource 绑定到列表视图的所选项目,如下所示:

<ContextMenu ItemsSource="{Binding Path=PlacementTarget.SelectedItem,
    RelativeSource={RelativeSource Self}, Converter={StaticResource possibleConverter}}"/>

possibleConverter 枚举属性的所有可能值所选项目的名称,显示在上下文菜单中。在上下文菜单的 Opened 事件中,我选择当前值,如下所示:

var cm = e.OriginalSource as ContextMenu;
if (cm != null) {
    var lv = cm.PlacementTarget as ListView;
    var field = lv.SelectedItem as Field;
    var item = cm.ItemContainerGenerator.ContainerFromItem(cm.Items.OfType<object>().Where(o => o.ToString().Equals(field.StringValue)).FirstOrDefault()) as MenuItem;
    if (item != null) {
        item.IsChecked = true;
    }
}

不是特别优雅,但它有效。使用调试器,我验证了 ContextMenu.Items.Count 属性在预期时具有非零值(即 cm.Items.Count 在 if 中为非零) 。

到目前为止,一切都很好。但是,列表视图中有些项目的上下文菜单中没有项目。在这种情况下,会显示一个空菜单。我试图在列表视图的 ContextMenuOpening 事件中抑制此行为,如下所示:

var lv = sender as ListView;
if (lv != null) {
    var cm = lv.ContextMenu;
    if ((cm != null) && (cm.Items.Count > 0)) {
        // Here we want to check the current item, which is currently done in the Opened event.
    } else {
        e.Handled = true;
    }
}

看起来应该可以工作。但是,cm.Items.Count 始终为零。即使 ListView.SelectedItem 未更改也是如此:对于具有菜单条目的项目,第一次单击后菜单会正确显示,因此数据绑定已经发生。第二次也显示正确,但无论如何,ContextMenuOpening 事件中的 Items.Count 为零。

我缺少什么?如何抑制空上下文菜单?为什么 ContextMenuOpening 处理程序中的计数为零,该处理程序位于 Windows 窗体 (ContextMenuStrip.Opening) 中执行这些操作的规范点?

编辑:经过进一步调查,发现在 ContextMenuOpening 处理程序中,任何与 listview 的绑定都会失败,这就是 ItemsSource 为 null 的原因。我尝试通过 ElementNameFindAncestor 关系进行绑定,但均无济于事。在该事件期间 PlacementTarget 为 null。不过,一个丑陋的黑客行为却奏效了:在 ContextMenuOpening 事件中,我将列表视图分配给 ContextMenu.Tag 属性,而现在绑定了 ItemsSource 绑定到Tag.SelectedItem。这会更新绑定,因此 Items.Count 就是它应该的样子。还是很奇怪。如果由于上下文菜单在事件期间脱离上下文而导致绑定失败,那么除了替换菜单或其他内容之外,如何在 ContextMenuOpening 中做有意义的事情?是否仅使用静态预定义菜单项进行测试?

I have a ContextMenu with the ItemsSource bound to the selected item of a list view, like this:

<ContextMenu ItemsSource="{Binding Path=PlacementTarget.SelectedItem,
    RelativeSource={RelativeSource Self}, Converter={StaticResource possibleConverter}}"/>

The possibleConverter enumerates all possible values for a property of the the selected item, which are shown in the context menu. In the Opened event of the context menu, I select the current value like this:

var cm = e.OriginalSource as ContextMenu;
if (cm != null) {
    var lv = cm.PlacementTarget as ListView;
    var field = lv.SelectedItem as Field;
    var item = cm.ItemContainerGenerator.ContainerFromItem(cm.Items.OfType<object>().Where(o => o.ToString().Equals(field.StringValue)).FirstOrDefault()) as MenuItem;
    if (item != null) {
        item.IsChecked = true;
    }
}

Not particularly elegant, but it works. With the debugger I verified that the ContextMenu.Items.Count property has a non-zero value when expected (i.e. cm.Items.Count is non-zero in the if).

So far, so good. There are, however, items in the listview where the context menu will have no items. In this case, an empty menu is shown. I tried to suppress this in the ContextMenuOpening event in the list view, like this:

var lv = sender as ListView;
if (lv != null) {
    var cm = lv.ContextMenu;
    if ((cm != null) && (cm.Items.Count > 0)) {
        // Here we want to check the current item, which is currently done in the Opened event.
    } else {
        e.Handled = true;
    }
}

Seems like it should work. However, cm.Items.Count is always zero. This is true even if ListView.SelectedItem did not change: For an item with menu entries, the menu is shown correctly after the first click, so the data binding has already happend. It is shown correct the second time as well, but in any case, Items.Count is zero in the ContextMenuOpening event.

What am I missing? How can I suppress empty context menus? Why is the count zero in the ContextMenuOpening handler, which is in Windows Forms (ContextMenuStrip.Opening) the canonical point where to do these things?

EDIT: Upon further investigating, it turns out that in the ContextMenuOpening handler, any binding to the listview fails, which is why ItemsSource is null. I tried to bind via ElementName, via a FindAncestor relationship, all to no avail. The PlacementTarget is null during that event. An ugly hack worked though: In the ContextMenuOpening event, I assign the list view to the ContextMenu.Tag property, while the ItemsSource binding now binds to Tag.SelectedItem. This updates the binding, so Items.Count is what it should be. It's still strange. How can you do meaningful things in ContextMenuOpening other than replacing the menu or something, if the binding fails because somehow the context menu is out of context during the event? Was it only tested with static pre-defined menu items?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文