RaiseCanExecuteChanged() on a RelayCommand<enum> doesn't cause CanExecute() to be called

here comes my first question.

In a UWP application with MVVM Light, I'm trying to define just one command with an enumerative parameter to respond to all button interactions in the view. However buttons remain disabled and not responsive.

I have defined the parameter for the command

public enum ButtonKey
{
    Connect = 0,
    Disconnect = 1
}

which is then used by the MainViewModel class

public class MainViewModel : ViewModelBase
{
    RelayCommand<ButtonKey> buttonPressed;
    public RelayCommand<ButtonKey> ButtonPressed
    {
        get => buttonPressed;
        set => buttonPressed = value;
    }

    public MainViewModel()
    {
        buttonPressed = new RelayCommand<ButtonKey> (ButtonPressed_Execute, ButtonPressed_CanExecute);
    }

    bool ButtonPressed_CanExecute(ButtonKey arg)
    {
        bool retValue = false;
        switch (arg)
        {
            // The following conditions are just for testing
            case ButtonKey.Connect:
                retValue = true;
                break;
            case ButtonKey.Disconnect:
                retValue = false;
                break;
        }
        return retValue;
    }

    void ButtonPressed_Execute(ButtonKey obj)
    {
        // another switch() case:
    }
}

Then the xaml code is the following:

<CommandBar>
    <AppBarButton Label="Connect" Command="{Binding ButtonPressed}">
        <AppBarButton.CommandParameter>
            <viewModel:ButtonKey>Connect</viewModel:ButtonKey>
        </AppBarButton.CommandParameter>
    </AppBarButton>
    <AppBarButton Label="Disconnect" Command="{Binding ButtonPressed}">
        <AppBarButton.CommandParameter>
            <viewModel:ButtonKey>Disconnect</viewModel:ButtonKey>
    </AppBarButton.CommandParameter>
</AppBarButton>

The ButtonPressed_CanExecute(ButtonKey arg) method is not called even with a buttonPressed.RaiseCanExecuteChanged() call in the MainViewModel constructor.

I think that all is caused by the enumerative type used as the command parameter, but I cannot figure out why. Any help will be highly appreciated.

2 answers

  • answered 2018-01-11 21:12 Martin Zikmund

    I have checked the source code of MvvmLight and found the issue. In the RelayCommand<T>'s source, you will find the following check within the CanExecute method:

    if (parameter == null || parameter is T)
    {
        return (_canExecute.Execute((T)parameter));
    }
    

    For some reason, the enum parameter of the method is an int, not an instance of your enum when the check is being performed. Because of that, the second check will fail.

    If you update your code to work with int, it will work as expected:

    public RelayCommand<int> ButtonPressed { get; set; }
    
    public MainViewModel()
    {
        ButtonPressed = new RelayCommand<int>(ButtonPressed_Execute, ButtonPressed_CanExecute);            
    }
    
    bool ButtonPressed_CanExecute(int arg)
    {
        bool retValue = false;
        switch (arg)
        {
            // The following conditions are just for testing
            case (int) ButtonKey.Connect:
                retValue = true;
                break;
            case (int) ButtonKey.Disconnect:
                retValue = false;
                break;
        }
        return retValue;
    }
    
    void ButtonPressed_Execute(int  obj)
    {
        // another switch() case:
    }
    

    I find it quite surprising and I guess this should be addressed as a bug in MvvmLight.

  • answered 2018-01-11 21:12 Peregrine

    Try setting the command parameter using the x:static markup extension

    <AppBarButton Label="Connect" 
                  Command="{Binding ButtonPressed}"
                  CommandParameter="{x:Static viewModel:ButtonKey.Connect}" />