web-dev-qa-db-ja.com

WPF-コマンドのCanExecuteがfalseの場合にメニュー項目を非表示にする方法

デフォルトでは、コマンドを実行できない場合(CanExecute = false)、メニュー項目は無効になります。 CanExecuteメソッドに基づいてメニュー項目を表示/折りたたむ最も簡単な方法は何ですか?

47
Gus Cavalcanti

VisibilityをIsEnabledにバインドするだけです(CanExecute == falseでfalseに設定)。 boolを可視/折りたたみに変換するには、IValueConverterが必要です。

    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

解決策をありがとう。明示的な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も設定する必要があります。

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

CanExecuteIsEnabledプロパティを切り替えるため、これを監視してUIにすべてを保持します。これを再利用する場合は、別のスタイルを作成します。

44
rjarmstrong

MicrosoftはBooleanToVisibilityConverterを提供しています。
http://msdn.Microsoft.com/en-us/library/system.windows.controls.booleantovisibilityconverter.aspx

8
Jacob Foshee

これが最も簡単な方法かどうかはわかりませんが、CanExecute()を返すプロパティをいつでも作成し、IValueConverterを使用してこのプロパティに要素のVisibilityをバインドできますブール値を可視性に変換します。

1
RoelF

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