WPF - how to hide menu item if command's CanExecute is false?

WpfCommandContextmenuMenuitem

Wpf Problem Overview


By default menu items become disabled when its command cannot be executed (CanExecute = false). What is the easiest way to make the menu item visible/collapsed based on the CanExecute method?

Wpf Solutions


Solution 1 - Wpf

Thanks for the solution. For those wanting explicit XAML this might help:

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

In my case, the context menu is a resource, so the binding for the visibility must use the RelativeSource Self binding setup.

As a side, for the CommandParameter, you might also pass the DataContext of the item whom was clicked to open the context menu. And in order to route the command bindings to the parent window, you will need to set the CommandTarget accordingly also.

Solution 2 - Wpf

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

CanExecute toggles the IsEnabled property so just watch this and keep everything in the UI. Create a separate style if you want to reuse this.

Solution 3 - Wpf

You can simply bind Visibility to IsEnabled (set to false on CanExecute == false). You still would need an IValueConverter to convert the bool to visible/collapsed.

    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
    }

Solution 4 - Wpf

Solution 5 - Wpf

I don't know if this is the easiest way, but you can always create a property which returns the CanExecute() and then bind the Visibility of your element to this property, using a IValueConverter to convert the boolean to Visibility.

Solution 6 - Wpf

Binding Visibility to IsEnabled does the trick, but the required XAML is unpleasantly long and complicated:

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

You can use an attached property to hide all the binding details and clearly convey your intent.

Here is the attached property:

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

And here is how you would use it:

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGus CavalcantiView Question on Stackoverflow
Solution 1 - WpfReddogView Answer on Stackoverflow
Solution 2 - WpfrjarmstrongView Answer on Stackoverflow
Solution 3 - WpfMrDosuView Answer on Stackoverflow
Solution 4 - WpfJacob FosheeView Answer on Stackoverflow
Solution 5 - WpfRoelFView Answer on Stackoverflow
Solution 6 - WpfSirCxyrtyxView Answer on Stackoverflow