WPF - 如果命令的 CanExecute 为 false,如何隐藏菜单项?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3761672/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 22:08:45  来源:igfitidea点击:

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

wpfcommandcontextmenumenuitem

提问by Gus Cavalcanti

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?

默认情况下,当其命令无法执行时,菜单项会被禁用(CanExecute = false)。基于 CanExecute 方法使菜单项可见/折叠的最简单方法是什么?

采纳答案by MrDosu

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.

您可以简单地将 Visibility 绑定到 IsEnabled(在 CanExecute == false 上设置为 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
    }

回答by Reddog

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

感谢您的解决方案。对于那些想要显式 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>

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

就我而言,上下文菜单是一种资源,因此可见性的绑定必须使用 RelativeSource Self 绑定设置。

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.

另一方面,对于 CommandParameter,您还可以传递被单击以打开上下文菜单的项目的 DataContext。并且为了将命令绑定路由到父窗口,您还需要相应地设置 CommandTarget。

回答by rjarmstrong

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

CanExecutetoggles the IsEnabledproperty so just watch this and keep everything in the UI. Create a separate style if you want to reuse this.

CanExecute切换IsEnabled属性,因此只需观看此内容并将所有内容保留在 UI 中。如果您想重用它,请创建一个单独的样式。

回答by RoelF

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 IValueConverterto convert the boolean to Visibility.

我不知道这是否是最简单的方法,但您始终可以创建一个属性,该属性返回CanExecute(),然后将元素的可见性绑定到此属性,使用 aIValueConverter将布尔值转换为可见性。

回答by SirCxyrtyx

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

将可见性绑定到 IsEnabled 可以解决问题,但所需的 XAML 非常长且复杂:

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>