Mirar
Mirar

Reputation: 171

The RelayCommand for enable/disable a button don't work like expected

The case is that I try to disable a button in the window form when it was clicked and after some time (some seconds) it should be enabled again.

But this didn't work. After a click on the button the command set the enabled to false and after some seconds the command set it back to true (I tested it, the order is right and it set it to true again) but the button is still not enabled on the window form.

For that case I use a RelayCommmand. The RelayCommand is a standard class you find on Internet and will be shown in the end. To organise the command I wrote a class called Testclass:

class Testclass
{
    private bool _testValueCanExecute;
    public bool TestValueCanExecute
    {
        get { return _testValueCanExecute; }
        set
        {
            _testValueCanExecute = value;
            OnPropertyChanged();
        }
    }

    public ICommand TestValueCommand { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
    public Testclass()
    {
        TestValueCommand = new RelayCommand(TestMethod, param => _testValueCanExecute);
        TestValueCanExecute = true;
    }

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private async void TestMethod(object obj)
    {
        TestValueCanExecute = false;
        await Task.Delay(3000);
        TestValueCanExecute = true;
    }
}

In the XAML File I added a button as followed:

<Button x:Name="TestButton" Command="{Binding TestValueCommand}" Content="Test Button" HorizontalAlignment="Left" Margin="149,96,0,0" VerticalAlignment="Top" Width="75"/>

The MainWindow code looks as followed:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new Testclass();
    }
}

So the RelayCommand use the TestMethod method set the command enable variable to false, wait 3 seconds and set them back to true. But as I wrote above the button on the window form still not enabled.

It would be nice to understand what happens here and how I can solve this.

Update: I use the following Code for the RelayCommand:

public class RelayCommand : ICommand
{
    private Action<object> execute;
    private Func<object, bool> canExecute;

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        this.execute = execute;
        this.canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return this.canExecute == null || this.canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        this.execute(parameter);
    }
}

Upvotes: 0

Views: 2677

Answers (2)

Krzysztof Skowronek
Krzysztof Skowronek

Reputation: 2936

I strongly recommend using existing frameworks, instead of inveting the wheel once again. Take a look at ReactiveUI ReactiveCommand

In your case, it would do all the work by itself:

TestValueCommand = ReactiveCommand.CreateFromTask(async () => await Task.Delay(500));

You bind that command to a button in xaml and the button is disabled until command is done.

You can easily add another condition for disabling the command, and then, binding will disable button

Upvotes: 0

mm8
mm8

Reputation: 169240

The RelayCommand is a standard class you find on Internet ...

There is no such thing as a "standard class you find on Internet". In fact there are several different implementations of the RelayCommand available "on the Internet".

A good implementation should contain a method for raising the CanExecuteChanged event. MvvmLight's implementation has a RaiseCanExecuteChanged() method that does this. You need to call this one to "refresh" the status of the command:

private async void TestMethod(object obj)
{
    RelayCommand cmd = TestValueCommand as RelayCommand;
    TestValueCanExecute = false;
    cmd.RaiseCanExecuteChanged();
    await Task.Delay(3000);
    TestValueCanExecute = true;
    cmd.RaiseCanExecuteChanged();
}

The event is not raised automatically when you set the TestValueCanExecute property and raise the PropertyChanged event for the view model.

Edit: Your implementation doesn't have any RaiseCanExecuteChanged() method. Add one to your RelayCommand class and call it as per above:

public void RaiseCanExecuteChanged()
{
    CommandManager.InvalidateRequerySuggested();
}

Upvotes: 1

Related Questions