4

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}"/>
Yoh Deadfall
  • 2,711
  • 7
  • 28
  • 32
Lindsay
  • 575
  • 8
  • 18

3 Answers3

3

I'd probably bet your problem has to do with RaiseCanExecuteChanged(). This is especially true if you are used to WPF and how it automatically refreshes CanExecute for you. Check out this Delegate Command implementation:

http://codepaste.net/ho9s5a

The ICommand interface defines the event CanExecuteChanged which instructs the button (or UI Element) to refresh its Enabled status. In WPF, this was raised constantly by the static, command manager. This does not exist in WinRT. In WPF, because it was raised so frequently, WPF developers had to be careful that CanExecute() was not an expensive operation. WinRT provides for expensive tests, but consequently requires the developer to raise the event manually. I hope this makes sense.

One way I handle this is:

DelegateCommand _SaveCommand = null;
public DelegateCommand SaveCommand
{
    get
    {
        if (_SaveCommand != null)
            return _SaveCommand;
        _SaveCommand = new DelegateCommand
        (
            () =>
            {
                // TODO
            }, 
            () => true
        );
        this.PropertyChanged += (s, e) => _SaveCommand.RaiseCanExecuteChanged();
        return _SaveCommand;
    }
}

This basically refreshes the CanExecute based on the change of any property in (usually in my View Model). This is not sufficient if you have potential changes in models that you have in an ObservableCollection, but it's a nice start to the whole thing.

There's a possibility that you don't have this problem at all. And that you are calling to raise the event, it is returning true, and is still not working. If that is what is happening, it just has to be your code because Commands are working for thousands of apps. But, if you want to send me your code, I'll take a look.

Best of luck!

maxp
  • 24,209
  • 39
  • 123
  • 201
Jerry Nixon
  • 31,313
  • 14
  • 117
  • 233
  • Thanks Jerry. I'm beginning to think it is something that I've done differently in my code, but I can't locate where. I've just performed a couple more debug step-thrus and have discovered that if I leave the checks against `NameWorking` and `PointsLimitWorking` then the button becomes enabled when returning `true`. When I bring back in either of the other two checks, then the button doesn't become enabled when returning true. – Lindsay Mar 10 '15 at 03:15
  • I'm having real trouble locating what is causing the issue. If your offer of looking over my code is still open, I would like to take it up. Let me know how, and I'll send you the solution. – Lindsay Mar 10 '15 at 11:10
  • Please simplify your project before you send it to me. Please ensure I can open the zip and build it fine. jnixon@microsoft is my email. It might take me several days to get to it. – Jerry Nixon Mar 31 '15 at 00:18
1

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;
    }
}
Community
  • 1
  • 1
Rachel
  • 130,264
  • 66
  • 304
  • 490
  • Long time since this answer was posted, and I've moved on from this particular problem, however this answer has helped me on other problems, and most of all, inadvertently writing code that causes the same issue with other buttons. Marked as answer. – Lindsay Oct 20 '16 at 03:55
0

If you are using Mvvm Light, make sure you are including the GalaSoft.MvvmLight.CommandWpf namespace instead of the GalaSoft.MvvmLight.Command namespace. (See the second answer on MVVM RelayCommand CanExecute)

Community
  • 1
  • 1
Eva423
  • 31
  • 10