C# 如何隐藏没有可见项的上下文菜单?

C# 如何隐藏没有可见项的上下文菜单?,c#,wpf,contextmenu,C#,Wpf,Contextmenu,我有几个上下文菜单,其中的项目定义如下: <ContextMenu> <MenuItem Header="Item 1" Command="{Binding Item1Command}"/> <MenuItem Header="Item 2" Command="{Binding Item2Command}"/> <MenuItem Header="Item 3" Command="{Binding Item3Command}"/&

我有几个上下文菜单,其中的项目定义如下:

<ContextMenu>
    <MenuItem Header="Item 1" Command="{Binding Item1Command}"/>
    <MenuItem Header="Item 2" Command="{Binding Item2Command}"/>
    <MenuItem Header="Item 3" Command="{Binding Item3Command}"/>
</ContextMenu>


命令
绑定设置了
CanExecute
,因此当它为false时,
MenuItem
具有
IsEnabled=false
。当
IsEnabled=false
时,该触发器将设置
Visibility=Collapsed
,反之亦然。我遇到的问题是,如果禁用了所有
MenuItem
s,则
ContextMenu
仍然显示为一个小的空白矩形。我有很多
ContextMenu
s,那么实现这一点的最模块化的方法是什么呢?

我最终做了一个不同的解决方法,而不是隐藏ContextMenu的可见性。由于它检查所有FrameworkElement ContextMenus,似乎效率较低,但它正在工作:

EventManager.RegisterClassHandler(typeof(FrameworkElement), ContextMenuOpeningEvent,
    new RoutedEventHandler(OnContextMenuOpening));

private void OnContextMenuOpening(object sender, RoutedEventArgs e)
{
    var control = (sender as FrameworkElement);
    var menu = control == null ? null : control.ContextMenu;
    if (menu != null)
    {
        var items = menu.Items.Cast<MenuItem>();
        if (items.All(i => i.Visibility != Visibility.Visible))
        {
            e.Handled = true;
        }
    }
}
EventManager.RegisterClassHandler(typeof(FrameworkElement)、ContextMenuOpenEvent、,
新的路由EventHandler(OnContextMenuoOpening);
私有void OnContextMenuOpening(对象发送方,路由目标)
{
var-control=(发送方作为框架元素);
var menu=control==null?null:control.ContextMenu;
如果(菜单!=null)
{
var items=menu.items.Cast();
if(items.All(i=>i.Visibility!=Visibility.Visibility))
{
e、 已处理=正确;
}
}
}

如果有一种更好的方法可以做到这一点,而不需要代码隐藏,那么对于父控件,我愿意接受其他解决方案。

这里有一段xaml,它将帮助您开始使用“条件”上下文菜单。
请注意:这只是一个起点

<ListView>
         <ListView.Resources>
             <ContextMenu x:Key="initial">
                 <Menu>
                     <MenuItem Header="First Option"></MenuItem>
                 </Menu>
             </ContextMenu>
             <Style TargetType="ListViewItem">
                 <Style.Triggers>
                     <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Value="1">
                         <Setter Property="ContextMenu" Value="{StaticResource initial}" />
                     </DataTrigger>
                 </Style.Triggers>
                 <Setter Property="ContextMenu" Value="{StaticResource initial}"/>
             </Style>
         </ListView.Resources>
         <ListViewItem>1</ListViewItem>
         <ListViewItem>2</ListViewItem>
     </ListView>

1.
2.

我发现解决这个问题的唯一方法是通过attached属性,但它看起来对我来说是可以接受的。首先定义一个具有属性的类:

public class ContextMenuExtension
{
    public static bool GetHideOnEmpty(DependencyObject obj)
    {
        return (bool)obj.GetValue(HideOnEmptyProperty);
    }

    public static void SetHideOnEmpty(DependencyObject obj, bool value)
    {
        obj.SetValue(HideOnEmptyProperty, value);
    }

    public static readonly DependencyProperty HideOnEmptyProperty =
        DependencyProperty.RegisterAttached("HideOnEmpty", typeof(bool), typeof(ContextMenuExtension), new UIPropertyMetadata(false, HideOnEmptyChanged));

    private static void HideOnEmptyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var contextMenu = d as ContextMenu;

        if (contextMenu == null)
            return;

        contextMenu.Loaded += ContextMenu_Loaded;
    }

    private static void ContextMenu_Loaded(object sender, RoutedEventArgs e)
    {
        var contextMenu = sender as ContextMenu;
        var hideOnEmpty = GetHideOnEmpty(contextMenu);

        HideContextMenu(contextMenu, hideOnEmpty);
    }

    //This is where we check if all the items are not visible
    private static void HideContextMenu(ContextMenu contextMenu, bool val)
    {
        //First, we have to know if the HideOnEmpty property is set to true. 
        if (val)
        {
            //Check if the contextMenu is either null or empty
            if (contextMenu.Items == null || contextMenu.Items.Count < 1)
                contextMenu.Visibility = Visibility.Collapsed; //Hide the contextMenu
            else
            {
                bool hide = true;
                //Check if all the items are not visible.  
                foreach (MenuItem i in contextMenu.Items)
                {
                    if (i.Visibility == Visibility.Visible)
                    {
                        hide = false;
                        break;
                    }
                }

                //If one or more items above is visible we won't hide the contextMenu.
                if (!hide)
                    contextMenu.Visibility = Visibility.Visible;
                else
                    contextMenu.Visibility = Visibility.Collapsed;
            }
        }
    }
}
public类ContextMenuExtension
{
公共静态bool GetHideOnEmpty(DependencyObject obj)
{
返回(bool)对象GetValue(HideOnEmptyProperty);
}
公共静态void SetHideOnEmpty(DependencyObject对象,布尔值)
{
对象设置值(HideOnEmptyProperty,值);
}
公共静态只读从属属性HideOnEmptyProperty=
DependencyProperty.RegisterAttached(“HideOnEmpty”、typeof(bool)、typeof(ContextMenuExtension)、新UIPropertyMetadata(false、HideOnEmptyChanged));
私有静态void HideOnEmptyChanged(DependencyObject d、DependencyPropertyChangedEventArgs e)
{
var contextMenu=d作为contextMenu;
if(contextMenu==null)
返回;
contextMenu.Loaded+=contextMenu\u Loaded;
}
已加载私有静态void ContextMenu_(对象发送方,路由目标)
{
var contextMenu=发送方作为contextMenu;
var hideOnEmpty=GetHideOnEmpty(上下文菜单);
HideContextMenu(上下文菜单,hideOnEmpty);
}
//这是我们检查是否所有项目都不可见的地方
私有静态无效HideContextMenu(上下文菜单上下文菜单,布尔值)
{
//首先,我们必须知道HideOnEmpty属性是否设置为true。
if(val)
{
//检查上下文菜单是否为null或空
if(contextMenu.Items==null | | contextMenu.Items.Count<1)
contextMenu.Visibility=Visibility.Collapsed;//隐藏contextMenu
其他的
{
布尔隐藏=真;
//检查是否所有项目都不可见。
foreach(contextMenu.Items中的MenuItem i)
{
如果(i.Visibility==Visibility.Visibility)
{
隐藏=假;
打破
}
}
//如果上面的一个或多个项目可见,我们不会隐藏contextMenu。
如果(!隐藏)
contextMenu.Visibility=可见性.Visibility;
其他的
contextMenu.Visibility=Visibility.Collapsed;
}
}
}
}
然后在XAML中,使用ContextMenu向上面的类添加名称空间,并执行以下操作:

<ContextMenu cc:ContextMenuExtension.HideOnEmpty="True">
   <MenuItem Header="Delete" Command="<delete_command>" Visibility="Collapsed"/>
</ContextMenu>


每次用户右键单击查看菜单时,它都会从“ContextMenuExtension”类触发“HideContextMenu”方法,如果没有项目或所有项目都不可见,则隐藏整个ContextMenu。

您可以将ContextMenu的可视性绑定到项目,如果没有可见项目,则编写转换器。据我所知,绑定在DataContext中,DataContext是我的ViewModel,但是我的菜单项是在视图中显式定义的。我理解转换器部分,但是如何仅通过更改视图来完成前半部分?更不用说,我不确定Items集合是否会触发NotifyPropertyChanged,以使转换器根据MenuItems的触发可见性实际工作。我只是尝试了StaticResource,结果正是您所需要的。如果你愿意,我可以把代码贴在这里让你检查一下。这不是一个直接的解决方案,但它应该给你一个关于如何做的好主意。