Подтвердить что ты не робот

WPF - как скрыть пункт меню, если команда CanExecute является ложной?

По умолчанию пункты меню становятся недоступными, когда его команда не может быть выполнена (CanExecute = false). Какой самый простой способ сделать элемент меню видимым/свернутым на основе метода CanExecute?

4b9b3361

Ответ 1

Вы можете просто привязать видимость к IsEnabled (установить значение false на CanExecute == false). Вам все равно понадобится IValueConverter для преобразования bool в видимый/спящий.

    public class BooleanToCollapsedVisibilityConverter : IValueConverter
    {
        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            //reverse conversion (false=>Visible, true=>collapsed) on any given parameter
            bool input = (null == parameter) ? (bool)value : !((bool)value);
            return (input) ? Visibility.Visible : Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

Ответ 2

Спасибо за решение. Для тех, кто хочет явного XAML, это может помочь:

<Window.Resources>
        <BooleanToVisibilityConverter x:Key="booleanToVisibilityConverter" />
</Window.Resources>

<ContextMenu x:Key="innerResultsContextMenu">
    <MenuItem Header="Open"
              Command="{x:Static local:Commands.AccountOpened}"
              CommandParameter="{Binding Path=PlacementTarget.DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}" 
              CommandTarget="{Binding Path=PlacementTarget, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"
              Visibility="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}, Mode=OneWay, Converter={StaticResource booleanToVisibilityConverter}}" 
              />
</ContextMenu>

В моем случае контекстное меню является ресурсом, поэтому привязка для видимости должна использовать настройку привязки RelativeSource Self.

В качестве стороны для CommandParameter вы также можете передать DataContext элемента, на который было нажато, чтобы открыть контекстное меню. И для того, чтобы перенаправить командные привязки в родительское окно, вам также необходимо установить CommandTarget.

Ответ 3

<Style.Triggers>
    <Trigger Property="IsEnabled" Value="False">
        <Setter Property="Visibility" Value="Collapsed"/>
    </Trigger>
</Style.Triggers>

CanExecute переключает свойство IsEnabled, поэтому просто наблюдайте за этим и сохраняйте все в пользовательском интерфейсе. Создайте отдельный стиль, если вы хотите его повторно использовать.

Ответ 5

Я не знаю, является ли это самым простым способом, но вы всегда можете создать свойство, которое возвращает CanExecute(), а затем привязать видимость вашего элемента к этому свойству, используя IValueConverter, чтобы преобразовать логическое значение в Видимость.

Ответ 6

Привязка видимости к IsEnabled делает свое дело, но требуемый XAML неприятно длинный и сложный:

Visibility="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}, Mode=OneWay, Converter={StaticResource booleanToVisibilityConverter}}"

Вы можете использовать прикрепленное свойство, чтобы скрыть все обязательные детали и четко передать свои намерения.

Вот прикрепленное свойство:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace MyNamespace
{
    public static class Bindings
    {
        public static bool GetVisibilityToEnabled(DependencyObject obj)
        {
            return (bool)obj.GetValue(VisibilityToEnabledProperty);
        }

        public static void SetVisibilityToEnabled(DependencyObject obj, bool value)
        {
            obj.SetValue(VisibilityToEnabledProperty, value);
        }
        public static readonly DependencyProperty VisibilityToEnabledProperty =
            DependencyProperty.RegisterAttached("VisibilityToEnabled", typeof(bool), typeof(Bindings), new PropertyMetadata(false, OnVisibilityToEnabledChanged));

        private static void OnVisibilityToEnabledChanged(object sender, DependencyPropertyChangedEventArgs args)
        {
            if (sender is FrameworkElement element)
            {
                if ((bool)args.NewValue)
                {
                    Binding b = new Binding
                    {
                        Source = element,
                        Path = new PropertyPath(nameof(FrameworkElement.IsEnabled)),
                        Converter = new BooleanToVisibilityConverter()
                    };
                    element.SetBinding(UIElement.VisibilityProperty, b);
                }
                else
                {
                    BindingOperations.ClearBinding(element, UIElement.VisibilityProperty);
                }
            }
        }
    }
}

А вот как вы бы это использовали:

<Window x:Class="MyNamespace.SomeClass"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyNamespace">

    <ContextMenu x:Key="bazContextMenu">
        <MenuItem Header="Open"
                  Command="{x:Static local:FooCommand}"
                  local:Bindings.VisibilityToEnabled="True"/>
    </ContextMenu>
</Window>