Why is a button enabled if the Command binding res

2019-02-23 21:30发布

问题:

OK, the XAML is quite simple and uses MVVM to bind to an ICommand SomeCommand { get; } property on a view model:

<Button Command="{Binding Path=SomeCommand}">Something</Button>

If SomeCommand returns null, the button is enabled. (Nothing to do with CanExecute(object param) method on ICommand, because there is no instance to call that method on)

And now the question: Why is the button enabled? How would you work it around?

If you press the "enabled" button, obviously nothing is called. It is just ugly that the button looks enabled.

回答1:

It is enabled because that's the default state. Disabling it automatically would be an arbitrary measure that gives rise to other problems.

If you want to have a button without an associated command be disabled, bind the IsEnabled property to SomeCommand using an appropriate converter, e.g.:

[ValueConversion(typeof(object), typeof(bool))]
public class NullToBooleanConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value !== null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}


回答2:

My colleague found an elegant solution: using a binding fallback value!

public class NullCommand : ICommand
{
    private static readonly Lazy<NullCommand> _instance = new Lazy<NullCommand>(() => new NullCommand());

    private NullCommand()
    {
    }

    public event EventHandler CanExecuteChanged;

    public static ICommand Instance
    {
        get { return _instance.Value; }
    }

    public void Execute(object parameter)
    {
        throw new InvalidOperationException("NullCommand cannot be executed");
    }

    public bool CanExecute(object parameter)
    {
        return false;
    }
}

And then the XAML looks like:

<Button Command="{Binding Path=SomeCommand, FallbackValue={x:Static local:NullCommand.Instance}}">Something</Button>

The advantage of this solution is that it works better if you break Law of Demeter and you have some dots in the binding path, where each instance might become null.



回答3:

Very similar to Jon's answer, you could use a style with a trigger to mark buttons which should be disabled when there is no command set.

<Style x:Key="CommandButtonStyle"
        TargetType="Button">
    <Style.Triggers>
        <Trigger Property="Command"
                    Value="{x:Null}">
            <Setter Property="IsEnabled"
                    Value="False" />
        </Trigger>
    </Style.Triggers>
</Style>

I prefer this solution because it addresses the problem very directly and it does not require any new types.



标签: c# wpf mvvm