Search code examples
c#winrt-xamlwin-universal-app

CanExecute() returns true and button is still disabled


I have a BottomAppBar.AppBarButton in a Windows Phone specific page, that is bound to a relay command. The code, binding and viewmodel implementation have all been used in basically the same way on other pages in the project and works exactly as expected there.

The issue in this particular scenario is that the button remains disabled even after raising the .RaiseCanExecuteChanged() method, and the CanExecute() returns true.

I originally thought that it might be due to excess calls to manually raising the notification with property changes, so have tightened that part of my code so that the method is only raised as needed, and when it is needed to change the button's status. Even still, the button remains disabled despite CanExecute() returning true. If I comment out all the checks in CanExecute() and default to true, the button is enabled as expected, and when tapped fires the expected Execute() function, so it appears that the initialization of the RelayCommand is ok. If I then let the checks back in, and run step through each time CanExecute() is fired, when it returns true, the button doesn't become enabled.

Any ideas? For what its worth, I've added code below, but I don't think that is the cause.

RelayCommand class is the standard class that comes with the HubApp in VS, so I will omit that code.

last line of the viewmodel constructor is the RelayCommand;

AddStrikeTeamCommand = new RelayCommand(async() => await AddStrikeTeam(), CanAddStrikeTeam);

Can Add is;

private bool CanAddStrikeTeam()
{
    //if (NameWorking == string.Empty) return false;
    //if (FactionWorking == string.Empty) return false;
    //if (PointsLimitWorking < 1) return false;
    //if (!IsValidTeamWorking) return false;
    return true;
}

And finally, the button binding

<AppBarButton x:Name="accept" Icon="Accept" Label="accept"
              Command="{Binding AddStrikeTeamCommand}"/>

Solution

  • I know this is a late answer, but this post is being linked in another question so I feel like I should post a better code sample.

    Jerry's answer is most likely correct that the problem is RaiseCanExecuteChanged is not raised automatically in that implementation of ICommand, however the code sample provided re-introduces the exact same problem that caused it to be taken out in the first place - it raises CanExecuteChanged whenever any property changes, resulting in CanExecute being called far more than necessary.

    The PropertyChanged event handler should include a check and only raise CanExecuteChanged if the property changed is one that is used in CanExecute.

    Since your CanExecute is

    private bool CanAddStrikeTeam()
    {
        if (NameWorking == string.Empty) return false;
        if (FactionWorking == string.Empty) return false;
        if (PointsLimitWorking < 1) return false;
        if (!IsValidTeamWorking) return false;
        return true;
    }
    

    then the event handler needs to only raise CanExecuteChanged if one of those for properties changes

    this.PropertyChanged += (s, e) => 
    {
        switch (e.PropertyName)
        {
            case "NameWorking":
            case "FactionWorking":
            case "PointsLimitWorking":
            case "IsValidTeamWorking":
                AddStrikeTeamCommand.RaiseCanExecuteChanged();
                break;
        }
    }