WPF:当菜单为空时隐藏ContextMenu

11

我有一个上下文菜单,通过数据绑定获取菜单项 (我使用MVVM模式):

<ContextMenu ItemsSource="{Binding Path=ContextMenuItems}" />

这个方法运行良好。但是,当没有菜单项可显示时,在任何情况下我都不想出现上下文菜单。有没有办法实现这一点?可能是某种XAML触发器吗?

我尝试在打开事件中捕获和关闭上下文菜单,当没有子元素时关闭它。这样可以工作,但上下文菜单仍会闪烁...

5个回答

28

您可以定义一个隐式样式:

<Style TargetType="{x:Type ContextMenu}">
    <Style.Triggers>
        <Trigger Property="HasItems" Value="False">
            <Setter Property="Visibility" Value="Collapsed" />
        </Trigger>
    </Style.Triggers>
</Style>

这应该适用于你所有的上下文菜单。


2
对我来说可以工作。与Adam的答案非常相似(也得到了赞同),但我更喜欢Trigger而不是BooleanToVisibilityConverter。显然比被接受的答案好 - 我没有经历过Meleak提到的闪烁上下文菜单。 - Mike Fuchs
2
比转换选项更好。问题在于当菜单项不可见时,属性HasItems也为true。没有HasVisibleItems属性 :-/ - mamuesstack
2
我尝试了您的方法,它似乎相当简单。 但是,如果您有一个动态上下文菜单,在其中,如果不需要,则内部项目将被折叠,则此方法将无法工作。您对如何解决这个问题有什么建议吗? - Master Azazel

19

可能绑定到您的菜单项集合计数属性,并使用转换器来设置上下文菜单的可见性。

 <ContextMenu ItemsSource="{Binding Path=ContextMenuItems}"
              Visibility="{Binding Path=ContextMenuItems.Count,Converter={StaticResource zeroToHiddenConverter}}">

public  class ZeroToHiddenConverter:IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    {
        int count = (int) value;

        if (count == 0) 
        {
            return Visibility.Hidden;
        }
        else
        {
            return Visibility.Visible;
        }
    }

好的答案!我尝试触发HasItems并将可见性设置为false,但是当再次添加菜单项时,我会看到闪烁的上下文菜单,但如果这里不是这种情况,那么这绝对是正确的方法。 - Fredrik Hedblad
很高兴听到这个消息。如果没有其他需要补充的内容,请将其标记为已回答。 - ThomasAndersson
这不是一个完整的解决方案。主要问题在于,当您的第一个上下文菜单中没有任何项时,转换器不会被调用。'Count'的初始属性值为0,因此直到计数更改,转换器才会被调用。 - Quark Soup
注意!使用这种方法将调用“ContextMenuItems”的getter 2次。因此,如果getter中有任何逻辑,则会计算两次。 - VRage

3
以下是如何设置应用程序范围内的样式以隐藏空上下文菜单。
HasItems是ContextMenu本身的一个依赖属性,因此您可以基于该布尔值设置上下文菜单的可见性。
以下是在资源字典中执行此操作的方法:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <BooleanToVisibilityConverter x:Key="VisibilityOfBool" />

    <Style TargetType="{x:Type ContextMenu}">
        <Setter Property="Visibility" Value="{Binding HasItems, RelativeSource={RelativeSource Self}, Converter={StaticResource VisibilityOfBool}}"/>
    </Style>
</ResourceDictionary>

1
我想出了一种使用 OnContextMenuOpening 回调的 TreeView 解决方案。它可以防止 Alex G 提到的问题。如果您使用 XAML 样式折叠菜单,则在上下文菜单为空时它不会出现,但是当您左键单击另一个项目后,它会出现。
代码查找要打开上下文菜单的 TreeViewItem,如果它没有子项,则将事件的 Handled 属性设置为 true。
protected override void OnContextMenuOpening(ContextMenuEventArgs e) {
     var item = FindTreeViewItem(e.OriginalSource as DependencyObject);
     var contextMenu = item.ContextMenu;
     if (contextMenu != null && !contextMenu.HasItems) {
         e.Handled = true;
     }
 }

 private TreeViewItem FindTreeViewItem(DependencyObject dependencyObject) {
     if (dependencyObject == null) {
         return null;
     }
     var treeViewItem = dependencyObject as TreeViewItem;
     if (treeViewItem != null) {
         return treeViewItem;
     }
     return FindTreeViewItem(VisualTreeHelper.GetParent(dependencyObject));
}

0
你可以尝试使用一个值转换器,在Items.Count上做一个Visibility的绑定 - 这应该可以防止你的菜单出现 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接