I have a ViewModel
with some ReactiveCommands
that user can invoke manually.
CommandA
CommandB
CommandC
So the user can invoke only B, or A first, and then C.
The problem is that there is that I want to create another command that runs them in order, from A to C. They shouldn't run in parallel, but sequentially.
How can I do a command of the given characteristics?
Import System.Reactive.Linq, now you can:
{
await CommandA.Execute();
await CommandB.Execute();
await CommandC.Execute();
}
System.Reactive.Linq makes IObservable as awaitable.
Since you want them executed sequentially, ReactiveCommand.CreateCombined
isn't really a good fit. However, you could create something similar to CombinedReactiveCommand to work for your situation.
Reactive commands can be executed manually by calling ReactiveCommand<TParam,TResult>.Execute
, which returns an IObservable<TResult>
that returns a single value. Therefore, a very basic implementation could just chain it to other execute calls with SelectMany
:
ReactiveCommand.CreateFromObservable(() =>
CommandA.Execute()
.SelectMany(_ => CommandB.Execute())
.SelectMany(_ => CommandC.Execute()));
If all of your commands have the same type, it could be made more generic:
var commands = new[] { CommandA, CommandB, CommandC };
ReactiveCommand.CreateFromObservable(() =>
commands.Aggregate(
Observable.Return(Unit.Default),
(acc, cur) => acc.SelectMany(_ => cur.Execute())));
However, this does not take into consideration the other things CombinedReactiveCommand handles, such as CanExecute
and ThrownExceptions
. If you need to handle those, then you might consider rolling your own CombinedReactiveCommand.
I am asuming, that your commands are of type ReactiveCommand<Unit,Unit>
so, you can do this:
public class MyCoolViewModel
{
bool runSequentially = false;
public ReactiveCommand<Unit, Unit> Command { get; set; }
public ReactiveCommand<Unit, Unit> Command2 { get; set; }
public ReactiveCommand<Unit, Unit> Command3 { get; set; }
public ReactiveCommand<Unit, Unit> CommandwhoInvokesOtherCommands { get; set; }
public MyCoolViewModel()
{
Command = ReactiveCommand.CreateFromTask<Unit, Unit>(async _ =>
{
runSequentially= false;
Console.WriteLine("Start 1");
await Task.Delay(1000);
Console.WriteLine("End 1");
return Unit.Default;
});
Command2 = ReactiveCommand.CreateFromTask<Unit, Unit>(async _ =>
{
runSequentially = false;
Console.WriteLine("Start 2");
await Task.Delay(1000);
Console.WriteLine("End 2");
return Unit.Default;
});
Command3 = ReactiveCommand.CreateFromTask<Unit, Unit>(async _ =>
{
Console.WriteLine("Start 3");
await Task.Delay(1000);
Console.WriteLine("End 3");
return Unit.Default;
});
CommandwhoInvokesOtherCommands = ReactiveCommand.CreateFromTask<Unit, Unit>(async _ =>
{
Console.WriteLine("Invoking other commands");
runSequentially = true;
await Task.Delay(1000);
Console.WriteLine("End");
return Unit.Default;
});
/*Command 1, 2 and 3 only will run if flag is set to true*/
CommandwhoInvokesOtherCommands.Where(_ => runSequentially).InvokeCommand(Command);
Command.Where(_ => runSequentially).InvokeCommand(Command2);
Command2.Where(_ => runSequentially).InvokeCommand(Command3);
//Observable.Return(Unit.Default).InvokeCommand(CommandwhoInvokesOtherCommands);//for test purposes
}
}
The utility method InvokeCommand will call another command when a command returns, so this method plus a where clause will do the trick.
I hope this help you
Regards