Prevent double click using ReactiveUI

Viewed 942

I'm struggling to prevent double click on a button using ReactiveUI with Xamarin Forms. Let's say we have a command like this:

NextCommand = ReactiveCommand.CreateFromTask(async () => await HostScreen.Router.NavigateAndReset.Execute(new NewViewModel()));

It's bound to a button in a normal Xamarin Forms way. Unfortunately, very fast click causes two command calls.

Is it possible to fix it using ReactiveUI?

I updated the question to summarise what I've tried:

var obs = Observable.FromAsync(async () => await HostScreen.Router.NavigateAndReset.Execute(new CitizensIndexViewModel()))
                    .Throttle(TimeSpan.FromSeconds(2));
LoginCommand = ReactiveCommand.CreateFromObservable(() => obs);

Unfortunately, it didn't help. When you double-click very fast on a button, two executions of a command will take place.

I've also tried to add lock statement or semaphore to 'execute' method but it also failed to work because executions aren't done in parallel. That's also a reason why using CanExecute method won't work.

What can potentially work is an ugly bool flag. The only problem with this solution is that I'd have to reset it on back navigation which is obviously doable but I don't consider it as the best solution.

2 Answers

You can pass a CanExecute parameter which will decide if the command can be executed. As you cans see in Controlling Executability :

var canExecute = this
    .WhenAnyValue(
        x => x.UserName,
        x => x.Password,
        (u, p) => !string.IsNullOrEmpty(u) && !string.IsNullOrEmpty(p));
var command = ReactiveCommand.CreateFromObservable(this.LogOnAsync, canExecute);

In very similar manner you can use a waiting indicator with your command:

bool busy = false;
NextCommand = ReactiveCommand.CreateFromTask(
async () => 
{ 
    busy = true;
    // do your studd here.
    busy = false;
},
!busy);

Hope this helps.

var canLogin = this.WhenAnyValue(vm => vm.IsLogingExec, p =>!p);
LoginCommand = ReactiveCommand.CreateFromTask(async () => { this.IsLogingExec = true; await HostScreen.Router.NavigateAndReset.Execute(new CitizensIndexViewModel()); } , canLogin);

. 
. 
. 

private bool isLogingExec;
public bool IsLogingExec{
    get => isLoggingExec;
    set => this.RaiseAndSetIfChanged(ref isLogingExec, value);
} 

canLogin is an observable that return true or false depending on IsLogingExec value.

ReactiveCommand.CreateFromTask has a second parameter, true if the command can be executed, false if not. If a command cannot be executed, the button with the command binded will be disabled.

The first thing the command is doing when executed is to change the IsLogingExec to false, that's way canLogin will change to false and the commandbutton will be disabled, and then, it will execute the navigate method.

IsLogingExec is a reactive property, that's way canLogin will react to changes.

Related