0

In my test case I can verify that the event handler CollectionChanged gets hooked up correctly. This occurrs when the command is created. In my view model when I do the same thing the event handler is never hooked up. Why is this?

If I make an explicit call to Undo.CanExecute(null) in my view model it will hook up the event handler. I guess I should not have to do this and that something must be wrong with my view model code.

View Model

ActionManager = new ActionManager();    

var canUndo = Observable
    .FromEventPattern(e => ActionManager.CollectionChanged += e, e => ActionManager.CollectionChanged -= e)
    .Select(_ => ActionManager.CanUndo)
    ;

Undo = ReactiveCommand.CreateAsyncTask(canUndo, UndoAsync);

Test Case

public class MiscTests
{
    [Fact]
    public void CanExecute()
    { 
        var am = new ActionManager();
        var canUndo = Observable
            .FromEventPattern(e => am.CollectionChanged += e, e => am.CollectionChanged -= e)
            .Select(_ => am.CanUndo);

        var command = ReactiveCommand.Create(canUndo);

        var action = new CallMethodAction(() => { }, () => { });
        var canExecute = command.CanExecute(null);
        canExecute.Should().BeFalse();
        am.Execute(action);
        canExecute = command.CanExecute(null);
        canExecute.Should().BeTrue();

    }
}
Magnus Lindhe
  • 7,157
  • 5
  • 48
  • 60

1 Answers1

1

Don't worry, your view model code is most likely correct.

What you managed to observe is a lazy nature of Rx observables in action. Digging the source code of ReactiveCommand constructor reveals that canExecute observable you provide to CreateAsyncTask factory method is not subscribed immediately, but instead Publish is called, which returns IConnectableObservable, which is stored in a private field (this.canExecute). So, the ReactiveCommand starts to actually listen to canExecute changes, when Connect is called on this.canExecute. This happens in CanExecute(object parameter) method.

So, should you explicitly call ReactiveCommand's CanExecute in your ViewModel? No, because this should be done by your View framework when you bind your command to an actual button.

pmbanka
  • 1,902
  • 17
  • 24