WPF: скрытие ContextMenu при пустом

У меня есть контекстное меню, которое получает пункты меню через привязку данных (я использую шаблон MVVM):

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

Это прекрасно работает. Однако в тех случаях, когда нет элементов меню для показа, я не хочу, чтобы контекстное меню отображалось вообще. Есть ли способ сделать это? Может быть, какой-то триггер XAML?

Я попытался поймать событие Opened och, закрывающее контекстное меню, когда нет детей. Это работает, но контекстное меню все еще мигает...

Ответ 1

Возможно привязка к свойству count коллекций элементов меню и использование конвертера для настройки видимости контекстного меню.

 <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;
        }
    }

Ответ 2

Вы можете определить неявный стиль:

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

Это должно работать во всех контекстных меню сразу.

Ответ 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>

Ответ 4

Вы можете попробовать сделать привязку к Visibility на Items.Count с конвертером значений - это должно помешать появлению вашего меню:)

Ответ 5

Если вы используете решение Tendlon в элементе управления TreeView (и, вероятно, любом элементе управления списком) с контекстным меню, у него есть проблемы.

  • Щелкните правой кнопкой мыши по node, а не с элементами контекстного меню = > Ничего не происходит (что хорошо)
  • Щелкните левой кнопкой мыши по node с элементами контекстного меню = > Отобразится контекстное меню (что плохо)

Ответ 6

Я придумал решение для TreeView, которое использует обратный вызов OnContextMenuOpening. Это предотвращает проблему, описанную Alex G. Если вы сбрасываете меню с помощью стиля XAML, то он не появляется, когда контекстное меню пуст, однако оно появляется после того, как вы щелкнете левой кнопкой мыши по другому элементу.

Код ищет TreeViewItem, который хочет открыть ContextMenu, и если он не имеет элементов, он устанавливает для свойства 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));
}