0

I have a problem where I see events running after I expect all events to be completed. But that happens only when the method called is called by a ReactiveCommand.

I have a viewmodel that has one property and a calculated property:

public class ViewModel : ReactiveObject
{
    private int _property;
    private readonly ObservableAsPropertyHelper<int> _calculatedProperty;

    public ViewModel()
    {
        Command = new DelegateCommand(x => Test("Command"));
        ReactiveCommand = ReactiveCommand.Create(() => Test("ReactiveCommand"));

        this.WhenAnyValue(x => x.Property)
            .Do(x => Console.WriteLine($"CalculatedProperty will be set to {x}"))
            .ToProperty(this, x => x.CalculatedProperty, out _calculatedProperty);
        this.WhenAnyValue(x => x.CalculatedProperty)
            .Subscribe(v => Console.WriteLine($"CalculatedProperty was set to {v}"));
    }

    public void Test(string method)
    {
        Console.WriteLine($"Begin {method}");
        Property++;
        Console.WriteLine($"End {method}");
    }

    public int Property
    {
        get => _property;
        set => this.RaiseAndSetIfChanged(ref _property, value);
    }

    public int CalculatedProperty => _calculatedProperty.Value;

    public ICommand Command { get; }
    public ReactiveCommand ReactiveCommand { get; }
}

I have a window with 2 buttons, one bound to the Command and one to the ReactiveCommand (and one button with an oldfashioned clickhandler which behaves identical to the Command).

When I click the Command button, the outputted text is as I expect:

Begin Command
CalculatedProperty will be set to 1
CalculatedProperty was set to 1
End Command

However when I click the ReactiveCommand-button I get this:

Begin ReactiveCommand
CalculatedProperty will be set to 2
End ReactiveCommand
CalculatedProperty was set to 2

The calculated property event firing after the End-message is being run. This causes problems in my code and I do not understand why this happens. I've been investigating and playing with sending in schedulers in the ReactiveCommand.Create method, looking at SynchronizationContexts but all my experiments haven't worked out.

Why is this happening and what can I do to stop it?

2 Answers2

0

Be due to the fact that by default ReactiveCommand will issue the commands onto the Main Thread dispatcher for ReactiveCommand.Create(() => action)

Your Delegate command is just running it straight away without going through the dispatcher which is similar functionality to what ImmediateScheduler would do.

You could pass the ImmediateScheduler.Default to the second parameter of the ReactiveCommand.Create()

Glenn Watson
  • 2,758
  • 1
  • 20
  • 30
0

I found out that the ToProperty method also takes in a scheduler. When I changed the:

.ToProperty(this, x => x.CalculatedProperty, out _calculatedProperty);

to:

.ToProperty(this, x => x.CalculatedProperty, out _calculatedProperty,
            scheduler: ImmediateScheduler.Instance);

it worked as I intended. Not sure if this is intended behaviour or some kind of bug. It certainly was unexpected for me.

  • 1
    As intended I suspect, avoids UI threading issues where if CalculatedProperty is set from another thread the notifications etc occur on the main thread, which without would cause problems in some platforms like WPF etc. Usually if you have logic that relies on ordering of property sets you should be looking for a solution for that. – Glenn Watson Jun 21 '18 at 20:22
  • @GlennWatson In this case the logic has to do with how changes in a field are handled depending if it was opened for updates or not. I agree depending on the ordering is problematic, but in this case I don't **think** there's a way around that. Due to this issue I ended up reading the status after it had been changed again, causing all kinds of issues. – Jan-Jaap van der Geer Jun 25 '18 at 11:08