По умолчанию пункты меню становятся недоступными, когда его команда не может быть выполнена (CanExecute = false). Какой самый простой способ сделать элемент меню видимым/свернутым на основе метода CanExecute?
WPF - как скрыть пункт меню, если команда CanExecute является ложной?
Ответ 1
Вы можете просто привязать видимость к IsEnabled (установить значение false на CanExecute == 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
}
Ответ 2
Спасибо за решение. Для тех, кто хочет явного 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.
Ответ 3
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
</Style.Triggers>
CanExecute
переключает свойство IsEnabled
, поэтому просто наблюдайте за этим и сохраняйте все в пользовательском интерфейсе. Создайте отдельный стиль, если вы хотите его повторно использовать.
Ответ 4
Microsoft предоставляет BooleanToVisibilityConverter.
http://msdn.microsoft.com/en-us/library/system.windows.controls.booleantovisibilityconverter.aspx
Ответ 5
Я не знаю, является ли это самым простым способом, но вы всегда можете создать свойство, которое возвращает CanExecute()
, а затем привязать видимость вашего элемента к этому свойству, используя IValueConverter
, чтобы преобразовать логическое значение в Видимость.
Ответ 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>