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

Posted

技术标签:

【中文标题】WPF - 如果命令的 CanExecute 为假,如何隐藏菜单项?【英文标题】:WPF - how to hide menu item if command's CanExecute is false? 【发布时间】:2011-04-15 06:28:55 【问题描述】:

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

【问题讨论】:

【参考方案1】:

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

【讨论】:

【参考方案2】:
<Style.Triggers>
    <Trigger Property="IsEnabled" Value="False">
        <Setter Property="Visibility" Value="Collapsed"/>
    </Trigger>
</Style.Triggers>

CanExecute 切换IsEnabled 属性,所以请注意这一点并将所有内容保留在 UI 中。如果您想重用它,请创建一个单独的样式。

【讨论】:

这是完美的——就像一个魅力一样工作(虽然我使用了一个直接绑定到可见性转换器而不是触发器,但想法是一样的) 可见性应设置为Collapsed,否则隐藏的菜单项仍会占用空间。 是的,这是一个更好的解决方案,尽管按照 Roman 的建议,可见性应该设置为 Collapsed 将可见性更改为“已折叠”。 更改可见性是对样式的更改,因此使用样式比直接绑定更有意义【参考方案3】:

您可以简单地将 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
    

【讨论】:

这比你需要的更多的努力,你可以使用触发器【参考方案4】:

Microsoft 提供了一个 BooleanToVisibilityConverter。http://msdn.microsoft.com/en-us/library/system.windows.controls.booleantovisibilityconverter.aspx

【讨论】:

【参考方案5】:

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

【讨论】:

这个答案没有多大帮助,但我给它 +1 以平衡那些我完全不明白为什么有人给出的负面观点。虽然这个答案没有太大帮助,但其中提到的所有内容都是有效的,此外,所有其他正面标记的答案都使用提到的内容。这个答案应得的最低分值是零,而不是负数! 这是我最初的想法,但是如何从这个新属性中访问(对象参数)参数,并将其传递给 CanExecute()?【参考方案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>

【讨论】:

以上是关于WPF - 如果命令的 CanExecute 为假,如何隐藏菜单项?的主要内容,如果未能解决你的问题,请参考以下文章

WPF 的命令的自动刷新时机——当你 CanExecute 会返回 true 但命令依旧不可用时可能是这些原因

2019-11-29-WPF-绑定命令在-MVVM-的-CanExecute-和-Execute-在按钮点击都没触发可能的原因...

WPF CommandParameter 在第一次调用 CanExecute 时为 NULL

wpf的命令怎么绑定多个条件

WPF 自定义快捷键命令(COMMAND)(转)

当命令 CanExecute 为 false 时,按钮不会被禁用