web-dev-qa-db-fra.com

WPF - comment masquer l'élément de menu si CanExecute de la commande est faux?

Par défaut, les éléments de menu sont désactivés lorsque sa commande ne peut pas être exécutée (CanExecute = false). Quelle est la façon la plus simple de rendre l'élément de menu visible/réduit en fonction de la méthode CanExecute?

47
Gus Cavalcanti

Vous pouvez simplement lier Visibility à IsEnabled (défini sur false sur CanExecute == false). Vous auriez toujours besoin d'un IValueConverter pour convertir le booléen en visible/réduit.

    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
    }
43
MrDosu

Merci pour la solution. Pour ceux qui veulent du XAML explicite, cela pourrait aider:

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

Dans mon cas, le menu contextuel est une ressource, donc la liaison pour la visibilité doit utiliser la configuration de liaison RelativeSource Self.

Comme côté, pour le CommandParameter, vous pouvez également passer le DataContext de l'élément sur lequel vous avez cliqué pour ouvrir le menu contextuel. Et pour acheminer les liaisons de commandes vers la fenêtre parente, vous devrez également définir le CommandTarget en conséquence.

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

CanExecute active/désactive la propriété IsEnabled, il suffit donc de regarder cela et de tout conserver dans l'interface utilisateur. Créez un style distinct si vous souhaitez le réutiliser.

44
rjarmstrong
8
Jacob Foshee

Je ne sais pas si c'est le moyen le plus simple, mais vous pouvez toujours créer une propriété qui renvoie la CanExecute() puis lier la visibilité de votre élément à cette propriété, en utilisant un IValueConverter à convertir le booléen en visibilité.

1
RoelF

Lier la visibilité à IsEnabled fait l'affaire, mais le XAML requis est désagréablement long et compliqué:

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

Vous pouvez utiliser une propriété jointe pour masquer tous les détails de liaison et exprimer clairement votre intention.

Voici la propriété jointe:

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

Et voici comment vous l'utiliseriez:

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